./0000755000014500017510000000000012246613016010501 5ustar michaelstaff./call/0000755000014500017510000000000012246613010011406 5ustar michaelstaff./call/call.go0000644000014500017510000001006612246613010012653 0ustar michaelstaff// 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 call defines the call graph abstraction and various algorithms and utilities to operate on it. It does not provide a concrete implementation but permits other analyses (such as pointer analyses or Rapid Type Analysis) to expose their own call graphs in a representation-independent manner. A call graph is a labelled directed graph whose nodes represent functions and whose edge labels represent syntactic function call sites. The presence of a labelled edge (caller, site, callee) indicates that caller may call callee at the specified call site. A call graph is a multigraph: it may contain multiple edges (caller, *, callee) connecting the same pair of nodes, so long as the edges differ by label; this occurs when one function calls another function from multiple call sites. Also, it may contain multiple edges (caller, site, *) that differ only by callee; this indicates a polymorphic call. A call graph is called CONTEXT INSENSITIVE if no two nodes in N represent the same syntactic function declaration, i.e. the set of nodes and the set of syntactic functions are in one-to-one correspondence. A context-sensitive call graph may have multiple nodes corresponding to the same function; this may yield a more precise approximation to the calling behavior of the program. Consider this program: func Apply(fn func(V), value V) { fn(value) } Apply(F, v1) ... Apply(G, v2) A context-insensitive call graph would represent all calls to Apply by the same node, so that node would have successors F and G. A context-sensitive call graph might represent the first and second calls to Apply by distinct nodes, so that the first would have successor F and the second would have successor G. This is a more precise representation of the possible behaviors of the program. A SOUND call graph is one that overapproximates the dynamic calling behaviors of the program in all possible executions. One call graph is more PRECISE than another if it is a smaller overapproximation of the dynamic behavior. All call graphs have a synthetic root node which is responsible for calling main() and init(). Calls to built-in functions (e.g. panic, println) are not represented in the call graph; they are treated like built-in operators of the language. */ package call import "code.google.com/p/go.tools/ssa" // A Graph represents a call graph. // // A graph may contain nodes that are not reachable from the root. // If the call graph is sound, such nodes indicate unreachable // functions. // type Graph interface { Root() GraphNode // the distinguished root node Nodes() []GraphNode // new unordered set of all nodes } // A GraphNode represents a node in a call graph. // // If the call graph is context sensitive, there may be multiple // GraphNodes with the same Func(); the identity of the graph node // indicates the context. // // Sites returns the set of syntactic call sites within this function. // // For nodes representing synthetic or intrinsic functions // (e.g. reflect.Call, or the root of the call graph), Sites() returns // a slice containing a single nil value to indicate the synthetic // call site, and each edge in Edges() has a nil Site. // // All nodes "belong" to a single graph and must not be mixed with // nodes belonging to another graph. // // A site may appear in Sites() but not in {e.Site | e ∈ Edges()}. // This indicates that that caller node was unreachable, or that the // call was dynamic yet no func or interface values flow to the call // site. // // Clients should not mutate the node via the results of its methods. // type GraphNode interface { Func() *ssa.Function // the function this node represents Sites() []ssa.CallInstruction // new unordered set of call sites within this function Edges() []Edge // new unordered set of outgoing edges } // A Edge represents an edge in the call graph. type Edge struct { Caller GraphNode Site ssa.CallInstruction Callee GraphNode } ./call/util.go0000644000014500017510000000474112246613010012720 0ustar michaelstaff// 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 call // This file provides various representation-independent utilities // over call graphs, such as visitation and path search. // // TODO(adonovan): // // Consider adding lookup functions such as: // FindSitesByPos(g Graph, lparen token.Pos) []Site // FindSitesByCallExpr(g Graph, expr *ast.CallExpr) []Site // FindSitesByInstr(g Graph, instr ssa.CallInstruction) []Site // FindNodesByFunc(g Graph, fn *ssa.Function) []GraphNode // (Counterargument: they're all inefficient linear scans; if the // caller does it explicitly there may be opportunities to optimize. // // Add a utility function to eliminate all context from a call graph. // CalleesOf returns a new set containing all direct callees of the // caller node. // func CalleesOf(caller GraphNode) map[GraphNode]bool { callees := make(map[GraphNode]bool) for _, e := range caller.Edges() { callees[e.Callee] = true } return callees } // GraphVisitEdges visits all the edges in graph g in depth-first order. // The edge function is called for each edge in postorder. If it // returns non-nil, visitation stops and GraphVisitEdges returns that // value. // func GraphVisitEdges(g Graph, edge func(Edge) error) error { seen := make(map[GraphNode]bool) var visit func(n GraphNode) error visit = func(n GraphNode) error { if !seen[n] { seen[n] = true for _, e := range n.Edges() { if err := visit(e.Callee); err != nil { return err } if err := edge(e); err != nil { return err } } } return nil } for _, n := range g.Nodes() { if err := visit(n); err != nil { return err } } return nil } // PathSearch finds an arbitrary path starting at node start and // ending at some node for which isEnd() returns true. On success, // PathSearch returns the path as an ordered list of edges; on // failure, it returns nil. // func PathSearch(start GraphNode, isEnd func(GraphNode) bool) []Edge { stack := make([]Edge, 0, 32) seen := make(map[GraphNode]bool) var search func(n GraphNode) []Edge search = func(n GraphNode) []Edge { if !seen[n] { seen[n] = true if isEnd(n) { return stack } for _, e := range n.Edges() { stack = append(stack, e) // push if found := search(e.Callee); found != nil { return found } stack = stack[:len(stack)-1] // pop } } return nil } return search(start) } ./README0000644000014500017510000000077212246613010011361 0ustar michaelstaffThis subrepository holds the source for various packages and tools that support the Go programming language. Some of the tools, godoc and vet for example, are included in binary Go distributions. Others, including the Go oracle and the test coverage tool, can be fetched with "go get". Packages include a type-checker for Go and an implementation of the Static Single Assignment form (SSA) representation for Go programs. To submit changes to this repository, see http://golang.org/doc/contribute.html. ./present/0000755000014500017510000000000012246613010012153 5ustar michaelstaff./present/parse.go0000644000014500017510000002734412246613010013626 0ustar michaelstaff// 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. package present import ( "bufio" "bytes" "errors" "fmt" "html/template" "io" "io/ioutil" "log" "net/url" "regexp" "strings" "time" "unicode" "unicode/utf8" ) var ( parsers = make(map[string]ParseFunc) funcs = template.FuncMap{} ) // Template returns an empty template with the action functions in its FuncMap. func Template() *template.Template { return template.New("").Funcs(funcs) } // Render renders the doc to the given writer using the provided template. func (d *Doc) Render(w io.Writer, t *template.Template) error { data := struct { *Doc Template *template.Template PlayEnabled bool }{d, t, PlayEnabled} return t.ExecuteTemplate(w, "root", data) } // Render renders the section to the given writer using the provided template. func (s *Section) Render(w io.Writer, t *template.Template) error { data := struct { *Section Template *template.Template PlayEnabled bool }{s, t, PlayEnabled} return t.ExecuteTemplate(w, "section", data) } type ParseFunc func(ctx *Context, fileName string, lineNumber int, inputLine string) (Elem, error) // Register binds the named action, which does not begin with a period, to the // specified parser to be invoked when the name, with a period, appears in the // present input text. func Register(name string, parser ParseFunc) { if len(name) == 0 || name[0] == ';' { panic("bad name in Register: " + name) } parsers["."+name] = parser } // Doc represents an entire document. type Doc struct { Title string Subtitle string Time time.Time Authors []Author Sections []Section Tags []string } // Author represents the person who wrote and/or is presenting the document. type Author struct { Elem []Elem } // TextElem returns the first text elements of the author details. // This is used to display the author' name, job title, and company // without the contact details. func (p *Author) TextElem() (elems []Elem) { for _, el := range p.Elem { if _, ok := el.(Text); !ok { break } elems = append(elems, el) } return } // Section represents a section of a document (such as a presentation slide) // comprising a title and a list of elements. type Section struct { Number []int Title string Elem []Elem } func (s Section) Sections() (sections []Section) { for _, e := range s.Elem { if section, ok := e.(Section); ok { sections = append(sections, section) } } return } // Level returns the level of the given section. // The document title is level 1, main section 2, etc. func (s Section) Level() int { return len(s.Number) + 1 } // FormattedNumber returns a string containing the concatenation of the // numbers identifying a Section. func (s Section) FormattedNumber() string { b := &bytes.Buffer{} for _, n := range s.Number { fmt.Fprintf(b, "%v.", n) } return b.String() } func (s Section) TemplateName() string { return "section" } // Elem defines the interface for a present element. That is, something that // can provide the name of the template used to render the element. type Elem interface { TemplateName() string } // renderElem implements the elem template function, used to render // sub-templates. func renderElem(t *template.Template, e Elem) (template.HTML, error) { var data interface{} = e if s, ok := e.(Section); ok { data = struct { Section Template *template.Template }{s, t} } return execTemplate(t, e.TemplateName(), data) } func init() { funcs["elem"] = renderElem } // execTemplate is a helper to execute a template and return the output as a // template.HTML value. func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) { b := new(bytes.Buffer) err := t.ExecuteTemplate(b, name, data) if err != nil { return "", err } return template.HTML(b.String()), nil } // Text represents an optionally preformatted paragraph. type Text struct { Lines []string Pre bool } func (t Text) TemplateName() string { return "text" } // List represents a bulleted list. type List struct { Bullet []string } func (l List) TemplateName() string { return "list" } // Lines is a helper for parsing line-based input. type Lines struct { line int // 0 indexed, so has 1-indexed number of last line returned text []string } func readLines(r io.Reader) (*Lines, error) { var lines []string s := bufio.NewScanner(r) for s.Scan() { lines = append(lines, s.Text()) } if err := s.Err(); err != nil { return nil, err } return &Lines{0, lines}, nil } func (l *Lines) next() (text string, ok bool) { for { current := l.line l.line++ if current >= len(l.text) { return "", false } text = l.text[current] // Lines starting with # are comments. if len(text) == 0 || text[0] != '#' { ok = true break } } return } func (l *Lines) back() { l.line-- } func (l *Lines) nextNonEmpty() (text string, ok bool) { for { text, ok = l.next() if !ok { return } if len(text) > 0 { break } } return } // A Context specifies the supporting context for parsing a presentation. type Context struct { // ReadFile reads the file named by filename and returns the contents. ReadFile func(filename string) ([]byte, error) } // ParseMode represents flags for the Parse function. type ParseMode int const ( // If set, parse only the title and subtitle. TitlesOnly ParseMode = 1 ) // Parse parses a document from r. func (ctx *Context) Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) { doc := new(Doc) lines, err := readLines(r) if err != nil { return nil, err } err = parseHeader(doc, lines) if err != nil { return nil, err } if mode&TitlesOnly != 0 { return doc, nil } // Authors if doc.Authors, err = parseAuthors(lines); err != nil { return nil, err } // Sections if doc.Sections, err = parseSections(ctx, name, lines, []int{}, doc); err != nil { return nil, err } return doc, nil } // Parse parses a document from r. Parse reads assets used by the presentation // from the file system using ioutil.ReadFile. func Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) { ctx := Context{ReadFile: ioutil.ReadFile} return ctx.Parse(r, name, mode) } // isHeading matches any section heading. var isHeading = regexp.MustCompile(`^\*+ `) // lesserHeading returns true if text is a heading of a lesser or equal level // than that denoted by prefix. func lesserHeading(text, prefix string) bool { return isHeading.MatchString(text) && !strings.HasPrefix(text, prefix+"*") } // parseSections parses Sections from lines for the section level indicated by // number (a nil number indicates the top level). func parseSections(ctx *Context, name string, lines *Lines, number []int, doc *Doc) ([]Section, error) { var sections []Section for i := 1; ; i++ { // Next non-empty line is title. text, ok := lines.nextNonEmpty() for ok && text == "" { text, ok = lines.next() } if !ok { break } prefix := strings.Repeat("*", len(number)+1) if !strings.HasPrefix(text, prefix+" ") { lines.back() break } section := Section{ Number: append(append([]int{}, number...), i), Title: text[len(prefix)+1:], } text, ok = lines.nextNonEmpty() for ok && !lesserHeading(text, prefix) { var e Elem r, _ := utf8.DecodeRuneInString(text) switch { case unicode.IsSpace(r): i := strings.IndexFunc(text, func(r rune) bool { return !unicode.IsSpace(r) }) if i < 0 { break } indent := text[:i] var s []string for ok && (strings.HasPrefix(text, indent) || text == "") { if text != "" { text = text[i:] } s = append(s, text) text, ok = lines.next() } lines.back() pre := strings.Join(s, "\n") pre = strings.Replace(pre, "\t", " ", -1) // browsers treat tabs badly pre = strings.TrimRightFunc(pre, unicode.IsSpace) e = Text{Lines: []string{pre}, Pre: true} case strings.HasPrefix(text, "- "): var b []string for ok && strings.HasPrefix(text, "- ") { b = append(b, text[2:]) text, ok = lines.next() } lines.back() e = List{Bullet: b} case strings.HasPrefix(text, prefix+"* "): lines.back() subsecs, err := parseSections(ctx, name, lines, section.Number, doc) if err != nil { return nil, err } for _, ss := range subsecs { section.Elem = append(section.Elem, ss) } case strings.HasPrefix(text, "."): args := strings.Fields(text) parser := parsers[args[0]] if parser == nil { return nil, fmt.Errorf("%s:%d: unknown command %q\n", name, lines.line, text) } t, err := parser(ctx, name, lines.line, text) if err != nil { return nil, err } e = t default: var l []string for ok && strings.TrimSpace(text) != "" { if text[0] == '.' { // Command breaks text block. break } if strings.HasPrefix(text, `\.`) { // Backslash escapes initial period. text = text[1:] } l = append(l, text) text, ok = lines.next() } if len(l) > 0 { e = Text{Lines: l} } } if e != nil { section.Elem = append(section.Elem, e) } text, ok = lines.nextNonEmpty() } if isHeading.MatchString(text) { lines.back() } sections = append(sections, section) } return sections, nil } func parseHeader(doc *Doc, lines *Lines) error { var ok bool // First non-empty line starts header. doc.Title, ok = lines.nextNonEmpty() if !ok { return errors.New("unexpected EOF; expected title") } for { text, ok := lines.next() if !ok { return errors.New("unexpected EOF") } if text == "" { break } const tagPrefix = "Tags:" if strings.HasPrefix(text, tagPrefix) { tags := strings.Split(text[len(tagPrefix):], ",") for i := range tags { tags[i] = strings.TrimSpace(tags[i]) } doc.Tags = append(doc.Tags, tags...) } else if t, ok := parseTime(text); ok { doc.Time = t } else if doc.Subtitle == "" { doc.Subtitle = text } else { return fmt.Errorf("unexpected header line: %q", text) } } return nil } func parseAuthors(lines *Lines) (authors []Author, err error) { // This grammar demarcates authors with blanks. // Skip blank lines. if _, ok := lines.nextNonEmpty(); !ok { return nil, errors.New("unexpected EOF") } lines.back() var a *Author for { text, ok := lines.next() if !ok { return nil, errors.New("unexpected EOF") } // If we find a section heading, we're done. if strings.HasPrefix(text, "* ") { lines.back() break } // If we encounter a blank we're done with this author. if a != nil && len(text) == 0 { authors = append(authors, *a) a = nil continue } if a == nil { a = new(Author) } // Parse the line. Those that // - begin with @ are twitter names, // - contain slashes are links, or // - contain an @ symbol are an email address. // The rest is just text. var el Elem switch { case strings.HasPrefix(text, "@"): el = parseURL("http://twitter.com/" + text[1:]) case strings.Contains(text, ":"): el = parseURL(text) case strings.Contains(text, "@"): el = parseURL("mailto:" + text) } if l, ok := el.(Link); ok { l.Label = text el = l } if el == nil { el = Text{Lines: []string{text}} } a.Elem = append(a.Elem, el) } if a != nil { authors = append(authors, *a) } return authors, nil } func parseURL(text string) Elem { u, err := url.Parse(text) if err != nil { log.Printf("Parse(%q): %v", text, err) return nil } return Link{URL: u} } func parseTime(text string) (t time.Time, ok bool) { t, err := time.Parse("15:04 2 Jan 2006", text) if err == nil { return t, true } t, err = time.Parse("2 Jan 2006", text) if err == nil { // at 11am UTC it is the same date everywhere t = t.Add(time.Hour * 11) return t, true } return time.Time{}, false } ./present/doc.go0000644000014500017510000001243012246613010013247 0ustar michaelstaff// 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. /* The present file format Present files have the following format. The first non-blank non-comment line is the title, so the header looks like Title of document Subtitle of document 15:04 2 Jan 2006 Tags: foo, bar, baz Author Name Job title, Company joe@example.com http://url/ @twitter_name The subtitle, date, and tags lines are optional. The date line may be written without a time: 2 Jan 2006 In this case, the time will be interpreted as 10am UTC on that date. The tags line is a comma-separated list of tags that may be used to categorize the document. The author section may contain a mixture of text, twitter names, and links. For slide presentations, only the plain text lines will be displayed on the first slide. Multiple presenters may be specified, separated by a blank line. After that come slides/sections, each after a blank line: * Title of slide or section (must have asterisk) Some Text ** Subsection - bullets - more bullets - a bullet with *** Sub-subsection Some More text Preformatted text is indented (however you like) Further Text, including invocations like: .code x.go /^func main/,/^}/ .play y.go .image image.jpg .iframe http://foo .link http://foo label .html file.html Again, more text Blank lines are OK (not mandatory) after the title and after the text. Text, bullets, and .code etc. are all optional; title is not. Lines starting with # in column 1 are commentary. Fonts: Within the input for plain text or lists, text bracketed by font markers will be presented in italic, bold, or program font. Marker characters are _ (italic), * (bold) and ` (program font). Unmatched markers appear as plain text. Within marked text, a single marker character becomes a space and a doubled single marker quotes the marker character. _italic_ *bold* `program` _this_is_all_italic_ _Why_use_scoped__ptr_? Use plain ***ptr* instead. Inline links: Links can be included in any text with the form [[url][label]], or [[url]] to use the URL itself as the label. Functions: A number of template functions are available through invocations in the input text. Each such invocation contains a period as the first character on the line, followed immediately by the name of the function, followed by any arguments. A typical invocation might be .play demo.go /^func show/,/^}/ (except that the ".play" must be at the beginning of the line and not be indented like this.) Here follows a description of the functions: code: Injects program source into the output by extracting code from files and injecting them as HTML-escaped
 blocks.  The argument is
a file name followed by an optional address that specifies what
section of the file to display. The address syntax is similar in
its simplest form to that of ed, but comes from sam and is more
general. See
	http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
for full details. The displayed block is always rounded out to a
full line at both ends.

If no pattern is present, the entire file is displayed.

Any line in the program that ends with the four characters
	OMIT
is deleted from the source before inclusion, making it easy
to write things like
	.code test.go /START OMIT/,/END OMIT/
to find snippets like this
	tedious_code = boring_function()
	// START OMIT
	interesting_code = fascinating_function()
	// END OMIT
and see only this:
	interesting_code = fascinating_function()

Also, inside the displayed text a line that ends
	// HL
will be highlighted in the display; the 'h' key in the browser will
toggle extra emphasis of any highlighted lines. A highlighting mark
may have a suffix word, such as
	// HLxxx
Such highlights are enabled only if the code invocation ends with
"HL" followed by the word:
	.code test.go /^type Foo/,/^}/ HLxxx

The .code function may take one or more flags immediately preceding
the filename. This command shows test.go in an editable text area:
	.code -edit test.go
This command shows test.go with line numbers:
	.code -numbers test.go

play:

The function "play" is the same as "code" but puts a button
on the displayed source so the program can be run from the browser.
Although only the selected text is shown, all the source is included
in the HTML output so it can be presented to the compiler.

link:

Create a hyperlink. The syntax is 1 or 2 space-separated arguments.
The first argument is always the HTTP URL.  If there is a second
argument, it is the text label to display for this link.

	.link http://golang.org golang.org

image:

The template uses the function "image" to inject picture files.

The syntax is simple: 1 or 3 space-separated arguments.
The first argument is always the file name.
If there are more arguments, they are the height and width;
both must be present.

	.image images/betsy.jpg 100 200

iframe:

The function "iframe" injects iframes (pages inside pages).
Its syntax is the same as that of image.

html:

The function html includes the contents of the specified file as
unescaped HTML. This is useful for including custom HTML elements
that cannot be created using only the slide format.
It is your responsibilty to make sure the included HTML is valid and safe.

	.html file.html

*/
package present
./present/link_test.go0000644000014500017510000000276512246613010014510 0ustar  michaelstaff// Copyright 2012 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 "testing"

func TestInlineParsing(t *testing.T) {
	var tests = []struct {
		in     string
		link   string
		text   string
		length int
	}{
		{"[[http://golang.org]]", "http://golang.org", "golang.org", 21},
		{"[[http://golang.org][]]", "http://golang.org", "http://golang.org", 23},
		{"[[http://golang.org]] this is ignored", "http://golang.org", "golang.org", 21},
		{"[[http://golang.org][link]]", "http://golang.org", "link", 27},
		{"[[http://golang.org][two words]]", "http://golang.org", "two words", 32},
		{"[[http://golang.org][*link*]]", "http://golang.org", "link", 29},
		{"[[http://bad[url]]", "", "", 0},
		{"[[http://golang.org][a [[link]] ]]", "http://golang.org", "a [[link", 31},
		{"[[http:// *spaces* .com]]", "", "", 0},
		{"[[http://bad`char.com]]", "", "", 0},
		{" [[http://google.com]]", "", "", 0},
		{"[[mailto:gopher@golang.org][Gopher]]", "mailto:gopher@golang.org", "Gopher", 36},
		{"[[mailto:gopher@golang.org]]", "mailto:gopher@golang.org", "gopher@golang.org", 28},
	}

	for i, test := range tests {
		link, length := parseInlineLink(test.in)
		if length == 0 && test.length == 0 {
			continue
		}
		if a := renderLink(test.link, test.text); length != test.length || link != a {
			t.Errorf("#%d: parseInlineLink(%q):\ngot\t%q, %d\nwant\t%q, %d", i, test.in, link, length, a, test.length)
		}
	}
}
./present/iframe.go0000644000014500017510000000154312246613010013750 0ustar  michaelstaff// 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 present

import (
	"fmt"
	"strings"
)

func init() {
	Register("iframe", parseIframe)
}

type Iframe struct {
	URL    string
	Width  int
	Height int
}

func (i Iframe) TemplateName() string { return "iframe" }

func parseIframe(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
	args := strings.Fields(text)
	i := Iframe{URL: args[1]}
	a, err := parseArgs(fileName, lineno, args[2:])
	if err != nil {
		return nil, err
	}
	switch len(a) {
	case 0:
		// no size parameters
	case 2:
		if v, ok := a[0].(int); ok {
			i.Height = v
		}
		if v, ok := a[1].(int); ok {
			i.Width = v
		}
	default:
		return nil, fmt.Errorf("incorrect image invocation: %q", text)
	}
	return i, nil
}
./present/html.go0000644000014500017510000000106312246613010013446 0ustar  michaelstaffpackage present

import (
	"errors"
	"html/template"
	"path/filepath"
	"strings"
)

func init() {
	Register("html", parseHTML)
}

func parseHTML(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
	p := strings.Fields(text)
	if len(p) != 2 {
		return nil, errors.New("invalid .html args")
	}
	name := filepath.Join(filepath.Dir(fileName), p[1])
	b, err := ctx.ReadFile(name)
	if err != nil {
		return nil, err
	}
	return HTML{template.HTML(b)}, nil
}

type HTML struct {
	template.HTML
}

func (s HTML) TemplateName() string { return "html" }
./present/style.go0000644000014500017510000000753512246613010013654 0ustar  michaelstaff// Copyright 2012 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 (
	"bytes"
	"html"
	"html/template"
	"strings"
	"unicode"
	"unicode/utf8"
)

/*
	Fonts are demarcated by an initial and final char bracketing a
	space-delimited word, plus possibly some terminal punctuation.
	The chars are
		_ for italic
		* for bold
		` (back quote) for fixed width.
	Inner appearances of the char become spaces. For instance,
		_this_is_italic_!
	becomes
		this is italic!
*/

func init() {
	funcs["style"] = Style
}

// Style returns s with HTML entities escaped and font indicators turned into
// HTML font tags.
func Style(s string) template.HTML {
	return template.HTML(font(html.EscapeString(s)))
}

// font returns s with font indicators turned into HTML font tags.
func font(s string) string {
	if strings.IndexAny(s, "[`_*") == -1 {
		return s
	}
	words := split(s)
	var b bytes.Buffer
Word:
	for w, word := range words {
		if len(word) < 2 {
			continue Word
		}
		if link, _ := parseInlineLink(word); link != "" {
			words[w] = link
			continue Word
		}
		const punctuation = `.,;:()!?—–'"`
		const marker = "_*`"
		// Initial punctuation is OK but must be peeled off.
		first := strings.IndexAny(word, marker)
		if first == -1 {
			continue Word
		}
		// Is the marker prefixed only by punctuation?
		for _, r := range word[:first] {
			if !strings.ContainsRune(punctuation, r) {
				continue Word
			}
		}
		open, word := word[:first], word[first:]
		char := word[0] // ASCII is OK.
		close := ""
		switch char {
		default:
			continue Word
		case '_':
			open += ""
			close = ""
		case '*':
			open += ""
			close = ""
		case '`':
			open += ""
			close = ""
		}
		// Terminal punctuation is OK but must be peeled off.
		last := strings.LastIndex(word, word[:1])
		if last == 0 {
			continue Word
		}
		head, tail := word[:last+1], word[last+1:]
		for _, r := range tail {
			if !strings.ContainsRune(punctuation, r) {
				continue Word
			}
		}
		b.Reset()
		b.WriteString(open)
		var wid int
		for i := 1; i < len(head)-1; i += wid {
			var r rune
			r, wid = utf8.DecodeRuneInString(head[i:])
			if r != rune(char) {
				// Ordinary character.
				b.WriteRune(r)
				continue
			}
			if head[i+1] != char {
				// Inner char becomes space.
				b.WriteRune(' ')
				continue
			}
			// Doubled char becomes real char.
			// Not worth worrying about "_x__".
			b.WriteByte(char)
			wid++ // Consumed two chars, both ASCII.
		}
		b.WriteString(close) // Write closing tag.
		b.WriteString(tail)  // Restore trailing punctuation.
		words[w] = b.String()
	}
	return strings.Join(words, "")
}

// split is like strings.Fields but also returns the runs of spaces
// and treats inline links as distinct words.
func split(s string) []string {
	var (
		words = make([]string, 0, 10)
		start = 0
	)

	// appendWord appends the string s[start:end] to the words slice.
	// If the word contains the beginning of a link, the non-link portion
	// of the word and the entire link are appended as separate words,
	// and the start index is advanced to the end of the link.
	appendWord := func(end int) {
		if j := strings.Index(s[start:end], "[["); j > -1 {
			if _, l := parseInlineLink(s[start+j:]); l > 0 {
				// Append portion before link, if any.
				if j > 0 {
					words = append(words, s[start:start+j])
				}
				// Append link itself.
				words = append(words, s[start+j:start+j+l])
				// Advance start index to end of link.
				start = start + j + l
				return
			}
		}
		// No link; just add the word.
		words = append(words, s[start:end])
		start = end
	}

	wasSpace := false
	for i, r := range s {
		isSpace := unicode.IsSpace(r)
		if i > start && isSpace != wasSpace {
			appendWord(i)
		}
		wasSpace = isSpace
	}
	for start < len(s) {
		appendWord(len(s))
	}
	return words
}
./present/link.go0000644000014500017510000000420712246613010013442 0ustar  michaelstaff// Copyright 2012 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"
	"net/url"
	"strings"
)

func init() {
	Register("link", parseLink)
}

type Link struct {
	URL   *url.URL
	Label string
}

func (l Link) TemplateName() string { return "link" }

func parseLink(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
	args := strings.Fields(text)
	url, err := url.Parse(args[1])
	if err != nil {
		return nil, err
	}
	label := ""
	if len(args) > 2 {
		label = strings.Join(args[2:], " ")
	} else {
		scheme := url.Scheme + "://"
		if url.Scheme == "mailto" {
			scheme = "mailto:"
		}
		label = strings.Replace(url.String(), scheme, "", 1)
	}
	return Link{url, label}, nil
}

func renderLink(url, text string) string {
	text = font(text)
	if text == "" {
		text = url
	}
	return fmt.Sprintf(`%s`, url, text)
}

// parseInlineLink parses an inline link at the start of s, and returns
// a rendered HTML link and the total length of the raw inline link.
// If no inline link is present, it returns all zeroes.
func parseInlineLink(s string) (link string, length int) {
	if !strings.HasPrefix(s, "[[") {
		return
	}
	end := strings.Index(s, "]]")
	if end == -1 {
		return
	}
	urlEnd := strings.Index(s, "]")
	rawURL := s[2:urlEnd]
	const badURLChars = `<>"{}|\^[] ` + "`" // per RFC2396 section 2.4.3
	if strings.ContainsAny(rawURL, badURLChars) {
		return
	}
	if urlEnd == end {
		simpleUrl := ""
		url, err := url.Parse(rawURL)
		if err == nil {
			// If the URL is http://foo.com, drop the http://
			// In other words, render [[http://golang.org]] as:
			//   golang.org
			if strings.HasPrefix(rawURL, url.Scheme+"://") {
				simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+"://")
			} else if strings.HasPrefix(rawURL, url.Scheme+":") {
				simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+":")
			}
		}
		return renderLink(rawURL, simpleUrl), end + 2
	}
	if s[urlEnd:urlEnd+2] != "][" {
		return
	}
	text := s[urlEnd+2 : end]
	return renderLink(rawURL, text), end + 2
}
./present/args.go0000644000014500017510000001165712246613010013450 0ustar  michaelstaff// Copyright 2012 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 (
	"errors"
	"regexp"
	"strconv"
	"unicode/utf8"
)

// This file is stolen from go/src/cmd/godoc/codewalk.go.
// It's an evaluator for the file address syntax implemented by acme and sam,
// but using Go-native regular expressions.
// To keep things reasonably close, this version uses (?m:re) for all user-provided
// regular expressions. That is the only change to the code from codewalk.go.
// See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
// for details on the syntax.

// addrToByte evaluates the given address starting at offset start in data.
// It returns the lo and hi byte offset of the matched region within data.
func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) {
	if addr == "" {
		lo, hi = start, len(data)
		return
	}
	var (
		dir        byte
		prevc      byte
		charOffset bool
	)
	lo = start
	hi = start
	for addr != "" && err == nil {
		c := addr[0]
		switch c {
		default:
			err = errors.New("invalid address syntax near " + string(c))
		case ',':
			if len(addr) == 1 {
				hi = len(data)
			} else {
				_, hi, err = addrToByteRange(addr[1:], hi, data)
			}
			return

		case '+', '-':
			if prevc == '+' || prevc == '-' {
				lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset)
			}
			dir = c

		case '$':
			lo = len(data)
			hi = len(data)
			if len(addr) > 1 {
				dir = '+'
			}

		case '#':
			charOffset = true

		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			var i int
			for i = 1; i < len(addr); i++ {
				if addr[i] < '0' || addr[i] > '9' {
					break
				}
			}
			var n int
			n, err = strconv.Atoi(addr[0:i])
			if err != nil {
				break
			}
			lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset)
			dir = 0
			charOffset = false
			prevc = c
			addr = addr[i:]
			continue

		case '/':
			var i, j int
		Regexp:
			for i = 1; i < len(addr); i++ {
				switch addr[i] {
				case '\\':
					i++
				case '/':
					j = i + 1
					break Regexp
				}
			}
			if j == 0 {
				j = i
			}
			pattern := addr[1:i]
			lo, hi, err = addrRegexp(data, lo, hi, dir, pattern)
			prevc = c
			addr = addr[j:]
			continue
		}
		prevc = c
		addr = addr[1:]
	}

	if err == nil && dir != 0 {
		lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset)
	}
	if err != nil {
		return 0, 0, err
	}
	return lo, hi, nil
}

// addrNumber applies the given dir, n, and charOffset to the address lo, hi.
// dir is '+' or '-', n is the count, and charOffset is true if the syntax
// used was #n.  Applying +n (or +#n) means to advance n lines
// (or characters) after hi.  Applying -n (or -#n) means to back up n lines
// (or characters) before lo.
// The return value is the new lo, hi.
func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) {
	switch dir {
	case 0:
		lo = 0
		hi = 0
		fallthrough

	case '+':
		if charOffset {
			pos := hi
			for ; n > 0 && pos < len(data); n-- {
				_, size := utf8.DecodeRune(data[pos:])
				pos += size
			}
			if n == 0 {
				return pos, pos, nil
			}
			break
		}
		// find next beginning of line
		if hi > 0 {
			for hi < len(data) && data[hi-1] != '\n' {
				hi++
			}
		}
		lo = hi
		if n == 0 {
			return lo, hi, nil
		}
		for ; hi < len(data); hi++ {
			if data[hi] != '\n' {
				continue
			}
			switch n--; n {
			case 1:
				lo = hi + 1
			case 0:
				return lo, hi + 1, nil
			}
		}

	case '-':
		if charOffset {
			// Scan backward for bytes that are not UTF-8 continuation bytes.
			pos := lo
			for ; pos > 0 && n > 0; pos-- {
				if data[pos]&0xc0 != 0x80 {
					n--
				}
			}
			if n == 0 {
				return pos, pos, nil
			}
			break
		}
		// find earlier beginning of line
		for lo > 0 && data[lo-1] != '\n' {
			lo--
		}
		hi = lo
		if n == 0 {
			return lo, hi, nil
		}
		for ; lo >= 0; lo-- {
			if lo > 0 && data[lo-1] != '\n' {
				continue
			}
			switch n--; n {
			case 1:
				hi = lo
			case 0:
				return lo, hi, nil
			}
		}
	}

	return 0, 0, errors.New("address out of range")
}

// addrRegexp searches for pattern in the given direction starting at lo, hi.
// The direction dir is '+' (search forward from hi) or '-' (search backward from lo).
// Backward searches are unimplemented.
func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) {
	// We want ^ and $ to work as in sam/acme, so use ?m.
	re, err := regexp.Compile("(?m:" + pattern + ")")
	if err != nil {
		return 0, 0, err
	}
	if dir == '-' {
		// Could implement reverse search using binary search
		// through file, but that seems like overkill.
		return 0, 0, errors.New("reverse search not implemented")
	}
	m := re.FindIndex(data[hi:])
	if len(m) > 0 {
		m[0] += hi
		m[1] += hi
	} else if hi > 0 {
		// No match.  Wrap to beginning of data.
		m = re.FindIndex(data)
	}
	if len(m) == 0 {
		return 0, 0, errors.New("no match for " + pattern)
	}
	return m[0], m[1], nil
}
./present/style_test.go0000644000014500017510000000710712246613010014706 0ustar  michaelstaff// Copyright 2012 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"
	"reflect"
	"testing"
)

func TestSplit(t *testing.T) {
	var tests = []struct {
		in  string
		out []string
	}{
		{"", []string{}},
		{" ", []string{" "}},
		{"abc", []string{"abc"}},
		{"abc def", []string{"abc", " ", "def"}},
		{"abc def ", []string{"abc", " ", "def", " "}},
		{"hey [[http://golang.org][Gophers]] around",
			[]string{"hey", " ", "[[http://golang.org][Gophers]]", " ", "around"}},
		{"A [[http://golang.org/doc][two words]] link",
			[]string{"A", " ", "[[http://golang.org/doc][two words]]", " ", "link"}},
		{"Visit [[http://golang.org/doc]] now",
			[]string{"Visit", " ", "[[http://golang.org/doc]]", " ", "now"}},
		{"not [[http://golang.org/doc][a [[link]] ]] around",
			[]string{"not", " ", "[[http://golang.org/doc][a [[link]]", " ", "]]", " ", "around"}},
		{"[[http://golang.org][foo bar]]",
			[]string{"[[http://golang.org][foo bar]]"}},
		{"ends with [[http://golang.org][link]]",
			[]string{"ends", " ", "with", " ", "[[http://golang.org][link]]"}},
		{"my talk ([[http://talks.golang.org/][slides here]])",
			[]string{"my", " ", "talk", " ", "(", "[[http://talks.golang.org/][slides here]]", ")"}},
	}
	for _, test := range tests {
		out := split(test.in)
		if !reflect.DeepEqual(out, test.out) {
			t.Errorf("split(%q):\ngot\t%q\nwant\t%q", test.in, out, test.out)
		}
	}
}

func TestFont(t *testing.T) {
	var tests = []struct {
		in  string
		out string
	}{
		{"", ""},
		{" ", " "},
		{"\tx", "\tx"},
		{"_a_", "a"},
		{"*a*", "a"},
		{"`a`", "a"},
		{"_a_b_", "a b"},
		{"_a__b_", "a_b"},
		{"_a___b_", "a_ b"},
		{"*a**b*?", "a*b?"},
		{"_a_<>_b_.", "a <> b."},
		{"(_a_)", "(a)"},
		{"((_a_), _b_, _c_).", "((a), b, c)."},
		{"(_a)", "(_a)"},
		{"(_a)", "(_a)"},
		{"_Why_use_scoped__ptr_? Use plain ***ptr* instead.", "Why use scoped_ptr? Use plain *ptr instead."},
		{"_hey_ [[http://golang.org][*Gophers*]] *around*",
			`hey Gophers around`},
		{"_hey_ [[http://golang.org][so _many_ *Gophers*]] *around*",
			`hey so many Gophers around`},
		{"Visit [[http://golang.org]] now",
			`Visit golang.org now`},
		{"my talk ([[http://talks.golang.org/][slides here]])",
			`my talk (slides here)`},
	}
	for _, test := range tests {
		out := font(test.in)
		if out != test.out {
			t.Errorf("font(%q):\ngot\t%q\nwant\t%q", test.in, out, test.out)
		}
	}
}

func TestStyle(t *testing.T) {
	var tests = []struct {
		in  string
		out string
	}{
		{"", ""},
		{" ", " "},
		{"\tx", "\tx"},
		{"_a_", "a"},
		{"*a*", "a"},
		{"`a`", "a"},
		{"_a_b_", "a b"},
		{"_a__b_", "a_b"},
		{"_a___b_", "a_ b"},
		{"*a**b*?", "a*b?"},
		{"_a_<>_b_.", "a <> b."},
		{"(_a_<>_b_)", "(a <> b)"},
		{"((_a_), _b_, _c_).", "((a), b, c)."},
		{"(_a)", "(_a)"},
	}
	for _, test := range tests {
		out := string(Style(test.in))
		if out != test.out {
			t.Errorf("style(%q):\ngot\t%q\nwant\t%q", test.in, out, test.out)
		}
	}
}

func ExampleStyle() {
	const s = "*Gophers* are _clearly_ > *cats*!"
	fmt.Println(Style(s))
	// Output: Gophers are clearly > cats!
}
./present/code.go0000644000014500017510000001710612246613010013421 0ustar  michaelstaff// Copyright 2012 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 (
	"bufio"
	"bytes"
	"fmt"
	"html/template"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// Is the playground available?
var PlayEnabled = false

// TOOD(adg): replace the PlayEnabled flag with something less spaghetti-like.
// Instead this will probably be determined by a template execution Context
// value that contains various global metadata required when rendering
// templates.

func init() {
	Register("code", parseCode)
	Register("play", parseCode)
}

type Code struct {
	Text template.HTML
	Play bool   // runnable code
	Ext  string // file extension
	Raw  []byte // content of the file
}

func (c Code) TemplateName() string { return "code" }

// The input line is a .code or .play entry with a file name and an optional HLfoo marker on the end.
// Anything between the file and HL (if any) is an address expression, which we treat as a string here.
// We pick off the HL first, for easy parsing.
var (
	highlightRE = regexp.MustCompile(`\s+HL([a-zA-Z0-9_]+)?$`)
	hlCommentRE = regexp.MustCompile(`(.+) // HL(.*)$`)
	codeRE      = regexp.MustCompile(`\.(code|play)\s+((?:(?:-edit|-numbers)\s+)*)([^\s]+)(?:\s+(.*))?$`)
)

// parseCode parses a code present directive. Its syntax:
//   .code [-numbers] [-edit]  [address] [highlight]
// The directive may also be ".play" if the snippet is executable.
func parseCode(ctx *Context, sourceFile string, sourceLine int, cmd string) (Elem, error) {
	cmd = strings.TrimSpace(cmd)

	// Pull off the HL, if any, from the end of the input line.
	highlight := ""
	if hl := highlightRE.FindStringSubmatchIndex(cmd); len(hl) == 4 {
		highlight = cmd[hl[2]:hl[3]]
		cmd = cmd[:hl[2]-2]
	}

	// Parse the remaining command line.
	// Arguments:
	// args[0]: whole match
	// args[1]:  .code/.play
	// args[2]: flags ("-edit -numbers")
	// args[3]: file name
	// args[4]: optional address
	args := codeRE.FindStringSubmatch(cmd)
	if len(args) != 5 {
		return nil, fmt.Errorf("%s:%d: syntax error for .code/.play invocation", sourceFile, sourceLine)
	}
	command, flags, file, addr := args[1], args[2], args[3], strings.TrimSpace(args[4])
	play := command == "play" && PlayEnabled

	// Read in code file and (optionally) match address.
	filename := filepath.Join(filepath.Dir(sourceFile), file)
	textBytes, err := ctx.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}
	lo, hi, err := addrToByteRange(addr, 0, textBytes)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}

	// Acme pattern matches can stop mid-line,
	// so run to end of line in both directions if not at line start/end.
	for lo > 0 && textBytes[lo-1] != '\n' {
		lo--
	}
	if hi > 0 {
		for hi < len(textBytes) && textBytes[hi-1] != '\n' {
			hi++
		}
	}

	lines := codeLines(textBytes, lo, hi)

	for i, line := range lines {
		// Replace tabs by spaces, which work better in HTML.
		line.L = strings.Replace(line.L, "\t", "    ", -1)

		// Highlight lines that end with "// HL[highlight]"
		// and strip the magic comment.
		if m := hlCommentRE.FindStringSubmatch(line.L); m != nil {
			line.L = m[1]
			line.HL = m[2] == highlight
		}

		lines[i] = line
	}

	data := &codeTemplateData{
		Lines:   lines,
		Edit:    strings.Contains(flags, "-edit"),
		Numbers: strings.Contains(flags, "-numbers"),
	}

	// Include before and after in a hidden span for playground code.
	if play {
		data.Prefix = textBytes[:lo]
		data.Suffix = textBytes[hi:]
	}

	var buf bytes.Buffer
	if err := codeTemplate.Execute(&buf, data); err != nil {
		return nil, err
	}
	return Code{
		Text: template.HTML(buf.String()),
		Play: play,
		Ext:  filepath.Ext(filename),
		Raw:  textBytes,
	}, nil
}

type codeTemplateData struct {
	Lines          []codeLine
	Prefix, Suffix []byte
	Edit, Numbers  bool
}

var leadingSpaceRE = regexp.MustCompile(`^[ \t]*`)

var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{
	"trimSpace":    strings.TrimSpace,
	"leadingSpace": leadingSpaceRE.FindString,
}).Parse(codeTemplateHTML))

const codeTemplateHTML = `
{{with .Prefix}}
{{printf "%s" .}}
{{end}} {{/* */}}{{range .Lines}}{{/* */}}{{if .HL}}{{leadingSpace .L}}{{trimSpace .L}}{{/* */}}{{else}}{{.L}}{{end}}{{/* */}} {{end}}
{{with .Suffix}}
{{printf "%s" .}}
{{end}} ` // codeLine represents a line of code extracted from a source file. type codeLine struct { L string // The line of code. N int // The line number from the source file. HL bool // Whether the line should be highlighted. } // codeLines takes a source file and returns the lines that // span the byte range specified by start and end. // It discards lines that end in "OMIT". func codeLines(src []byte, start, end int) (lines []codeLine) { startLine := 1 for i, b := range src { if i == start { break } if b == '\n' { startLine++ } } s := bufio.NewScanner(bytes.NewReader(src[start:end])) for n := startLine; s.Scan(); n++ { l := s.Text() if strings.HasSuffix(l, "OMIT") { continue } lines = append(lines, codeLine{L: l, N: n}) } // Trim leading and trailing blank lines. for len(lines) > 0 && len(lines[0].L) == 0 { lines = lines[1:] } for len(lines) > 0 && len(lines[len(lines)-1].L) == 0 { lines = lines[:len(lines)-1] } return } func parseArgs(name string, line int, args []string) (res []interface{}, err error) { res = make([]interface{}, len(args)) for i, v := range args { if len(v) == 0 { return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) } switch v[0] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': n, err := strconv.Atoi(v) if err != nil { return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) } res[i] = n case '/': if len(v) < 2 || v[len(v)-1] != '/' { return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) } res[i] = v case '$': res[i] = "$" default: return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v) } } return } // parseArg returns the integer or string value of the argument and tells which it is. func parseArg(arg interface{}, max int) (ival int, sval string, isInt bool, err error) { switch n := arg.(type) { case int: if n <= 0 || n > max { return 0, "", false, fmt.Errorf("%d is out of range", n) } return n, "", true, nil case string: return 0, n, false, nil } return 0, "", false, fmt.Errorf("unrecognized argument %v type %T", arg, arg) } // match identifies the input line that matches the pattern in a code invocation. // If start>0, match lines starting there rather than at the beginning. // The return value is 1-indexed. func match(file string, start int, lines []string, pattern string) (int, error) { // $ matches the end of the file. if pattern == "$" { if len(lines) == 0 { return 0, fmt.Errorf("%q: empty file", file) } return len(lines), nil } // /regexp/ matches the line that matches the regexp. if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' { re, err := regexp.Compile(pattern[1 : len(pattern)-1]) if err != nil { return 0, err } for i := start; i < len(lines); i++ { if re.MatchString(lines[i]) { return i + 1, nil } } return 0, fmt.Errorf("%s: no match for %#q", file, pattern) } return 0, fmt.Errorf("unrecognized pattern: %q", pattern) } ./present/image.go0000644000014500017510000000154412246613010013570 0ustar michaelstaff// Copyright 2012 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("image", parseImage) } type Image struct { URL string Width int Height int } func (i Image) TemplateName() string { return "image" } func parseImage(ctx *Context, fileName string, lineno int, text string) (Elem, error) { args := strings.Fields(text) img := Image{URL: args[1]} a, err := parseArgs(fileName, lineno, args[2:]) if err != nil { return nil, err } switch len(a) { case 0: // no size parameters case 2: if v, ok := a[0].(int); ok { img.Height = v } if v, ok := a[1].(int); ok { img.Width = v } default: return nil, fmt.Errorf("incorrect image invocation: %q", text) } return img, nil } ./LICENSE0000644000014500017510000000270712246613010011506 0ustar michaelstaffCopyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ./godoc/0000755000014500017510000000000012246613010011566 5ustar michaelstaff./godoc/redirect/0000755000014500017510000000000012246613010013367 5ustar michaelstaff./godoc/redirect/redirect.go0000644000014500017510000001351612246613010015525 0ustar michaelstaff// 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 redirect provides hooks to register HTTP handlers that redirect old // godoc paths to their new equivalents and assist in accessing the issue // tracker, wiki, code review system, etc. package redirect import ( "net/http" "regexp" ) // Register registers HTTP handlers that redirect old godoc paths to their new // equivalents and assist in accessing the issue tracker, wiki, code review // system, etc. If mux is nil it uses http.DefaultServeMux. func Register(mux *http.ServeMux) { if mux == nil { mux = http.DefaultServeMux } handlePathRedirects(mux, pkgRedirects, "/pkg/") handlePathRedirects(mux, cmdRedirects, "/cmd/") for prefix, redirect := range prefixHelpers { p := "/" + prefix + "/" mux.Handle(p, PrefixHandler(p, redirect)) } for path, redirect := range redirects { mux.Handle(path, Handler(redirect)) } } func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) { for source, target := range redirects { h := Handler(prefix + target + "/") p := prefix + source mux.Handle(p, h) mux.Handle(p+"/", h) } } // Packages that were renamed between r60 and go1. var pkgRedirects = map[string]string{ "asn1": "encoding/asn1", "big": "math/big", "cmath": "math/cmplx", "csv": "encoding/csv", "exec": "os/exec", "exp/template/html": "html/template", "gob": "encoding/gob", "http": "net/http", "http/cgi": "net/http/cgi", "http/fcgi": "net/http/fcgi", "http/httptest": "net/http/httptest", "http/pprof": "net/http/pprof", "json": "encoding/json", "mail": "net/mail", "rand": "math/rand", "rpc": "net/rpc", "rpc/jsonrpc": "net/rpc/jsonrpc", "scanner": "text/scanner", "smtp": "net/smtp", "tabwriter": "text/tabwriter", "template": "text/template", "template/parse": "text/template/parse", "url": "net/url", "utf16": "unicode/utf16", "utf8": "unicode/utf8", "xml": "encoding/xml", } // Commands that were renamed between r60 and go1. var cmdRedirects = map[string]string{ "gofix": "fix", "goinstall": "go", "gopack": "pack", "gotest": "go", "govet": "vet", "goyacc": "yacc", } var redirects = map[string]string{ "/blog": "/blog/", "/build": "http://build.golang.org", "/change": "https://code.google.com/p/go/source/list", "/cl": "https://gocodereview.appspot.com/", "/cmd/godoc/": "http://godoc.org/code.google.com/p/go.tools/cmd/godoc/", "/cmd/vet/": "http://godoc.org/code.google.com/p/go.tools/cmd/vet/", "/issue": "https://code.google.com/p/go/issues", "/issue/new": "https://code.google.com/p/go/issues/entry", "/issues": "https://code.google.com/p/go/issues", "/play": "http://play.golang.org", // In Go 1.2 the references page is part of /doc/. "/ref": "/doc/#references", // This next rule clobbers /ref/spec and /ref/mem. // TODO(adg): figure out what to do here, if anything. // "/ref/": "/doc/#references", // Be nice to people who are looking in the wrong place. "/doc/mem": "/ref/mem", "/doc/spec": "/ref/spec", "/talks": "http://talks.golang.org", "/tour": "http://tour.golang.org", "/wiki": "https://code.google.com/p/go-wiki/w/list", "/doc/articles/c_go_cgo.html": "/blog/c-go-cgo", "/doc/articles/concurrency_patterns.html": "/blog/go-concurrency-patterns-timing-out-and", "/doc/articles/defer_panic_recover.html": "/blog/defer-panic-and-recover", "/doc/articles/error_handling.html": "/blog/error-handling-and-go", "/doc/articles/gobs_of_data.html": "/blog/gobs-of-data", "/doc/articles/godoc_documenting_go_code.html": "/blog/godoc-documenting-go-code", "/doc/articles/gos_declaration_syntax.html": "/blog/gos-declaration-syntax", "/doc/articles/image_draw.html": "/blog/go-imagedraw-package", "/doc/articles/image_package.html": "/blog/go-image-package", "/doc/articles/json_and_go.html": "/blog/json-and-go", "/doc/articles/json_rpc_tale_of_interfaces.html": "/blog/json-rpc-tale-of-interfaces", "/doc/articles/laws_of_reflection.html": "/blog/laws-of-reflection", "/doc/articles/race_detector.html": "/blog/race-detector", "/doc/articles/slices_usage_and_internals.html": "/blog/go-slices-usage-and-internals", "/doc/go_for_cpp_programmers.html": "https://code.google.com/p/go-wiki/wiki/GoForCPPProgrammers", "/doc/go_tutorial.html": "http://tour.golang.org/", } var prefixHelpers = map[string]string{ "change": "https://code.google.com/p/go/source/detail?r=", "cl": "https://codereview.appspot.com/", "issue": "https://code.google.com/p/go/issues/detail?id=", "play": "http://play.golang.org/", "talks": "http://talks.golang.org/", "wiki": "https://code.google.com/p/go-wiki/wiki/", } func Handler(target string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, target, http.StatusMovedPermanently) }) } var validId = regexp.MustCompile(`^[A-Za-z0-9-]*$`) func PrefixHandler(prefix, baseURL string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if p := r.URL.Path; p == prefix { // redirect /prefix/ to /prefix http.Redirect(w, r, p[:len(p)-1], http.StatusFound) return } id := r.URL.Path[len(prefix):] if !validId.MatchString(id) { http.Error(w, "Not found", http.StatusNotFound) return } target := baseURL + id http.Redirect(w, r, target, http.StatusFound) }) } ./godoc/format.go0000644000014500017510000002504112246613010013407 0ustar michaelstaff// 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 implements FormatSelections and FormatText. // FormatText is used to HTML-format Go and non-Go source // text with line numbers and highlighted sections. It is // built on top of FormatSelections, a generic formatter // for "selected" text. package godoc import ( "fmt" "go/scanner" "go/token" "io" "regexp" "strconv" "text/template" ) // ---------------------------------------------------------------------------- // Implementation of FormatSelections // A Segment describes a text segment [start, end). // The zero value of a Segment is a ready-to-use empty segment. // type Segment struct { start, end int } func (seg *Segment) isEmpty() bool { return seg.start >= seg.end } // A Selection is an "iterator" function returning a text segment. // Repeated calls to a selection return consecutive, non-overlapping, // non-empty segments, followed by an infinite sequence of empty // segments. The first empty segment marks the end of the selection. // type Selection func() Segment // A LinkWriter writes some start or end "tag" to w for the text offset offs. // It is called by FormatSelections at the start or end of each link segment. // type LinkWriter func(w io.Writer, offs int, start bool) // A SegmentWriter formats a text according to selections and writes it to w. // The selections parameter is a bit set indicating which selections provided // to FormatSelections overlap with the text segment: If the n'th bit is set // in selections, the n'th selection provided to FormatSelections is overlapping // with the text. // type SegmentWriter func(w io.Writer, text []byte, selections int) // FormatSelections takes a text and writes it to w using link and segment // writers lw and sw as follows: lw is invoked for consecutive segment starts // and ends as specified through the links selection, and sw is invoked for // consecutive segments of text overlapped by the same selections as specified // by selections. The link writer lw may be nil, in which case the links // Selection is ignored. // func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) { // If we have a link writer, make the links // selection the last entry in selections if lw != nil { selections = append(selections, links) } // compute the sequence of consecutive segment changes changes := newMerger(selections) // The i'th bit in bitset indicates that the text // at the current offset is covered by selections[i]. bitset := 0 lastOffs := 0 // Text segments are written in a delayed fashion // such that consecutive segments belonging to the // same selection can be combined (peephole optimization). // last describes the last segment which has not yet been written. var last struct { begin, end int // valid if begin < end bitset int } // flush writes the last delayed text segment flush := func() { if last.begin < last.end { sw(w, text[last.begin:last.end], last.bitset) } last.begin = last.end // invalidate last } // segment runs the segment [lastOffs, end) with the selection // indicated by bitset through the segment peephole optimizer. segment := func(end int) { if lastOffs < end { // ignore empty segments if last.end != lastOffs || last.bitset != bitset { // the last segment is not adjacent to or // differs from the new one flush() // start a new segment last.begin = lastOffs } last.end = end last.bitset = bitset } } for { // get the next segment change index, offs, start := changes.next() if index < 0 || offs > len(text) { // no more segment changes or the next change // is past the end of the text - we're done break } // determine the kind of segment change if lw != nil && index == len(selections)-1 { // we have a link segment change (see start of this function): // format the previous selection segment, write the // link tag and start a new selection segment segment(offs) flush() lastOffs = offs lw(w, offs, start) } else { // we have a selection change: // format the previous selection segment, determine // the new selection bitset and start a new segment segment(offs) lastOffs = offs mask := 1 << uint(index) if start { bitset |= mask } else { bitset &^= mask } } } segment(len(text)) flush() } // A merger merges a slice of Selections and produces a sequence of // consecutive segment change events through repeated next() calls. // type merger struct { selections []Selection segments []Segment // segments[i] is the next segment of selections[i] } const infinity int = 2e9 func newMerger(selections []Selection) *merger { segments := make([]Segment, len(selections)) for i, sel := range selections { segments[i] = Segment{infinity, infinity} if sel != nil { if seg := sel(); !seg.isEmpty() { segments[i] = seg } } } return &merger{selections, segments} } // next returns the next segment change: index specifies the Selection // to which the segment belongs, offs is the segment start or end offset // as determined by the start value. If there are no more segment changes, // next returns an index value < 0. // func (m *merger) next() (index, offs int, start bool) { // find the next smallest offset where a segment starts or ends offs = infinity index = -1 for i, seg := range m.segments { switch { case seg.start < offs: offs = seg.start index = i start = true case seg.end < offs: offs = seg.end index = i start = false } } if index < 0 { // no offset found => all selections merged return } // offset found - it's either the start or end offset but // either way it is ok to consume the start offset: set it // to infinity so it won't be considered in the following // next call m.segments[index].start = infinity if start { return } // end offset found - consume it m.segments[index].end = infinity // advance to the next segment for that selection seg := m.selections[index]() if !seg.isEmpty() { m.segments[index] = seg } return } // ---------------------------------------------------------------------------- // Implementation of FormatText // lineSelection returns the line segments for text as a Selection. func lineSelection(text []byte) Selection { i, j := 0, 0 return func() (seg Segment) { // find next newline, if any for j < len(text) { j++ if text[j-1] == '\n' { break } } if i < j { // text[i:j] constitutes a line seg = Segment{i, j} i = j } return } } // tokenSelection returns, as a selection, the sequence of // consecutive occurrences of token sel in the Go src text. // func tokenSelection(src []byte, sel token.Token) Selection { var s scanner.Scanner fset := token.NewFileSet() file := fset.AddFile("", fset.Base(), len(src)) s.Init(file, src, nil, scanner.ScanComments) return func() (seg Segment) { for { pos, tok, lit := s.Scan() if tok == token.EOF { break } offs := file.Offset(pos) if tok == sel { seg = Segment{offs, offs + len(lit)} break } } return } } // makeSelection is a helper function to make a Selection from a slice of pairs. // Pairs describing empty segments are ignored. // func makeSelection(matches [][]int) Selection { i := 0 return func() Segment { for i < len(matches) { m := matches[i] i++ if m[0] < m[1] { // non-empty segment return Segment{m[0], m[1]} } } return Segment{} } } // regexpSelection computes the Selection for the regular expression expr in text. func regexpSelection(text []byte, expr string) Selection { var matches [][]int if rx, err := regexp.Compile(expr); err == nil { matches = rx.FindAllIndex(text, -1) } return makeSelection(matches) } var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`) // RangeSelection computes the Selection for a text range described // by the argument str; the range description must match the selRx // regular expression. func RangeSelection(str string) Selection { m := selRx.FindStringSubmatch(str) if len(m) >= 2 { from, _ := strconv.Atoi(m[1]) to, _ := strconv.Atoi(m[2]) if from < to { return makeSelection([][]int{{from, to}}) } } return nil } // Span tags for all the possible selection combinations that may // be generated by FormatText. Selections are indicated by a bitset, // and the value of the bitset specifies the tag to be used. // // bit 0: comments // bit 1: highlights // bit 2: selections // var startTags = [][]byte{ /* 000 */ []byte(``), /* 001 */ []byte(``), /* 010 */ []byte(``), /* 011 */ []byte(``), /* 100 */ []byte(``), /* 101 */ []byte(``), /* 110 */ []byte(``), /* 111 */ []byte(``), } var endTag = []byte(``) func selectionTag(w io.Writer, text []byte, selections int) { if selections < len(startTags) { if tag := startTags[selections]; len(tag) > 0 { w.Write(tag) template.HTMLEscape(w, text) w.Write(endTag) return } } template.HTMLEscape(w, text) } // FormatText HTML-escapes text and writes it to w. // Consecutive text segments are wrapped in HTML spans (with tags as // defined by startTags and endTag) as follows: // // - if line >= 0, line number (ln) spans are inserted before each line, // starting with the value of line // - if the text is Go source, comments get the "comment" span class // - each occurrence of the regular expression pattern gets the "highlight" // span class // - text segments covered by selection get the "selection" span class // // Comments, highlights, and selections may overlap arbitrarily; the respective // HTML span classes are specified in the startTags variable. // func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) { var comments, highlights Selection if goSource { comments = tokenSelection(text, token.COMMENT) } if pattern != "" { highlights = regexpSelection(text, pattern) } if line >= 0 || comments != nil || highlights != nil || selection != nil { var lineTag LinkWriter if line >= 0 { lineTag = func(w io.Writer, _ int, start bool) { if start { fmt.Fprintf(w, "%6d\t", line, line) line++ } } } FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection) } else { template.HTMLEscape(w, text) } } ./godoc/snippet.go0000644000014500017510000000632012246613010013600 0ustar michaelstaff// Copyright 2009 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 contains the infrastructure to create a code // snippet for search results. // // Note: At the moment, this only creates HTML snippets. package godoc import ( "bytes" "fmt" "go/ast" "go/token" ) type Snippet struct { Line int Text string // HTML-escaped } func (p *Presentation) newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { // TODO instead of pretty-printing the node, should use the original source instead var buf1 bytes.Buffer p.writeNode(&buf1, fset, decl) // wrap text with
 tag
	var buf2 bytes.Buffer
	buf2.WriteString("
")
	FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil)
	buf2.WriteString("
") return &Snippet{fset.Position(id.Pos()).Line, buf2.String()} } func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec { for _, spec := range list { switch s := spec.(type) { case *ast.ImportSpec: if s.Name == id { return s } case *ast.ValueSpec: for _, n := range s.Names { if n == id { return s } } case *ast.TypeSpec: if s.Name == id { return s } } } return nil } func (p *Presentation) genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { s := findSpec(d.Specs, id) if s == nil { return nil // declaration doesn't contain id - exit gracefully } // only use the spec containing the id for the snippet dd := &ast.GenDecl{ Doc: d.Doc, TokPos: d.Pos(), Tok: d.Tok, Lparen: d.Lparen, Specs: []ast.Spec{s}, Rparen: d.Rparen, } return p.newSnippet(fset, dd, id) } func (p *Presentation) funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet { if d.Name != id { return nil // declaration doesn't contain id - exit gracefully } // only use the function signature for the snippet dd := &ast.FuncDecl{ Doc: d.Doc, Recv: d.Recv, Name: d.Name, Type: d.Type, } return p.newSnippet(fset, dd, id) } // NewSnippet creates a text snippet from a declaration decl containing an // identifier id. Parts of the declaration not containing the identifier // may be removed for a more compact snippet. func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { // TODO(bradfitz, adg): remove this function. But it's used by indexer, which // doesn't have a *Presentation, and NewSnippet needs a TabWidth. var p Presentation p.TabWidth = 4 return p.NewSnippet(fset, decl, id) } // NewSnippet creates a text snippet from a declaration decl containing an // identifier id. Parts of the declaration not containing the identifier // may be removed for a more compact snippet. func (p *Presentation) NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { var s *Snippet switch d := decl.(type) { case *ast.GenDecl: s = p.genSnippet(fset, d, id) case *ast.FuncDecl: s = p.funcSnippet(fset, d, id) } // handle failure gracefully if s == nil { var buf bytes.Buffer fmt.Fprintf(&buf, `could not generate a snippet for %s`, id.Name) s = &Snippet{fset.Position(id.Pos()).Line, buf.String()} } return s } ./godoc/server.go0000644000014500017510000004024712246613010013432 0ustar michaelstaff// 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 godoc import ( "bytes" "fmt" "go/ast" "go/build" "go/doc" "go/token" htmlpkg "html" "io" "io/ioutil" "log" "net/http" "os" pathpkg "path" "path/filepath" "sort" "strings" "text/template" "time" "code.google.com/p/go.tools/godoc/util" "code.google.com/p/go.tools/godoc/vfs" ) // 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 } func (s *handlerServer) registerWithMux(mux *http.ServeMux) { mux.Handle(s.pattern, s) } // getPageInfo returns the PageInfo for a package directory abspath. If the // parameter genAST is set, an AST containing only the package exports is // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) // is extracted from the AST. If there is no corresponding package in the // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- // directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is // set to the respective error but the error is not logged. // func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo { info := &PageInfo{Dirname: abspath} // Restrict to the package files that would be used when building // the package on this system. This makes sure that if there are // separate implementations for, say, Windows vs Unix, we don't // jumble them all together. // Note: Uses current binary's GOOS/GOARCH. // To use different pair, such as if we allowed the user to choose, // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir. ctxt := build.Default ctxt.IsAbsPath = pathpkg.IsAbs ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) { return h.c.fs.ReadDir(filepath.ToSlash(dir)) } ctxt.OpenFile = func(name string) (r io.ReadCloser, err error) { data, err := vfs.ReadFile(h.c.fs, filepath.ToSlash(name)) if err != nil { return nil, err } return ioutil.NopCloser(bytes.NewReader(data)), nil } pkginfo, err := ctxt.ImportDir(abspath, 0) // continue if there are no Go source files; we still want the directory info if _, nogo := err.(*build.NoGoError); err != nil && !nogo { info.Err = err return info } // collect package files pkgname := pkginfo.Name pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...) if len(pkgfiles) == 0 { // Commands written in C have no .go files in the build. // Instead, documentation may be found in an ignored file. // The file may be ignored via an explicit +build ignore // constraint (recommended), or by defining the package // documentation (historic). pkgname = "main" // assume package main since pkginfo.Name == "" pkgfiles = pkginfo.IgnoredGoFiles } // get package information, if any if len(pkgfiles) > 0 { // build package AST fset := token.NewFileSet() files, err := h.c.parseFiles(fset, abspath, pkgfiles) if err != nil { info.Err = err return info } // ignore any errors - they are due to unresolved identifiers pkg, _ := ast.NewPackage(fset, files, poorMansImporter, nil) // extract package documentation info.FSet = fset if mode&ShowSource == 0 { // show extracted documentation var m doc.Mode if mode&NoFiltering != 0 { m |= doc.AllDecls } if mode&AllMethods != 0 { m |= doc.AllMethods } info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath if mode&NoFactoryFuncs != 0 { for _, t := range info.PDoc.Types { info.PDoc.Funcs = append(info.PDoc.Funcs, t.Funcs...) t.Funcs = nil } sort.Sort(funcsByName(info.PDoc.Funcs)) } // collect examples testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...) files, err = h.c.parseFiles(fset, abspath, testfiles) if err != nil { log.Println("parsing examples:", err) } info.Examples = collectExamples(h.c, pkg, files) // collect any notes that we want to show if info.PDoc.Notes != nil { // could regexp.Compile only once per godoc, but probably not worth it if rx := h.p.NotesRx; rx != nil { for m, n := range info.PDoc.Notes { if rx.MatchString(m) { if info.Notes == nil { info.Notes = make(map[string][]*doc.Note) } info.Notes[m] = n } } } } } else { // show source code // TODO(gri) Consider eliminating export filtering in this mode, // or perhaps eliminating the mode altogether. if mode&NoFiltering == 0 { packageExports(fset, pkg) } info.PAst = ast.MergePackageFiles(pkg, 0) } info.IsMain = pkgname == "main" } // get directory information, if any var dir *Directory var timestamp time.Time if tree, ts := h.c.fsTree.Get(); tree != nil && tree.(*Directory) != nil { // directory tree is present; lookup respective directory // (may still fail if the file system was updated and the // new directory tree has not yet been computed) dir = tree.(*Directory).lookup(abspath) timestamp = ts } if dir == nil { // no directory tree present (too early after startup or // command-line mode); compute one level for this page // note: cannot use path filter here because in general // it doesn't contain the FSTree path dir = h.c.newDirectory(abspath, 1) timestamp = time.Now() } info.Dirs = dir.listing(true) info.DirTime = timestamp info.DirFlat = mode&FlatDir != 0 return info } type funcsByName []*doc.Func func (s funcsByName) Len() int { return len(s) } func (s funcsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s funcsByName) Less(i, j int) bool { return s[i].Name < s[j].Name } func (h *handlerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { if redirect(w, r) { return } relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):]) abspath := pathpkg.Join(h.fsRoot, relpath) mode := h.p.GetPageInfoMode(r) if relpath == builtinPkgPath { mode = NoFiltering | NoFactoryFuncs } info := h.GetPageInfo(abspath, relpath, mode) if info.Err != nil { log.Print(info.Err) h.p.ServeError(w, r, relpath, info.Err) return } if mode&NoHTML != 0 { h.p.ServeText(w, applyTemplate(h.p.PackageText, "packageText", info)) return } var tabtitle, title, subtitle string switch { case info.PAst != nil: tabtitle = info.PAst.Name.Name case info.PDoc != nil: tabtitle = info.PDoc.Name default: tabtitle = info.Dirname title = "Directory " if h.p.ShowTimestamps { subtitle = "Last update: " + info.DirTime.String() } } if title == "" { if info.IsMain { // assume that the directory name is the command name _, tabtitle = pathpkg.Split(relpath) title = "Command " } else { title = "Package " } } title += tabtitle // special cases for top-level package/command directories switch tabtitle { case "/src/pkg": tabtitle = "Packages" case "/src/cmd": tabtitle = "Commands" } h.p.ServePage(w, Page{ Title: title, Tabtitle: tabtitle, Subtitle: subtitle, Body: applyTemplate(h.p.PackageHTML, "packageHTML", info), }) } type PageInfoMode uint const ( NoFiltering PageInfoMode = 1 << iota // do not filter exports AllMethods // show all embedded methods ShowSource // show source code, do not extract documentation NoHTML // show result in textual form, do not generate HTML FlatDir // show directory in a flat (non-indented) manner NoFactoryFuncs // don't associate factory functions with their result types ) // modeNames defines names for each PageInfoMode flag. var modeNames = map[string]PageInfoMode{ "all": NoFiltering, "methods": AllMethods, "src": ShowSource, "text": NoHTML, "flat": FlatDir, } // GetPageInfoMode computes the PageInfoMode flags by analyzing the request // URL form value "m". It is value is a comma-separated list of mode names // as defined by modeNames (e.g.: m=src,text). func (p *Presentation) GetPageInfoMode(r *http.Request) PageInfoMode { var mode PageInfoMode for _, k := range strings.Split(r.FormValue("m"), ",") { if m, found := modeNames[strings.TrimSpace(k)]; found { mode |= m } } if p.AdjustPageInfoMode != nil { mode = p.AdjustPageInfoMode(r, mode) } return mode } // poorMansImporter returns a (dummy) package object named // by the last path component of the provided package path // (as is the convention for packages). This is sufficient // to resolve package identifiers without doing an actual // import. It never returns an error. // func poorMansImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { pkg := imports[path] if pkg == nil { // note that strings.LastIndex returns -1 if there is no "/" pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:]) pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import imports[path] = pkg } return pkg, nil } // globalNames returns a set of the names declared by all package-level // declarations. Method names are returned in the form Receiver_Method. func globalNames(pkg *ast.Package) map[string]bool { names := make(map[string]bool) for _, file := range pkg.Files { for _, decl := range file.Decls { addNames(names, decl) } } return names } // collectExamples collects examples for pkg from testfiles. func collectExamples(c *Corpus, pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example { var files []*ast.File for _, f := range testfiles { files = append(files, f) } var examples []*doc.Example globals := globalNames(pkg) for _, e := range doc.Examples(files...) { name := stripExampleSuffix(e.Name) if name == "" || globals[name] { examples = append(examples, e) } else if c.Verbose { log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name) } } return examples } // addNames adds the names declared by decl to the names set. // Method names are added in the form ReceiverTypeName_Method. func addNames(names map[string]bool, decl ast.Decl) { switch d := decl.(type) { case *ast.FuncDecl: name := d.Name.Name if d.Recv != nil { var typeName string switch r := d.Recv.List[0].Type.(type) { case *ast.StarExpr: typeName = r.X.(*ast.Ident).Name case *ast.Ident: typeName = r.Name } name = typeName + "_" + name } names[name] = true case *ast.GenDecl: for _, spec := range d.Specs { switch s := spec.(type) { case *ast.TypeSpec: names[s.Name.Name] = true case *ast.ValueSpec: for _, id := range s.Names { names[id.Name] = true } } } } } // packageExports is a local implementation of ast.PackageExports // which correctly updates each package file's comment list. // (The ast.PackageExports signature is frozen, hence the local // implementation). // func packageExports(fset *token.FileSet, pkg *ast.Package) { for _, src := range pkg.Files { cmap := ast.NewCommentMap(fset, src, src.Comments) ast.FileExports(src) src.Comments = cmap.Filter(src).Comments() } } func applyTemplate(t *template.Template, name string, data interface{}) []byte { var buf bytes.Buffer if err := t.Execute(&buf, data); err != nil { log.Printf("%s.Execute: %s", name, err) } return buf.Bytes() } func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { canonical := pathpkg.Clean(r.URL.Path) if !strings.HasSuffix(canonical, "/") { canonical += "/" } if r.URL.Path != canonical { url := *r.URL url.Path = canonical http.Redirect(w, r, url.String(), http.StatusMovedPermanently) redirected = true } return } func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) { c := pathpkg.Clean(r.URL.Path) c = strings.TrimRight(c, "/") if r.URL.Path != c { url := *r.URL url.Path = c http.Redirect(w, r, url.String(), http.StatusMovedPermanently) redirected = true } return } func (p *Presentation) serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { src, err := vfs.ReadFile(p.Corpus.fs, abspath) if err != nil { log.Printf("ReadFile: %s", err) p.ServeError(w, r, relpath, err) return } if r.FormValue("m") == "text" { p.ServeText(w, src) return } var buf bytes.Buffer buf.WriteString("
")
	FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), RangeSelection(r.FormValue("s")))
	buf.WriteString("
") fmt.Fprintf(&buf, `

View as plain text

`, htmlpkg.EscapeString(relpath)) p.ServePage(w, Page{ Title: title + " " + relpath, Tabtitle: relpath, Body: buf.Bytes(), }) } func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) { if redirect(w, r) { return } list, err := p.Corpus.fs.ReadDir(abspath) if err != nil { p.ServeError(w, r, relpath, err) return } p.ServePage(w, Page{ Title: "Directory " + relpath, Tabtitle: relpath, Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list), }) } func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) { // get HTML body contents src, err := vfs.ReadFile(p.Corpus.fs, abspath) if err != nil { log.Printf("ReadFile: %s", err) p.ServeError(w, r, relpath, err) return } // if it begins with "= 0 tr time.Duration // accumulated time running ts time.Duration // accumulated time stopped tt time.Time // earliest throttle time (= time Throttle returned + tm) } // NewThrottle creates a new Throttle with a throttle value r and // a minimum allocated run time slice of dt: // // r == 0: "empty" throttle; the goroutine is always sleeping // r == 1: full throttle; the goroutine is never sleeping // // A value of r == 0.6 throttles a goroutine such that it runs // approx. 60% of the time, and sleeps approx. 40% of the time. // Values of r < 0 or r > 1 are clamped down to values between 0 and 1. // Values of dt < 0 are set to 0. // func NewThrottle(r float64, dt time.Duration) *Throttle { var f float64 switch { case r <= 0: f = -1 // indicates always sleep case r >= 1: f = 0 // assume r == 1 (never sleep) default: // 0 < r < 1 f = (1 - r) / r } if dt < 0 { dt = 0 } return &Throttle{f: f, dt: dt, tt: time.Now().Add(dt)} } // Throttle calls time.Sleep such that over time the ratio tr/ts between // accumulated run (tr) and sleep times (ts) approximates the value 1/(1-r) // where r is the throttle value. Throttle returns immediately (w/o sleeping) // if less than tm ns have passed since the last call to Throttle. // func (p *Throttle) Throttle() { if p.f < 0 { select {} // always sleep } t0 := time.Now() if t0.Before(p.tt) { return // keep running (minimum time slice not exhausted yet) } // accumulate running time p.tr += t0.Sub(p.tt) + p.dt // compute sleep time // Over time we want: // // tr/ts = r/(1-r) // // Thus: // // ts = tr*f with f = (1-r)/r // // After some incremental run time δr added to the total run time // tr, the incremental sleep-time δs to get to the same ratio again // after waking up from time.Sleep is: if δs := time.Duration(float64(p.tr)*p.f) - p.ts; δs > 0 { time.Sleep(δs) } // accumulate (actual) sleep time t1 := time.Now() p.ts += t1.Sub(t0) // set earliest next throttle time p.tt = t1.Add(p.dt) } ./godoc/util/util.go0000644000014500017510000000434112246613010014051 0ustar michaelstaff// 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 util contains utility types and functions for godoc. package util import ( pathpkg "path" "sync" "time" "unicode/utf8" "code.google.com/p/go.tools/godoc/vfs" ) // An RWValue wraps a value and permits mutually exclusive // access to it and records the time the value was last set. type RWValue struct { mutex sync.RWMutex value interface{} timestamp time.Time // time of last set() } func (v *RWValue) Set(value interface{}) { v.mutex.Lock() v.value = value v.timestamp = time.Now() v.mutex.Unlock() } func (v *RWValue) Get() (interface{}, time.Time) { v.mutex.RLock() defer v.mutex.RUnlock() return v.value, v.timestamp } // IsText reports whether a significant prefix of s looks like correct UTF-8; // that is, if it is likely that s is human-readable text. func IsText(s []byte) bool { const max = 1024 // at least utf8.UTFMax if len(s) > max { s = s[0:max] } for i, c := range string(s) { if i+utf8.UTFMax > len(s) { // last char may be incomplete - ignore break } if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' { // decoding error or control character - not a text file return false } } return true } // textExt[x] is true if the extension x indicates a text file, and false otherwise. var textExt = map[string]bool{ ".css": false, // must be served raw ".js": false, // must be served raw } // IsTextFile reports whether the file has a known extension indicating // a text file, or if a significant chunk of the specified file looks like // correct UTF-8; that is, if it is likely that the file contains human- // readable text. func IsTextFile(fs vfs.Opener, filename string) bool { // if the extension is known, use it for decision making if isText, found := textExt[pathpkg.Ext(filename)]; found { return isText } // the extension is not known; read an initial chunk // of the file and check if it looks like text f, err := fs.Open(filename) if err != nil { return false } defer f.Close() var buf [1024]byte n, err := f.Read(buf[0:]) if err != nil { return false } return IsText(buf[0:n]) } ./godoc/meta.go0000644000014500017510000000706512246613010013053 0ustar michaelstaff// Copyright 2009 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 godoc import ( "bytes" "encoding/json" "log" pathpkg "path" "strings" "time" "code.google.com/p/go.tools/godoc/vfs" ) var ( doctype = []byte("") ) // ---------------------------------------------------------------------------- // Documentation Metadata // TODO(adg): why are some exported and some aren't? -brad type Metadata struct { Title string Subtitle string Template bool // execute as template Path string // canonical path for this page filePath string // filesystem path relative to goroot } func (m *Metadata) FilePath() string { return m.filePath } // extractMetadata extracts the Metadata from a byte slice. // It returns the Metadata value and the remaining data. // If no metadata is present the original byte slice is returned. // func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) { tail = b if !bytes.HasPrefix(b, jsonStart) { return } end := bytes.Index(b, jsonEnd) if end < 0 { return } b = b[len(jsonStart)-1 : end+1] // drop leading {{with .PDoc}} {{if $.IsMain}} {{/* command documentation */}} {{comment_html .Doc}} {{else}} {{/* package documentation */}}
import "{{html .ImportPath}}"
Overview
Index
{{if $.Examples}}
Examples
{{end}} {{if $.Dirs}}
Subdirectories
{{end}}

Overview ▾

{{comment_html .Doc}}
{{example_html $ ""}}

Index ▾

{{if .Consts}}
Constants
{{end}} {{if .Vars}}
Variables
{{end}} {{range .Funcs}} {{$name_html := html .Name}}
{{node_html $ .Decl false}}
{{end}} {{range .Types}} {{$tname_html := html .Name}}
type {{$tname_html}}
{{range .Funcs}} {{$name_html := html .Name}}
    {{node_html $ .Decl false}}
{{end}} {{range .Methods}} {{$name_html := html .Name}}
    {{node_html $ .Decl false}}
{{end}} {{end}} {{if $.Notes}} {{range $marker, $item := $.Notes}}
{{noteTitle $marker | html}}s
{{end}} {{end}}
{{if $.Examples}}

Examples

{{range $.Examples}}
{{example_name .Name}}
{{end}}
{{end}} {{with .Filenames}}

Package files

{{range .}} {{.|filename|html}} {{end}}

{{end}}
{{with .Consts}}

Constants

{{range .}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{end}} {{with .Vars}}

Variables

{{range .}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{end}} {{range .Funcs}} {{/* Name is a string - no need for FSet */}} {{$name_html := html .Name}}

func {{$name_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{example_html $ .Name}} {{end}} {{range .Types}} {{$tname := .Name}} {{$tname_html := html .Name}}

type {{$tname_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{range .Consts}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{range .Vars}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{example_html $ $tname}} {{range .Funcs}} {{$name_html := html .Name}}

func {{$name_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{example_html $ .Name}} {{end}} {{range .Methods}} {{$name_html := html .Name}}

func ({{html .Recv}}) {{$name_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{$name := printf "%s_%s" $tname .Name}} {{example_html $ $name}} {{end}} {{end}} {{end}} {{with $.Notes}} {{range $marker, $content := .}}

{{noteTitle $marker | html}}s

    {{range .}}
  • {{html .Body}}
  • {{end}}
{{end}} {{end}} {{end}} {{with .PAst}}
{{node_html $ . false}}
{{end}} {{with .Dirs}} {{/* DirList entries are numbers and strings - no need for FSet */}} {{if $.PDoc}}

Subdirectories

{{else}}
{{end}} {{if not $.DirFlat}} {{end}} {{range .List}} {{if $.DirFlat}} {{if .HasPkg}} {{end}} {{else}} {{end}} {{end}}
Name      Synopsis
..
{{html .Path}}      {{html .Synopsis}}
{{repeat `     ` .Depth}}{{html .Name}}      {{html .Synopsis}}
{{if $.PDoc}}{{else}}

Need more packages? Check out the sub-repositories and other Go projects.

{{end}} {{end}} ./godoc/static/search.html0000644000014500017510000000674012246613010015217 0ustar michaelstaff {{$query_url := urlquery .Query}} {{with .Alert}}

{{html .}}

{{end}} {{with .Alt}}

Did you mean: {{range .Alts}} {{html .}} {{end}}

{{end}} {{with .Pak}}

Package {{html $.Query}}

{{range .}} {{$pkg_html := pkgLink .Pak.Path | html}} {{end}}
{{$pkg_html}}

{{end}} {{range $key, $val := .Idents}} {{if $val}}

{{$key.Name}}

{{range $val}} {{$pkg_html := pkgLink .Path | html}} {{$doc_html := docLink .Path .Name| html}} {{html .Package}}.{{.Name}} {{if .Doc}}

{{comment_html .Doc}}

{{else}}

No documentation available

{{end}} {{end}} {{end}} {{end}} {{with .Hit}} {{with .Decls}}

Package-level declarations

{{range .}} {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} {{$src_html := srcLink .File.Path | html}} {{range .Groups}} {{range .}} {{$src_html}}:{{infoLine .}} {{infoSnippet_html .}} {{end}} {{end}} {{end}} {{end}} {{end}} {{with .Others}}

Local declarations and uses

{{range .}} {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} {{$src_html := srcLink .File.Path | html}} {{$src_html}} {{range .Groups}} {{end}}
{{index . 0 | infoKind_html}} {{range .}} {{infoLine .}} {{end}}
{{end}} {{end}} {{end}} {{end}} {{with .Textual}} {{if $.Complete}}

{{html $.Found}} textual occurrences

{{else}}

More than {{html $.Found}} textual occurrences

Not all files or lines containing "{{html $.Query}}" are shown.

{{end}}

{{range .}} {{$src_html := srcLink .Filename | html}} {{end}} {{if not $.Complete}} {{end}}
{{$src_html}}: {{len .Lines}} {{range .Lines}} {{html .}} {{end}} {{if not $.Complete}} ... {{end}}
...

{{end}} ./godoc/static/static.go0000644000014500017510000043347212246613010014710 0ustar michaelstaff// DO NOT EDIT ** This file was generated with the bake tool ** DO NOT EDIT // package static var Files = map[string]string{ "codewalk.html": `
Pop Out Code
code on leftright code width 70% filepaths shownhidden
{{range .Step}}
{{html .Title}}
{{with .Err}} ERROR LOADING FILE: {{html .}}

{{end}} {{.XML}}
{{html .}}
{{end}}
`, "codewalkdir.html": ` {{range .}} {{$name_html := html .Name}} {{end}}
{{$name_html}}   {{html .Title}}
`, "dirlist.html": `

{{range .}} {{$name_html := fileInfoName . | html}} {{end}}
File   Bytes   Modified
..
{{$name_html}} {{html .Size}} {{fileInfoTime . | html}}

`, "error.html": `

{{html .}}

`, "example.html": `

Example{{example_suffix .Name}}

{{with .Doc}}

{{html .}}

{{end}} {{$output := .Output}} {{with .Play}}
{{html $output}}
{{else}}

Code:

{{.Code}}
{{with .Output}}

Output:

{{html .}}
{{end}} {{end}}
`, "godoc.html": ` {{with .Tabtitle}} {{html .}} - The Go Programming Language {{else}} The Go Programming Language {{end}} {{if .SearchBox}} {{end}} {{if .Playground}}
{{end}}
{{with .Title}}

{{html .}}

{{end}} {{with .Subtitle}}

{{html .}}

{{end}} {{/* The Table of Contents is automatically inserted in this
. Do not delete this
. */}} {{/* Body is HTML-escaped elsewhere */}} {{printf "%s" .Body}}
{{if .Playground}} {{end}} `, "godocs.js": `// Copyright 2012 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. /* A little code to ease navigation of these documents. * * On window load we: * + Bind search box hint placeholder show/hide events (bindSearchEvents) * + Generate a table of contents (generateTOC) * + Bind foldable sections (bindToggles) * + Bind links to foldable sections (bindToggleLinks) */ (function() { 'use strict'; function bindSearchEvents() { var search = $('#search'); if (search.length === 0) { return; // no search box } function clearInactive() { if (search.is('.inactive')) { search.val(''); search.removeClass('inactive'); } } function restoreInactive() { if (search.val() !== '') { return; } search.val(search.attr('placeholder')); search.addClass('inactive'); } search.on('focus', clearInactive); search.on('blur', restoreInactive); restoreInactive(); } /* Generates a table of contents: looks for h2 and h3 elements and generates * links. "Decorates" the element with id=="nav" with this table of contents. */ function generateTOC() { if ($('#manual-nav').length > 0) { return; } var nav = $('#nav'); if (nav.length === 0) { return; } var toc_items = []; $(nav).nextAll('h2, h3').each(function() { var node = this; if (node.id == '') node.id = 'tmp_' + toc_items.length; var link = $('').attr('href', '#' + node.id).text($(node).text()); var item; if ($(node).is('h2')) { item = $('
'); } else { // h3 item = $('
'); } item.append(link); toc_items.push(item); }); if (toc_items.length <= 1) { return; } var dl1 = $('
'); var dl2 = $('
'); var split_index = (toc_items.length / 2) + 1; if (split_index < 8) { split_index = toc_items.length; } for (var i = 0; i < split_index; i++) { dl1.append(toc_items[i]); } for (/* keep using i */; i < toc_items.length; i++) { dl2.append(toc_items[i]); } var tocTable = $('').appendTo(nav); var tocBody = $('').appendTo(tocTable); var tocRow = $('').appendTo(tocBody); // 1st column $(']","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
","
"],thead:[1,"
').appendTo(tocRow).append(dl1); // 2nd column $('').appendTo(tocRow).append(dl2); } function bindToggle(el) { $('.toggleButton', el).click(function() { if ($(el).is('.toggle')) { $(el).addClass('toggleVisible').removeClass('toggle'); } else { $(el).addClass('toggle').removeClass('toggleVisible'); } }); } function bindToggles(selector) { $(selector).each(function(i, el) { bindToggle(el); }); } function bindToggleLink(el, prefix) { $(el).click(function() { var href = $(el).attr('href'); var i = href.indexOf('#'+prefix); if (i < 0) { return; } var id = '#' + prefix + href.slice(i+1+prefix.length); if ($(id).is('.toggle')) { $(id).find('.toggleButton').first().click(); } }); } function bindToggleLinks(selector, prefix) { $(selector).each(function(i, el) { bindToggleLink(el, prefix); }); } function setupDropdownPlayground() { if (!$('#page').is('.wide')) { return; // don't show on front page } var button = $('#playgroundButton'); var div = $('#playground'); var setup = false; button.toggle(function() { button.addClass('active'); div.show(); if (setup) { return; } setup = true; playground({ 'codeEl': $('.code', div), 'outputEl': $('.output', div), 'runEl': $('.run', div), 'fmtEl': $('.fmt', div), 'shareEl': $('.share', div), 'shareRedirect': 'http://play.golang.org/p/' }); }, function() { button.removeClass('active'); div.hide(); }); button.show(); $('#menu').css('min-width', '+=60'); } function setupInlinePlayground() { 'use strict'; // Set up playground when each element is toggled. $('div.play').each(function (i, el) { // Set up playground for this example. var setup = function() { var code = $('.code', el); playground({ 'codeEl': code, 'outputEl': $('.output', el), 'runEl': $('.run', el), 'fmtEl': $('.fmt', el), 'shareEl': $('.share', el), 'shareRedirect': 'http://play.golang.org/p/' }); // Make the code textarea resize to fit content. var resize = function() { code.height(0); var h = code[0].scrollHeight; code.height(h+20); // minimize bouncing. code.closest('.input').height(h); }; code.on('keydown', resize); code.on('keyup', resize); code.keyup(); // resize now. }; // If example already visible, set up playground now. if ($(el).is(':visible')) { setup(); return; } // Otherwise, set up playground when example is expanded. var built = false; $(el).closest('.toggle').click(function() { // Only set up once. if (!built) { setup(); built = true; } }); }); } // fixFocus tries to put focus to div#page so that keyboard navigation works. function fixFocus() { var page = $('div#page'); var topbar = $('div#topbar'); page.css('outline', 0); // disable outline when focused page.attr('tabindex', -1); // and set tabindex so that it is focusable $(window).resize(function (evt) { // only focus page when the topbar is at fixed position (that is, it's in // front of page, and keyboard event will go to the former by default.) // by focusing page, keyboard event will go to page so that up/down arrow, // space, etc. will work as expected. if (topbar.css('position') == "fixed") page.focus(); }).resize(); } function toggleHash() { var hash = $(window.location.hash); if (hash.is('.toggle')) { hash.find('.toggleButton').first().click(); } } function addPlusButtons() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/platform.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); } $(document).ready(function() { bindSearchEvents(); generateTOC(); bindToggles(".toggle"); bindToggles(".toggleVisible"); bindToggleLinks(".exampleLink", "example_"); bindToggleLinks(".overviewLink", ""); bindToggleLinks(".examplesLink", ""); bindToggleLinks(".indexLink", ""); setupDropdownPlayground(); setupInlinePlayground(); fixFocus(); toggleHash(); addPlusButtons(); // godoc.html defines window.initFuncs in the tag, and root.html and // codewalk.js push their on-page-ready functions to the list. // We execute those functions here, to avoid loading jQuery until the page // content is loaded. for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i](); }); })(); `, "jquery.js": `/*! jQuery v1.8.2 jquery.com | jquery.org/license */ (function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
t
",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/
","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
","
"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window);`, "opensearch.xml": ` godoc The Go Programming Language go golang /favicon.ico UTF-8 UTF-8 `, "package.html": ` {{with .PDoc}} {{if $.IsMain}} {{/* command documentation */}} {{comment_html .Doc}} {{else}} {{/* package documentation */}}
import "{{html .ImportPath}}"
Overview
Index
{{if $.Examples}}
Examples
{{end}} {{if $.Dirs}}
Subdirectories
{{end}}

Overview ▾

{{comment_html .Doc}}
{{example_html $ ""}}

Index ▾

{{if .Consts}}
Constants
{{end}} {{if .Vars}}
Variables
{{end}} {{range .Funcs}} {{$name_html := html .Name}}
{{node_html $ .Decl false}}
{{end}} {{range .Types}} {{$tname_html := html .Name}}
type {{$tname_html}}
{{range .Funcs}} {{$name_html := html .Name}}
    {{node_html $ .Decl false}}
{{end}} {{range .Methods}} {{$name_html := html .Name}}
    {{node_html $ .Decl false}}
{{end}} {{end}} {{if $.Notes}} {{range $marker, $item := $.Notes}}
{{noteTitle $marker | html}}s
{{end}} {{end}}
{{if $.Examples}}

Examples

{{range $.Examples}}
{{example_name .Name}}
{{end}}
{{end}} {{with .Filenames}}

Package files

{{range .}} {{.|filename|html}} {{end}}

{{end}}
{{with .Consts}}

Constants

{{range .}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{end}} {{with .Vars}}

Variables

{{range .}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{end}} {{range .Funcs}} {{/* Name is a string - no need for FSet */}} {{$name_html := html .Name}}

func {{$name_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{example_html $ .Name}} {{end}} {{range .Types}} {{$tname := .Name}} {{$tname_html := html .Name}}

type {{$tname_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{range .Consts}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{range .Vars}}
{{node_html $ .Decl true}}
{{comment_html .Doc}} {{end}} {{example_html $ $tname}} {{range .Funcs}} {{$name_html := html .Name}}

func {{$name_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{example_html $ .Name}} {{end}} {{range .Methods}} {{$name_html := html .Name}}

func ({{html .Recv}}) {{$name_html}}

{{node_html $ .Decl true}}
{{comment_html .Doc}} {{$name := printf "%s_%s" $tname .Name}} {{example_html $ $name}} {{end}} {{end}} {{end}} {{with $.Notes}} {{range $marker, $content := .}}

{{noteTitle $marker | html}}s

    {{range .}}
  • {{html .Body}}
  • {{end}}
{{end}} {{end}} {{end}} {{with .PAst}}
{{node_html $ . false}}
{{end}} {{with .Dirs}} {{/* DirList entries are numbers and strings - no need for FSet */}} {{if $.PDoc}}

Subdirectories

{{else}}
{{end}}
{{if not $.DirFlat}} {{end}} {{range .List}} {{if $.DirFlat}} {{if .HasPkg}} {{end}} {{else}} {{end}} {{end}}
Name      Synopsis
..
{{html .Path}}      {{html .Synopsis}}
{{repeat ` + "`" + `     ` + "`" + ` .Depth}}{{html .Name}}      {{html .Synopsis}}
{{if $.PDoc}}{{else}}

Need more packages? Check out the sub-repositories and other Go projects.

{{end}} {{end}} `, "package.txt": `{{with .PAst}}{{node $ .}}{{end}}{{/* --------------------------------------- */}}{{with .PDoc}}{{if $.IsMain}}COMMAND DOCUMENTATION {{comment_text .Doc " " "\t"}} {{else}}PACKAGE DOCUMENTATION package {{.Name}} import "{{.ImportPath}}" {{comment_text .Doc " " "\t"}} {{example_text $ "" " "}}{{/* --------------------------------------- */}}{{with .Consts}} CONSTANTS {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{end}}{{/* --------------------------------------- */}}{{with .Vars}} VARIABLES {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{end}}{{/* --------------------------------------- */}}{{with .Funcs}} FUNCTIONS {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{example_text $ .Name " "}}{{end}}{{end}}{{/* --------------------------------------- */}}{{with .Types}} TYPES {{range .}}{{$tname := .Name}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{range .Consts}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{range .Vars}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{example_text $ .Name " "}} {{range .Funcs}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{example_text $ .Name " "}} {{end}}{{range .Methods}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{$name := printf "%s_%s" $tname .Name}}{{example_text $ $name " "}}{{end}} {{end}}{{end}}{{end}}{{/* --------------------------------------- */}}{{with $.Notes}} {{range $marker, $content := .}} {{$marker}}S {{range $content}}{{comment_text .Body " " "\t"}} {{end}}{{end}}{{end}}{{end}}{{/* --------------------------------------- */}}{{with .Dirs}} SUBDIRECTORIES {{if $.DirFlat}}{{range .List}}{{if .HasPkg}} {{.Path}}{{end}}{{end}} {{else}}{{range .List}} {{repeat ` + "`" + `. ` + "`" + ` .Depth}}{{.Name}}{{end}} {{end}}{{end}} `, "play.js": `// Copyright 2012 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. function initPlayground(transport) { "use strict"; function text(node) { var s = ""; for (var i = 0; i < node.childNodes.length; i++) { var n = node.childNodes[i]; if (n.nodeType === 1) { if (n.tagName === "BUTTON") continue if (n.tagName === "SPAN" && n.className === "number") continue; if (n.tagName === "DIV" || n.tagName == "BR") { s += "\n"; } s += text(n); continue; } if (n.nodeType === 3) { s += n.nodeValue; } } return s.replace("\xA0", " "); // replace non-breaking spaces } function init(code) { var output = document.createElement('div'); var outpre = document.createElement('pre'); var running; if ($ && $(output).resizable) { $(output).resizable({ handles: "n,w,nw", minHeight: 27, minWidth: 135, maxHeight: 608, maxWidth: 990 }); } function onKill() { if (running) running.Kill(); } function onRun(e) { onKill(); output.style.display = "block"; outpre.innerHTML = ""; run1.style.display = "none"; var options = {Race: e.shiftKey}; running = transport.Run(text(code), PlaygroundOutput(outpre), options); } function onClose() { onKill(); output.style.display = "none"; run1.style.display = "inline-block"; } var run1 = document.createElement('button'); run1.innerHTML = 'Run'; run1.className = 'run'; run1.addEventListener("click", onRun, false); var run2 = document.createElement('button'); run2.className = 'run'; run2.innerHTML = 'Run'; run2.addEventListener("click", onRun, false); var kill = document.createElement('button'); kill.className = 'kill'; kill.innerHTML = 'Kill'; kill.addEventListener("click", onKill, false); var close = document.createElement('button'); close.className = 'close'; close.innerHTML = 'Close'; close.addEventListener("click", onClose, false); var button = document.createElement('div'); button.classList.add('buttons'); button.appendChild(run1); // Hack to simulate insertAfter code.parentNode.insertBefore(button, code.nextSibling); var buttons = document.createElement('div'); buttons.classList.add('buttons'); buttons.appendChild(run2); buttons.appendChild(kill); buttons.appendChild(close); output.classList.add('output'); output.appendChild(buttons); output.appendChild(outpre); output.style.display = "none"; code.parentNode.insertBefore(output, button.nextSibling); } var play = document.querySelectorAll('div.playground'); for (var i = 0; i < play.length; i++) { init(play[i]); } } `, "playground.js": `// Copyright 2012 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. /* In the absence of any formal way to specify interfaces in JavaScript, here's a skeleton implementation of a playground transport. function Transport() { // Set up any transport state (eg, make a websocket connnection). return { Run: function(body, output, options) { // Compile and run the program 'body' with 'options'. // Call the 'output' callback to display program output. return { Kill: function() { // Kill the running program. } }; } }; } // The output callback is called multiple times, and each time it is // passed an object of this form. var write = { Kind: 'string', // 'start', 'stdout', 'stderr', 'end' Body: 'string' // content of write or end status message } // The first call must be of Kind 'start' with no body. // Subsequent calls may be of Kind 'stdout' or 'stderr' // and must have a non-null Body string. // The final call should be of Kind 'end' with an optional // Body string, signifying a failure ("killed", for example). // The output callback must be of this form. // See PlaygroundOutput (below) for an implementation. function outputCallback(write) { } */ function HTTPTransport() { 'use strict'; // TODO(adg): support stderr function playback(output, events) { var timeout; output({Kind: 'start'}); function next() { if (events.length === 0) { output({Kind: 'end'}); return; } var e = events.shift(); if (e.Delay === 0) { output({Kind: 'stdout', Body: e.Message}); next(); return; } timeout = setTimeout(function() { output({Kind: 'stdout', Body: e.Message}); next(); }, e.Delay / 1000000); } next(); return { Stop: function() { clearTimeout(timeout); } } } function error(output, msg) { output({Kind: 'start'}); output({Kind: 'stderr', Body: msg}); output({Kind: 'end'}); } var seq = 0; return { Run: function(body, output, options) { seq++; var cur = seq; var playing; $.ajax('/compile', { type: 'POST', data: {'version': 2, 'body': body}, dataType: 'json', success: function(data) { if (seq != cur) return; if (!data) return; if (playing != null) playing.Stop(); if (data.Errors) { error(output, data.Errors); return; } playing = playback(output, data.Events); }, error: function() { error(output, 'Error communicating with remote server.'); } }); return { Kill: function() { if (playing != null) playing.Stop(); output({Kind: 'end', Body: 'killed'}); } }; } }; } function SocketTransport() { 'use strict'; var id = 0; var outputs = {}; var started = {}; var websocket = new WebSocket('ws://' + window.location.host + '/socket'); websocket.onclose = function() { console.log('websocket connection closed'); } websocket.onmessage = function(e) { var m = JSON.parse(e.data); var output = outputs[m.Id]; if (output === null) return; if (!started[m.Id]) { output({Kind: 'start'}); started[m.Id] = true; } output({Kind: m.Kind, Body: m.Body}); } function send(m) { websocket.send(JSON.stringify(m)); } return { Run: function(body, output, options) { var thisID = id+''; id++; outputs[thisID] = output; send({Id: thisID, Kind: 'run', Body: body, Options: options}); return { Kill: function() { send({Id: thisID, Kind: 'kill'}); } }; } }; } function PlaygroundOutput(el) { 'use strict'; return function(write) { if (write.Kind == 'start') { el.innerHTML = ''; return; } var cl = 'system'; if (write.Kind == 'stdout' || write.Kind == 'stderr') cl = write.Kind; var m = write.Body; if (write.Kind == 'end') m = '\nProgram exited' + (m?(': '+m):'.'); if (m.indexOf('IMAGE:') === 0) { // TODO(adg): buffer all writes before creating image var url = 'data:image/png;base64,' + m.substr(6); var img = document.createElement('img'); img.src = url; el.appendChild(img); return; } // ^L clears the screen. var s = m.split('\x0c'); if (s.length > 1) { el.innerHTML = ''; m = s.pop(); } m = m.replace(/&/g, '&'); m = m.replace(//g, '>'); var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight; var span = document.createElement('span'); span.className = cl; span.innerHTML = m; el.appendChild(span); if (needScroll) el.scrollTop = el.scrollHeight - el.offsetHeight; } } (function() { function lineHighlight(error) { var regex = /prog.go:([0-9]+)/g; var r = regex.exec(error); while (r) { $(".lines div").eq(r[1]-1).addClass("lineerror"); r = regex.exec(error); } } function highlightOutput(wrappedOutput) { return function(write) { if (write.Body) lineHighlight(write.Body); wrappedOutput(write); } } function lineClear() { $(".lineerror").removeClass("lineerror"); } // opts is an object with these keys // codeEl - code editor element // outputEl - program output element // runEl - run button element // fmtEl - fmt button element (optional) // shareEl - share button element (optional) // shareURLEl - share URL text input element (optional) // shareRedirect - base URL to redirect to on share (optional) // toysEl - toys select element (optional) // enableHistory - enable using HTML5 history API (optional) // transport - playground transport to use (default is HTTPTransport) function playground(opts) { var code = $(opts.codeEl); var transport = opts['transport'] || new HTTPTransport(); var running; // autoindent helpers. function insertTabs(n) { // find the selection start and end var start = code[0].selectionStart; var end = code[0].selectionEnd; // split the textarea content into two, and insert n tabs var v = code[0].value; var u = v.substr(0, start); for (var i=0; i 0) { curpos--; if (el.value[curpos] == "\t") { tabs++; } else if (tabs > 0 || el.value[curpos] == "\n") { break; } } setTimeout(function() { insertTabs(tabs); }, 1); } function keyHandler(e) { if (e.keyCode == 9) { // tab insertTabs(1); e.preventDefault(); return false; } if (e.keyCode == 13) { // enter if (e.shiftKey) { // +shift run(); e.preventDefault(); return false; } else { autoindent(e.target); } } return true; } code.unbind('keydown').bind('keydown', keyHandler); var outdiv = $(opts.outputEl).empty(); var output = $('
').appendTo(outdiv);
  
    function body() {
      return $(opts.codeEl).val();
    }
    function setBody(text) {
      $(opts.codeEl).val(text);
    }
    function origin(href) {
      return (""+href).split("/").slice(0, 3).join("/");
    }
  
    var pushedEmpty = (window.location.pathname == "/");
    function inputChanged() {
      if (pushedEmpty) {
        return;
      }
      pushedEmpty = true;
      $(opts.shareURLEl).hide();
      window.history.pushState(null, "", "/");
    }
    function popState(e) {
      if (e === null) {
        return;
      }
      if (e && e.state && e.state.code) {
        setBody(e.state.code);
      }
    }
    var rewriteHistory = false;
    if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
      rewriteHistory = true;
      code[0].addEventListener('input', inputChanged);
      window.addEventListener('popstate', popState);
    }

    function setError(error) {
      if (running) running.Kill();
      lineClear();
      lineHighlight(error);
      output.empty().addClass("error").text(error);
    }
    function loading() {
      lineClear();
      if (running) running.Kill();
      output.removeClass("error").text('Waiting for remote server...');
    }
    function run() {
      loading();
      running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
    }
    function fmt() {
      loading();
      $.ajax("/fmt", {
        data: {"body": body()},
        type: "POST",
        dataType: "json",
        success: function(data) {
          if (data.Error) {
            setError(data.Error);
          } else {
            setBody(data.Body);
            setError("");
          }
        }
      });
    }

    $(opts.runEl).click(run);
    $(opts.fmtEl).click(fmt);
  
    if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
      var shareURL;
      if (opts.shareURLEl) {
        shareURL = $(opts.shareURLEl).hide();
      }
      var sharing = false;
      $(opts.shareEl).click(function() {
        if (sharing) return;
        sharing = true;
        var sharingData = body();
        $.ajax("/share", {
          processData: false,
          data: sharingData,
          type: "POST",
          complete: function(xhr) {
            sharing = false;
            if (xhr.status != 200) {
              alert("Server error; try again.");
              return;
            }
            if (opts.shareRedirect) {
              window.location = opts.shareRedirect + xhr.responseText;
            }
            if (shareURL) {
              var path = "/p/" + xhr.responseText;
              var url = origin(window.location) + path;
              shareURL.show().val(url).focus().select();
  
              if (rewriteHistory) {
                var historyData = {"code": sharingData};
                window.history.pushState(historyData, "", path);
                pushedEmpty = false;
              }
            }
          }
        });
      });
    }
  
    if (opts.toysEl !== null) {
      $(opts.toysEl).bind('change', function() {
        var toy = $(this).val();
        $.ajax("/doc/play/"+toy, {
          processData: false,
          type: "GET",
          complete: function(xhr) {
            if (xhr.status != 200) {
              alert("Server error; try again.");
              return;
            }
            setBody(xhr.responseText);
          }
        });
      });
    }
  }

  window.playground = playground;
})();
`,
	"search.html": `
{{$query_url := urlquery .Query}}
{{with .Alert}}
	

{{html .}}

{{end}} {{with .Alt}}

Did you mean: {{range .Alts}} {{html .}} {{end}}

{{end}} {{with .Pak}}

Package {{html $.Query}}

{{range .}} {{$pkg_html := pkgLink .Pak.Path | html}} {{end}}
{{$pkg_html}}

{{end}} {{range $key, $val := .Idents}} {{if $val}}

{{$key.Name}}

{{range $val}} {{$pkg_html := pkgLink .Path | html}} {{$doc_html := docLink .Path .Name| html}} {{html .Package}}.{{.Name}} {{if .Doc}}

{{comment_html .Doc}}

{{else}}

No documentation available

{{end}} {{end}} {{end}} {{end}} {{with .Hit}} {{with .Decls}}

Package-level declarations

{{range .}} {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} {{$src_html := srcLink .File.Path | html}} {{range .Groups}} {{range .}} {{$src_html}}:{{infoLine .}} {{infoSnippet_html .}} {{end}} {{end}} {{end}} {{end}} {{end}} {{with .Others}}

Local declarations and uses

{{range .}} {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} {{$src_html := srcLink .File.Path | html}} {{$src_html}} {{range .Groups}} {{end}}
{{index . 0 | infoKind_html}} {{range .}} {{infoLine .}} {{end}}
{{end}} {{end}} {{end}} {{end}} {{with .Textual}} {{if $.Complete}}

{{html $.Found}} textual occurrences

{{else}}

More than {{html $.Found}} textual occurrences

Not all files or lines containing "{{html $.Query}}" are shown.

{{end}}

{{range .}} {{$src_html := srcLink .Filename | html}} {{end}} {{if not $.Complete}} {{end}}
{{$src_html}}: {{len .Lines}} {{range .Lines}} {{html .}} {{end}} {{if not $.Complete}} ... {{end}}
...

{{end}} `, "search.txt": `QUERY {{.Query}} {{with .Alert}}{{.}} {{end}}{{/* .Alert */}}{{/* --------------------------------------- */}}{{with .Alt}}DID YOU MEAN {{range .Alts}} {{.}} {{end}} {{end}}{{/* .Alt */}}{{/* --------------------------------------- */}}{{with .Pak}}PACKAGE {{$.Query}} {{range .}} {{pkgLink .Pak.Path}} {{end}} {{end}}{{/* .Pak */}}{{/* --------------------------------------- */}}{{range $key, $val := .Idents}}{{if $val}}{{$key.Name}} {{range $val.Idents}} {{.Path}}.{{.Name}} {{end}} {{end}}{{end}}{{/* .Idents */}}{{/* --------------------------------------- */}}{{with .Hit}}{{with .Decls}}PACKAGE-LEVEL DECLARATIONS {{range .}}package {{.Pak.Name}} {{range $file := .Files}}{{range .Groups}}{{range .}} {{srcLink $file.File.Path}}:{{infoLine .}}{{end}} {{end}}{{end}}{{/* .Files */}} {{end}}{{end}}{{/* .Decls */}}{{/* --------------------------------------- */}}{{with .Others}}LOCAL DECLARATIONS AND USES {{range .}}package {{.Pak.Name}} {{range $file := .Files}}{{range .Groups}}{{range .}} {{srcLink $file.File.Path}}:{{infoLine .}} {{end}}{{end}}{{end}}{{/* .Files */}} {{end}}{{end}}{{/* .Others */}}{{end}}{{/* .Hit */}}{{/* --------------------------------------- */}}{{if .Textual}}{{if .Complete}}{{.Found}} TEXTUAL OCCURRENCES{{else}}MORE THAN {{.Found}} TEXTUAL OCCURRENCES{{end}} {{range .Textual}}{{len .Lines}} {{srcLink .Filename}} {{end}}{{if not .Complete}}... ... {{end}}{{end}} `, "style.css": `body { margin: 0; font-family: Helvetica, Arial, sans-serif; font-size: 16px; } pre, code { font-family: Menlo, monospace; font-size: 14px; } pre { line-height: 18px; } pre .comment { color: #006600; } pre .highlight, pre .highlight-comment, pre .selection-highlight, pre .selection-highlight-comment { background: #FFFF00; } pre .selection, pre .selection-comment { background: #FF9632; } pre .ln { color: #999; } body { color: #222; } a, .exampleHeading .text { color: #375EAB; text-decoration: none; } a:hover, .exampleHeading .text:hover { text-decoration: underline; } p, pre, ul, ol { margin: 20px; } pre { background: #e9e9e9; padding: 10px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } h1, h2, h3, h4, .rootHeading { margin: 20px 0; padding: 0; color: #375EAB; font-weight: bold; } h1 { font-size: 24px; } h2 { font-size: 20px; background: #E0EBF5; padding: 2px 5px; } h3 { font-size: 20px; } h3, h4 { margin: 20px 5px; } h4 { font-size: 16px; } .rootHeading { font-size: 20px; margin: 0; } dl { margin: 20px; } dd { margin: 2px 20px; } dl, dd { font-size: 14px; } div#nav table td { vertical-align: top; } table.dir th { text-align: left; } table.dir td { word-wrap: break-word; vertical-align: top; } div#page.wide table.dir td.name { white-space: nowrap; } .alert { color: #AA0000; } div#heading { float: left; margin: 0 0 10px 0; padding: 21px 0; font-size: 20px; font-weight: normal; } div#heading a { color: #222; text-decoration: none; } div#topbar { background: #E0EBF5; height: 64px; overflow: hidden; } body { text-align: center; } div#page { width: 100%; } div#page > .container, div#topbar > .container { text-align: left; margin-left: auto; margin-right: auto; padding: 0 20px; width: 900px; } div#page.wide > .container, div#topbar.wide > .container { width: auto; } div#plusone { float: right; clear: right; margin-top: 5px; } div#footer { text-align: center; color: #666; font-size: 14px; margin: 40px 0; } div#menu > a, div#menu > input, div#learn .buttons a, div.play .buttons a, div#blog .read a { padding: 10px; text-decoration: none; font-size: 16px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } div#playground .buttons a, div#menu > a, div#menu > input { border: 1px solid #375EAB; } div#playground .buttons a, div#menu > a { color: white; background: #375EAB; } #playgroundButton.active { background: white; color: #375EAB; } a#start, div#learn .buttons a, div.play .buttons a, div#blog .read a { color: #222; border: 1px solid #375EAB; background: #E0EBF5; } .download { width: 150px; } div#menu { float: right; min-width: 590px; padding: 10px 0; text-align: right; } div#menu > a { margin-right: 5px; margin-bottom: 10px; padding: 10px; } div#menu > input { position: relative; top: 1px; width: 60px; background: white; color: #222; } div#menu > input.inactive { color: #999; } div.left { float: left; clear: left; } div.right { float: right; clear: right; } div.left, div.right { width: 415px; } div#learn, div#about { padding-top: 20px; } div#learn h2, div#about { margin: 0; } div#about { font-size: 20px; } div#about { height: 96px; } div#gopher { background: url(/doc/gopher/frontpage.png) no-repeat; background-position: center top; height: 155px; } a#start { display: block; padding: 10px; text-align: center; text-decoration: none; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } a#start .big { display: block; font-weight: bold; font-size: 20px; } a#start .desc { display: block; font-size: 14px; font-weight: normal; margin-top: 5px; } div#learn .popout { float: right; display: block; cursor: pointer; font-size: 12px; background: url(/doc/share.png) no-repeat; background-position: right top; padding: 5px 27px; } div#learn pre, div#learn textarea { padding: 0; margin: 0; font-family: Menlo, monospace; font-size: 14px; } div#learn .input { padding: 10px; margin-top: 10px; height: 150px; -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; } div#learn .input textarea { width: 100%; height: 100%; border: none; outline: none; resize: none; } div#learn .output { border-top: none !important; padding: 10px; height: 59px; overflow: auto; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -moz-border-radius-bottomleft: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; } div#learn .output pre { padding: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } div#learn .input, div#learn .input textarea, div#learn .output, div#learn .output pre { background: #FFFFD8; } div#learn .input, div#learn .output { border: 1px solid #375EAB; } div#learn .buttons { float: right; padding: 20px 0 10px 0; text-align: right; } div#learn .buttons a { height: 16px; margin-left: 5px; padding: 10px; } div#learn .toys { margin-top: 8px; } div#learn .toys select { border: 1px solid #375EAB; margin: 0; } div#learn .output .exit { display: none; } div#blog, div#video { margin-top: 40px; } div#blog > a, div#blog > div, div#blog > h2, div#video > a, div#video > div, div#video > h2 { margin-bottom: 10px; } div#blog .title, div#video .title { display: block; font-size: 20px; } div#blog .when { color: #666; font-size: 14px; } div#blog .read { text-align: right; } .toggleButton { cursor: pointer; } .toggle .collapsed { display: block; } .toggle .expanded { display: none; } .toggleVisible .collapsed { display: none; } .toggleVisible .expanded { display: block; } table.codetable { margin-left: auto; margin-right: auto; border-style: none; } table.codetable td { padding-right: 10px; } hr { border-style: none; border-top: 1px solid black; } img.gopher { float: right; margin-left: 10px; margin-bottom: 10px; z-index: -1; } .pkgGopher { text-align: right; } .pkgGopher .gopher { float: none; position: relative; top: -40px; margin-bottom: -120px; } h2 { clear: right; } /* example and drop-down playground */ div.play { padding: 0 20px 40px 20px; } div.play pre, div.play textarea, div.play .lines { padding: 0; margin: 0; font-family: Menlo, monospace; font-size: 14px; } div.play .input { padding: 10px; margin-top: 10px; -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; overflow: hidden; } div.play .input textarea { width: 100%; height: 100%; border: none; outline: none; resize: none; overflow: hidden; } div#playground .input textarea { overflow: auto; resize: auto; } div.play .output { border-top: none !important; padding: 10px; max-height: 200px; overflow: auto; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -moz-border-radius-bottomleft: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; } div.play .output pre { padding: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } div.play .input, div.play .input textarea, div.play .output, div.play .output pre { background: #FFFFD8; } div.play .input, div.play .output { border: 1px solid #375EAB; } div.play .buttons { float: right; padding: 20px 0 10px 0; text-align: right; } div.play .buttons a { height: 16px; margin-left: 5px; padding: 10px; cursor: pointer; } .output .stderr { color: #933; } .output .system { color: #999; } /* drop-down playground */ #playgroundButton, div#playground { /* start hidden; revealed by javascript */ display: none; } div#playground { position: absolute; top: 63px; right: 20px; padding: 0 10px 10px 10px; z-index: 1; text-align: left; background: #E0EBF5; border: 1px solid #B0BBC5; border-top: none; -webkit-border-bottom-left-radius: 5px; -webkit-border-bottom-right-radius: 5px; -moz-border-radius-bottomleft: 5px; -moz-border-radius-bottomright: 5px; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } div#playground .code { width: 520px; height: 200px; } div#playground .output { height: 100px; } /* Inline runnable snippets (play.js/initPlayground) */ #content .code pre, #content .playground pre, #content .output pre { margin: 0; padding: 0; background: none; border: none; outline: 0px solid transparent; overflow: auto; } #content .playground .number, #content .code .number { color: #999; } #content .code, #content .playground, #content .output { width: auto; margin: 20px; padding: 10px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } #content .code, #content .playground { background: #e9e9e9; } #content .output { background: #202020; } #content .output .stdout, #content .output pre { color: #e6e6e6; } #content .output .stderr, #content .output .error { color: rgb(244, 74, 63); } #content .output .system, #content .output .exit { color: rgb(255, 209, 77) } #content .buttons { position: relative; float: right; top: -50px; right: 30px; } #content .output .buttons { top: -60px; right: 0; height: 0; } #content .buttons .kill { display: none; visibility: hidden; } `, } ./godoc/static/codewalk.html0000644000014500017510000000433512246613010015541 0ustar michaelstaff
Pop Out Code
code on leftright code width 70% filepaths shownhidden
{{range .Step}}
{{html .Title}}
{{with .Err}} ERROR LOADING FILE: {{html .}}

{{end}} {{.XML}}
{{html .}}
{{end}}
./godoc/static/godoc.html0000644000014500017510000000521312246613010015037 0ustar michaelstaff {{with .Tabtitle}} {{html .}} - The Go Programming Language {{else}} The Go Programming Language {{end}} {{if .SearchBox}} {{end}} {{if .Playground}}
{{end}}
{{with .Title}}

{{html .}}

{{end}} {{with .Subtitle}}

{{html .}}

{{end}} {{/* The Table of Contents is automatically inserted in this
. Do not delete this
. */}} {{/* Body is HTML-escaped elsewhere */}} {{printf "%s" .Body}}
{{if .Playground}} {{end}} ./godoc/static/doc.go0000644000014500017510000000055212246613010014153 0ustar michaelstaff// 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 static exports a map of static file content that supports the godoc // user interface. The map should be used with the mapfs package, see // code.google.com/p/godoc/vfs/mapfs. package static ./godoc/static/play.js0000644000014500017510000000523412246613010014364 0ustar michaelstaff// Copyright 2012 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. function initPlayground(transport) { "use strict"; function text(node) { var s = ""; for (var i = 0; i < node.childNodes.length; i++) { var n = node.childNodes[i]; if (n.nodeType === 1) { if (n.tagName === "BUTTON") continue if (n.tagName === "SPAN" && n.className === "number") continue; if (n.tagName === "DIV" || n.tagName == "BR") { s += "\n"; } s += text(n); continue; } if (n.nodeType === 3) { s += n.nodeValue; } } return s.replace("\xA0", " "); // replace non-breaking spaces } function init(code) { var output = document.createElement('div'); var outpre = document.createElement('pre'); var running; if ($ && $(output).resizable) { $(output).resizable({ handles: "n,w,nw", minHeight: 27, minWidth: 135, maxHeight: 608, maxWidth: 990 }); } function onKill() { if (running) running.Kill(); } function onRun(e) { onKill(); output.style.display = "block"; outpre.innerHTML = ""; run1.style.display = "none"; var options = {Race: e.shiftKey}; running = transport.Run(text(code), PlaygroundOutput(outpre), options); } function onClose() { onKill(); output.style.display = "none"; run1.style.display = "inline-block"; } var run1 = document.createElement('button'); run1.innerHTML = 'Run'; run1.className = 'run'; run1.addEventListener("click", onRun, false); var run2 = document.createElement('button'); run2.className = 'run'; run2.innerHTML = 'Run'; run2.addEventListener("click", onRun, false); var kill = document.createElement('button'); kill.className = 'kill'; kill.innerHTML = 'Kill'; kill.addEventListener("click", onKill, false); var close = document.createElement('button'); close.className = 'close'; close.innerHTML = 'Close'; close.addEventListener("click", onClose, false); var button = document.createElement('div'); button.classList.add('buttons'); button.appendChild(run1); // Hack to simulate insertAfter code.parentNode.insertBefore(button, code.nextSibling); var buttons = document.createElement('div'); buttons.classList.add('buttons'); buttons.appendChild(run2); buttons.appendChild(kill); buttons.appendChild(close); output.classList.add('output'); output.appendChild(buttons); output.appendChild(outpre); output.style.display = "none"; code.parentNode.insertBefore(output, button.nextSibling); } var play = document.querySelectorAll('div.playground'); for (var i = 0; i < play.length; i++) { init(play[i]); } } ./godoc/static/example.html0000644000014500017510000000167012246613010015402 0ustar michaelstaff

Example{{example_suffix .Name}}

{{with .Doc}}

{{html .}}

{{end}} {{$output := .Output}} {{with .Play}}
{{html $output}}
{{else}}

Code:

{{.Code}}
{{with .Output}}

Output:

{{html .}}
{{end}} {{end}}
./godoc/static/codewalkdir.html0000644000014500017510000000055412246613010016237 0ustar michaelstaff {{range .}} {{$name_html := html .Name}} {{end}}
{{$name_html}}   {{html .Title}}
./godoc/static/godocs.js0000644000014500017510000001531712246613010014700 0ustar michaelstaff// Copyright 2012 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. /* A little code to ease navigation of these documents. * * On window load we: * + Bind search box hint placeholder show/hide events (bindSearchEvents) * + Generate a table of contents (generateTOC) * + Bind foldable sections (bindToggles) * + Bind links to foldable sections (bindToggleLinks) */ (function() { 'use strict'; function bindSearchEvents() { var search = $('#search'); if (search.length === 0) { return; // no search box } function clearInactive() { if (search.is('.inactive')) { search.val(''); search.removeClass('inactive'); } } function restoreInactive() { if (search.val() !== '') { return; } search.val(search.attr('placeholder')); search.addClass('inactive'); } search.on('focus', clearInactive); search.on('blur', restoreInactive); restoreInactive(); } /* Generates a table of contents: looks for h2 and h3 elements and generates * links. "Decorates" the element with id=="nav" with this table of contents. */ function generateTOC() { if ($('#manual-nav').length > 0) { return; } var nav = $('#nav'); if (nav.length === 0) { return; } var toc_items = []; $(nav).nextAll('h2, h3').each(function() { var node = this; if (node.id == '') node.id = 'tmp_' + toc_items.length; var link = $('').attr('href', '#' + node.id).text($(node).text()); var item; if ($(node).is('h2')) { item = $('
'); } else { // h3 item = $('
'); } item.append(link); toc_items.push(item); }); if (toc_items.length <= 1) { return; } var dl1 = $('
'); var dl2 = $('
'); var split_index = (toc_items.length / 2) + 1; if (split_index < 8) { split_index = toc_items.length; } for (var i = 0; i < split_index; i++) { dl1.append(toc_items[i]); } for (/* keep using i */; i < toc_items.length; i++) { dl2.append(toc_items[i]); } var tocTable = $('').appendTo(nav); var tocBody = $('').appendTo(tocTable); var tocRow = $('').appendTo(tocBody); // 1st column $(']","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
","
"],thead:[1,"
').appendTo(tocRow).append(dl1); // 2nd column $('').appendTo(tocRow).append(dl2); } function bindToggle(el) { $('.toggleButton', el).click(function() { if ($(el).is('.toggle')) { $(el).addClass('toggleVisible').removeClass('toggle'); } else { $(el).addClass('toggle').removeClass('toggleVisible'); } }); } function bindToggles(selector) { $(selector).each(function(i, el) { bindToggle(el); }); } function bindToggleLink(el, prefix) { $(el).click(function() { var href = $(el).attr('href'); var i = href.indexOf('#'+prefix); if (i < 0) { return; } var id = '#' + prefix + href.slice(i+1+prefix.length); if ($(id).is('.toggle')) { $(id).find('.toggleButton').first().click(); } }); } function bindToggleLinks(selector, prefix) { $(selector).each(function(i, el) { bindToggleLink(el, prefix); }); } function setupDropdownPlayground() { if (!$('#page').is('.wide')) { return; // don't show on front page } var button = $('#playgroundButton'); var div = $('#playground'); var setup = false; button.toggle(function() { button.addClass('active'); div.show(); if (setup) { return; } setup = true; playground({ 'codeEl': $('.code', div), 'outputEl': $('.output', div), 'runEl': $('.run', div), 'fmtEl': $('.fmt', div), 'shareEl': $('.share', div), 'shareRedirect': 'http://play.golang.org/p/' }); }, function() { button.removeClass('active'); div.hide(); }); button.show(); $('#menu').css('min-width', '+=60'); } function setupInlinePlayground() { 'use strict'; // Set up playground when each element is toggled. $('div.play').each(function (i, el) { // Set up playground for this example. var setup = function() { var code = $('.code', el); playground({ 'codeEl': code, 'outputEl': $('.output', el), 'runEl': $('.run', el), 'fmtEl': $('.fmt', el), 'shareEl': $('.share', el), 'shareRedirect': 'http://play.golang.org/p/' }); // Make the code textarea resize to fit content. var resize = function() { code.height(0); var h = code[0].scrollHeight; code.height(h+20); // minimize bouncing. code.closest('.input').height(h); }; code.on('keydown', resize); code.on('keyup', resize); code.keyup(); // resize now. }; // If example already visible, set up playground now. if ($(el).is(':visible')) { setup(); return; } // Otherwise, set up playground when example is expanded. var built = false; $(el).closest('.toggle').click(function() { // Only set up once. if (!built) { setup(); built = true; } }); }); } // fixFocus tries to put focus to div#page so that keyboard navigation works. function fixFocus() { var page = $('div#page'); var topbar = $('div#topbar'); page.css('outline', 0); // disable outline when focused page.attr('tabindex', -1); // and set tabindex so that it is focusable $(window).resize(function (evt) { // only focus page when the topbar is at fixed position (that is, it's in // front of page, and keyboard event will go to the former by default.) // by focusing page, keyboard event will go to page so that up/down arrow, // space, etc. will work as expected. if (topbar.css('position') == "fixed") page.focus(); }).resize(); } function toggleHash() { var hash = $(window.location.hash); if (hash.is('.toggle')) { hash.find('.toggleButton').first().click(); } } function addPlusButtons() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/platform.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); } $(document).ready(function() { bindSearchEvents(); generateTOC(); bindToggles(".toggle"); bindToggles(".toggleVisible"); bindToggleLinks(".exampleLink", "example_"); bindToggleLinks(".overviewLink", ""); bindToggleLinks(".examplesLink", ""); bindToggleLinks(".indexLink", ""); setupDropdownPlayground(); setupInlinePlayground(); fixFocus(); toggleHash(); addPlusButtons(); // godoc.html defines window.initFuncs in the tag, and root.html and // codewalk.js push their on-page-ready functions to the list. // We execute those functions here, to avoid loading jQuery until the page // content is loaded. for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i](); }); })(); ./godoc/static/search.txt0000644000014500017510000000262212246613010015065 0ustar michaelstaffQUERY {{.Query}} {{with .Alert}}{{.}} {{end}}{{/* .Alert */}}{{/* --------------------------------------- */}}{{with .Alt}}DID YOU MEAN {{range .Alts}} {{.}} {{end}} {{end}}{{/* .Alt */}}{{/* --------------------------------------- */}}{{with .Pak}}PACKAGE {{$.Query}} {{range .}} {{pkgLink .Pak.Path}} {{end}} {{end}}{{/* .Pak */}}{{/* --------------------------------------- */}}{{range $key, $val := .Idents}}{{if $val}}{{$key.Name}} {{range $val.Idents}} {{.Path}}.{{.Name}} {{end}} {{end}}{{end}}{{/* .Idents */}}{{/* --------------------------------------- */}}{{with .Hit}}{{with .Decls}}PACKAGE-LEVEL DECLARATIONS {{range .}}package {{.Pak.Name}} {{range $file := .Files}}{{range .Groups}}{{range .}} {{srcLink $file.File.Path}}:{{infoLine .}}{{end}} {{end}}{{end}}{{/* .Files */}} {{end}}{{end}}{{/* .Decls */}}{{/* --------------------------------------- */}}{{with .Others}}LOCAL DECLARATIONS AND USES {{range .}}package {{.Pak.Name}} {{range $file := .Files}}{{range .Groups}}{{range .}} {{srcLink $file.File.Path}}:{{infoLine .}} {{end}}{{end}}{{end}}{{/* .Files */}} {{end}}{{end}}{{/* .Others */}}{{end}}{{/* .Hit */}}{{/* --------------------------------------- */}}{{if .Textual}}{{if .Complete}}{{.Found}} TEXTUAL OCCURRENCES{{else}}MORE THAN {{.Found}} TEXTUAL OCCURRENCES{{end}} {{range .Textual}}{{len .Lines}} {{srcLink .Filename}} {{end}}{{if not .Complete}}... ... {{end}}{{end}} ./godoc/static/dirlist.html0000644000014500017510000000121412246613010015413 0ustar michaelstaff

{{range .}} {{$name_html := fileInfoName . | html}} {{end}}
File   Bytes   Modified
..
{{$name_html}} {{html .Size}} {{fileInfoTime . | html}}

./godoc/static/bake.go0000644000014500017510000000345412246613010014314 0ustar michaelstaff// 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 ignore // Command bake takes a list of file names and writes a Go source file to // standard output that declares a map of string constants containing the input files. // // For example, the command // bake foo.html bar.txt // produces a source file in package main that declares the variable bakedFiles // that is a map with keys "foo.html" and "bar.txt" that contain the contents // of foo.html and bar.txt. package main import ( "bufio" "bytes" "fmt" "io/ioutil" "os" "path/filepath" "unicode/utf8" ) func main() { if err := bake(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func bake(files []string) error { w := bufio.NewWriter(os.Stdout) fmt.Fprintf(w, "%v\n\npackage static\n\n", warning) fmt.Fprintf(w, "var Files = map[string]string{\n") for _, fn := range files { b, err := ioutil.ReadFile(fn) if err != nil { return err } if !utf8.Valid(b) { return fmt.Errorf("file %s is not valid UTF-8", fn) } fmt.Fprintf(w, "\t%q: `%s`,\n", filepath.Base(fn), sanitize(b)) } fmt.Fprintln(w, "}") return w.Flush() } // sanitize prepares a string as a raw string constant. func sanitize(b []byte) []byte { // Replace ` with `+"`"+` b = bytes.Replace(b, []byte("`"), []byte("`+\"`\"+`"), -1) // Replace BOM with `+"\xEF\xBB\xBF"+` // (A BOM is valid UTF-8 but not permitted in Go source files. // I wouldn't bother handling this, but for some insane reason // jquery.js has a BOM somewhere in the middle.) return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1) } const warning = "// DO NOT EDIT ** This file was generated with the bake tool ** DO NOT EDIT //" ./godoc/static/error.html0000644000014500017510000000035112246613010015073 0ustar michaelstaff

{{html .}}

./godoc/static/jquery.js0000644000014500017510000026637312246613010014753 0ustar michaelstaff/*! jQuery v1.8.2 jquery.com | jquery.org/license */ (function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
t
",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/
","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
","
"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window);./godoc/static/bake.sh0000755000014500017510000000070212246613010014315 0ustar michaelstaff#!/usr/bin/env bash # 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. set -e STATIC=" codewalk.html codewalkdir.html dirlist.html error.html example.html godoc.html godocs.js jquery.js opensearch.xml package.html package.txt play.js playground.js search.html search.txt style.css " go run bake.go $STATIC | gofmt > static.go ./godoc/static/package.txt0000644000014500017510000000346012246613010015214 0ustar michaelstaff{{with .PAst}}{{node $ .}}{{end}}{{/* --------------------------------------- */}}{{with .PDoc}}{{if $.IsMain}}COMMAND DOCUMENTATION {{comment_text .Doc " " "\t"}} {{else}}PACKAGE DOCUMENTATION package {{.Name}} import "{{.ImportPath}}" {{comment_text .Doc " " "\t"}} {{example_text $ "" " "}}{{/* --------------------------------------- */}}{{with .Consts}} CONSTANTS {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{end}}{{/* --------------------------------------- */}}{{with .Vars}} VARIABLES {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{end}}{{/* --------------------------------------- */}}{{with .Funcs}} FUNCTIONS {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{example_text $ .Name " "}}{{end}}{{end}}{{/* --------------------------------------- */}}{{with .Types}} TYPES {{range .}}{{$tname := .Name}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{range .Consts}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{range .Vars}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{end}}{{example_text $ .Name " "}} {{range .Funcs}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{example_text $ .Name " "}} {{end}}{{range .Methods}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}} {{$name := printf "%s_%s" $tname .Name}}{{example_text $ $name " "}}{{end}} {{end}}{{end}}{{end}}{{/* --------------------------------------- */}}{{with $.Notes}} {{range $marker, $content := .}} {{$marker}}S {{range $content}}{{comment_text .Body " " "\t"}} {{end}}{{end}}{{end}}{{end}}{{/* --------------------------------------- */}}{{with .Dirs}} SUBDIRECTORIES {{if $.DirFlat}}{{range .List}}{{if .HasPkg}} {{.Path}}{{end}}{{end}} {{else}}{{range .List}} {{repeat `. ` .Depth}}{{.Name}}{{end}} {{end}}{{end}} ./godoc/static/style.css0000644000014500017510000002267212246613010014740 0ustar michaelstaffbody { margin: 0; font-family: Helvetica, Arial, sans-serif; font-size: 16px; } pre, code { font-family: Menlo, monospace; font-size: 14px; } pre { line-height: 18px; } pre .comment { color: #006600; } pre .highlight, pre .highlight-comment, pre .selection-highlight, pre .selection-highlight-comment { background: #FFFF00; } pre .selection, pre .selection-comment { background: #FF9632; } pre .ln { color: #999; } body { color: #222; } a, .exampleHeading .text { color: #375EAB; text-decoration: none; } a:hover, .exampleHeading .text:hover { text-decoration: underline; } p, pre, ul, ol { margin: 20px; } pre { background: #e9e9e9; padding: 10px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } h1, h2, h3, h4, .rootHeading { margin: 20px 0; padding: 0; color: #375EAB; font-weight: bold; } h1 { font-size: 24px; } h2 { font-size: 20px; background: #E0EBF5; padding: 2px 5px; } h3 { font-size: 20px; } h3, h4 { margin: 20px 5px; } h4 { font-size: 16px; } .rootHeading { font-size: 20px; margin: 0; } dl { margin: 20px; } dd { margin: 2px 20px; } dl, dd { font-size: 14px; } div#nav table td { vertical-align: top; } table.dir th { text-align: left; } table.dir td { word-wrap: break-word; vertical-align: top; } div#page.wide table.dir td.name { white-space: nowrap; } .alert { color: #AA0000; } div#heading { float: left; margin: 0 0 10px 0; padding: 21px 0; font-size: 20px; font-weight: normal; } div#heading a { color: #222; text-decoration: none; } div#topbar { background: #E0EBF5; height: 64px; overflow: hidden; } body { text-align: center; } div#page { width: 100%; } div#page > .container, div#topbar > .container { text-align: left; margin-left: auto; margin-right: auto; padding: 0 20px; width: 900px; } div#page.wide > .container, div#topbar.wide > .container { width: auto; } div#plusone { float: right; clear: right; margin-top: 5px; } div#footer { text-align: center; color: #666; font-size: 14px; margin: 40px 0; } div#menu > a, div#menu > input, div#learn .buttons a, div.play .buttons a, div#blog .read a { padding: 10px; text-decoration: none; font-size: 16px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } div#playground .buttons a, div#menu > a, div#menu > input { border: 1px solid #375EAB; } div#playground .buttons a, div#menu > a { color: white; background: #375EAB; } #playgroundButton.active { background: white; color: #375EAB; } a#start, div#learn .buttons a, div.play .buttons a, div#blog .read a { color: #222; border: 1px solid #375EAB; background: #E0EBF5; } .download { width: 150px; } div#menu { float: right; min-width: 590px; padding: 10px 0; text-align: right; } div#menu > a { margin-right: 5px; margin-bottom: 10px; padding: 10px; } div#menu > input { position: relative; top: 1px; width: 60px; background: white; color: #222; } div#menu > input.inactive { color: #999; } div.left { float: left; clear: left; } div.right { float: right; clear: right; } div.left, div.right { width: 415px; } div#learn, div#about { padding-top: 20px; } div#learn h2, div#about { margin: 0; } div#about { font-size: 20px; } div#about { height: 96px; } div#gopher { background: url(/doc/gopher/frontpage.png) no-repeat; background-position: center top; height: 155px; } a#start { display: block; padding: 10px; text-align: center; text-decoration: none; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } a#start .big { display: block; font-weight: bold; font-size: 20px; } a#start .desc { display: block; font-size: 14px; font-weight: normal; margin-top: 5px; } div#learn .popout { float: right; display: block; cursor: pointer; font-size: 12px; background: url(/doc/share.png) no-repeat; background-position: right top; padding: 5px 27px; } div#learn pre, div#learn textarea { padding: 0; margin: 0; font-family: Menlo, monospace; font-size: 14px; } div#learn .input { padding: 10px; margin-top: 10px; height: 150px; -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; } div#learn .input textarea { width: 100%; height: 100%; border: none; outline: none; resize: none; } div#learn .output { border-top: none !important; padding: 10px; height: 59px; overflow: auto; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -moz-border-radius-bottomleft: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; } div#learn .output pre { padding: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } div#learn .input, div#learn .input textarea, div#learn .output, div#learn .output pre { background: #FFFFD8; } div#learn .input, div#learn .output { border: 1px solid #375EAB; } div#learn .buttons { float: right; padding: 20px 0 10px 0; text-align: right; } div#learn .buttons a { height: 16px; margin-left: 5px; padding: 10px; } div#learn .toys { margin-top: 8px; } div#learn .toys select { border: 1px solid #375EAB; margin: 0; } div#learn .output .exit { display: none; } div#blog, div#video { margin-top: 40px; } div#blog > a, div#blog > div, div#blog > h2, div#video > a, div#video > div, div#video > h2 { margin-bottom: 10px; } div#blog .title, div#video .title { display: block; font-size: 20px; } div#blog .when { color: #666; font-size: 14px; } div#blog .read { text-align: right; } .toggleButton { cursor: pointer; } .toggle .collapsed { display: block; } .toggle .expanded { display: none; } .toggleVisible .collapsed { display: none; } .toggleVisible .expanded { display: block; } table.codetable { margin-left: auto; margin-right: auto; border-style: none; } table.codetable td { padding-right: 10px; } hr { border-style: none; border-top: 1px solid black; } img.gopher { float: right; margin-left: 10px; margin-bottom: 10px; z-index: -1; } .pkgGopher { text-align: right; } .pkgGopher .gopher { float: none; position: relative; top: -40px; margin-bottom: -120px; } h2 { clear: right; } /* example and drop-down playground */ div.play { padding: 0 20px 40px 20px; } div.play pre, div.play textarea, div.play .lines { padding: 0; margin: 0; font-family: Menlo, monospace; font-size: 14px; } div.play .input { padding: 10px; margin-top: 10px; -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; overflow: hidden; } div.play .input textarea { width: 100%; height: 100%; border: none; outline: none; resize: none; overflow: hidden; } div#playground .input textarea { overflow: auto; resize: auto; } div.play .output { border-top: none !important; padding: 10px; max-height: 200px; overflow: auto; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -moz-border-radius-bottomleft: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; } div.play .output pre { padding: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } div.play .input, div.play .input textarea, div.play .output, div.play .output pre { background: #FFFFD8; } div.play .input, div.play .output { border: 1px solid #375EAB; } div.play .buttons { float: right; padding: 20px 0 10px 0; text-align: right; } div.play .buttons a { height: 16px; margin-left: 5px; padding: 10px; cursor: pointer; } .output .stderr { color: #933; } .output .system { color: #999; } /* drop-down playground */ #playgroundButton, div#playground { /* start hidden; revealed by javascript */ display: none; } div#playground { position: absolute; top: 63px; right: 20px; padding: 0 10px 10px 10px; z-index: 1; text-align: left; background: #E0EBF5; border: 1px solid #B0BBC5; border-top: none; -webkit-border-bottom-left-radius: 5px; -webkit-border-bottom-right-radius: 5px; -moz-border-radius-bottomleft: 5px; -moz-border-radius-bottomright: 5px; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } div#playground .code { width: 520px; height: 200px; } div#playground .output { height: 100px; } /* Inline runnable snippets (play.js/initPlayground) */ #content .code pre, #content .playground pre, #content .output pre { margin: 0; padding: 0; background: none; border: none; outline: 0px solid transparent; overflow: auto; } #content .playground .number, #content .code .number { color: #999; } #content .code, #content .playground, #content .output { width: auto; margin: 20px; padding: 10px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } #content .code, #content .playground { background: #e9e9e9; } #content .output { background: #202020; } #content .output .stdout, #content .output pre { color: #e6e6e6; } #content .output .stderr, #content .output .error { color: rgb(244, 74, 63); } #content .output .system, #content .output .exit { color: rgb(255, 209, 77) } #content .buttons { position: relative; float: right; top: -50px; right: 30px; } #content .output .buttons { top: -60px; right: 0; height: 0; } #content .buttons .kill { display: none; visibility: hidden; } ./godoc/static/playground.js0000644000014500017510000002560312246613010015605 0ustar michaelstaff// Copyright 2012 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. /* In the absence of any formal way to specify interfaces in JavaScript, here's a skeleton implementation of a playground transport. function Transport() { // Set up any transport state (eg, make a websocket connnection). return { Run: function(body, output, options) { // Compile and run the program 'body' with 'options'. // Call the 'output' callback to display program output. return { Kill: function() { // Kill the running program. } }; } }; } // The output callback is called multiple times, and each time it is // passed an object of this form. var write = { Kind: 'string', // 'start', 'stdout', 'stderr', 'end' Body: 'string' // content of write or end status message } // The first call must be of Kind 'start' with no body. // Subsequent calls may be of Kind 'stdout' or 'stderr' // and must have a non-null Body string. // The final call should be of Kind 'end' with an optional // Body string, signifying a failure ("killed", for example). // The output callback must be of this form. // See PlaygroundOutput (below) for an implementation. function outputCallback(write) { } */ function HTTPTransport() { 'use strict'; // TODO(adg): support stderr function playback(output, events) { var timeout; output({Kind: 'start'}); function next() { if (events.length === 0) { output({Kind: 'end'}); return; } var e = events.shift(); if (e.Delay === 0) { output({Kind: 'stdout', Body: e.Message}); next(); return; } timeout = setTimeout(function() { output({Kind: 'stdout', Body: e.Message}); next(); }, e.Delay / 1000000); } next(); return { Stop: function() { clearTimeout(timeout); } } } function error(output, msg) { output({Kind: 'start'}); output({Kind: 'stderr', Body: msg}); output({Kind: 'end'}); } var seq = 0; return { Run: function(body, output, options) { seq++; var cur = seq; var playing; $.ajax('/compile', { type: 'POST', data: {'version': 2, 'body': body}, dataType: 'json', success: function(data) { if (seq != cur) return; if (!data) return; if (playing != null) playing.Stop(); if (data.Errors) { error(output, data.Errors); return; } playing = playback(output, data.Events); }, error: function() { error(output, 'Error communicating with remote server.'); } }); return { Kill: function() { if (playing != null) playing.Stop(); output({Kind: 'end', Body: 'killed'}); } }; } }; } function SocketTransport() { 'use strict'; var id = 0; var outputs = {}; var started = {}; var websocket = new WebSocket('ws://' + window.location.host + '/socket'); websocket.onclose = function() { console.log('websocket connection closed'); } websocket.onmessage = function(e) { var m = JSON.parse(e.data); var output = outputs[m.Id]; if (output === null) return; if (!started[m.Id]) { output({Kind: 'start'}); started[m.Id] = true; } output({Kind: m.Kind, Body: m.Body}); } function send(m) { websocket.send(JSON.stringify(m)); } return { Run: function(body, output, options) { var thisID = id+''; id++; outputs[thisID] = output; send({Id: thisID, Kind: 'run', Body: body, Options: options}); return { Kill: function() { send({Id: thisID, Kind: 'kill'}); } }; } }; } function PlaygroundOutput(el) { 'use strict'; return function(write) { if (write.Kind == 'start') { el.innerHTML = ''; return; } var cl = 'system'; if (write.Kind == 'stdout' || write.Kind == 'stderr') cl = write.Kind; var m = write.Body; if (write.Kind == 'end') m = '\nProgram exited' + (m?(': '+m):'.'); if (m.indexOf('IMAGE:') === 0) { // TODO(adg): buffer all writes before creating image var url = 'data:image/png;base64,' + m.substr(6); var img = document.createElement('img'); img.src = url; el.appendChild(img); return; } // ^L clears the screen. var s = m.split('\x0c'); if (s.length > 1) { el.innerHTML = ''; m = s.pop(); } m = m.replace(/&/g, '&'); m = m.replace(//g, '>'); var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight; var span = document.createElement('span'); span.className = cl; span.innerHTML = m; el.appendChild(span); if (needScroll) el.scrollTop = el.scrollHeight - el.offsetHeight; } } (function() { function lineHighlight(error) { var regex = /prog.go:([0-9]+)/g; var r = regex.exec(error); while (r) { $(".lines div").eq(r[1]-1).addClass("lineerror"); r = regex.exec(error); } } function highlightOutput(wrappedOutput) { return function(write) { if (write.Body) lineHighlight(write.Body); wrappedOutput(write); } } function lineClear() { $(".lineerror").removeClass("lineerror"); } // opts is an object with these keys // codeEl - code editor element // outputEl - program output element // runEl - run button element // fmtEl - fmt button element (optional) // shareEl - share button element (optional) // shareURLEl - share URL text input element (optional) // shareRedirect - base URL to redirect to on share (optional) // toysEl - toys select element (optional) // enableHistory - enable using HTML5 history API (optional) // transport - playground transport to use (default is HTTPTransport) function playground(opts) { var code = $(opts.codeEl); var transport = opts['transport'] || new HTTPTransport(); var running; // autoindent helpers. function insertTabs(n) { // find the selection start and end var start = code[0].selectionStart; var end = code[0].selectionEnd; // split the textarea content into two, and insert n tabs var v = code[0].value; var u = v.substr(0, start); for (var i=0; i 0) { curpos--; if (el.value[curpos] == "\t") { tabs++; } else if (tabs > 0 || el.value[curpos] == "\n") { break; } } setTimeout(function() { insertTabs(tabs); }, 1); } function keyHandler(e) { if (e.keyCode == 9) { // tab insertTabs(1); e.preventDefault(); return false; } if (e.keyCode == 13) { // enter if (e.shiftKey) { // +shift run(); e.preventDefault(); return false; } else { autoindent(e.target); } } return true; } code.unbind('keydown').bind('keydown', keyHandler); var outdiv = $(opts.outputEl).empty(); var output = $('
').appendTo(outdiv);
  
    function body() {
      return $(opts.codeEl).val();
    }
    function setBody(text) {
      $(opts.codeEl).val(text);
    }
    function origin(href) {
      return (""+href).split("/").slice(0, 3).join("/");
    }
  
    var pushedEmpty = (window.location.pathname == "/");
    function inputChanged() {
      if (pushedEmpty) {
        return;
      }
      pushedEmpty = true;
      $(opts.shareURLEl).hide();
      window.history.pushState(null, "", "/");
    }
    function popState(e) {
      if (e === null) {
        return;
      }
      if (e && e.state && e.state.code) {
        setBody(e.state.code);
      }
    }
    var rewriteHistory = false;
    if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
      rewriteHistory = true;
      code[0].addEventListener('input', inputChanged);
      window.addEventListener('popstate', popState);
    }

    function setError(error) {
      if (running) running.Kill();
      lineClear();
      lineHighlight(error);
      output.empty().addClass("error").text(error);
    }
    function loading() {
      lineClear();
      if (running) running.Kill();
      output.removeClass("error").text('Waiting for remote server...');
    }
    function run() {
      loading();
      running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
    }
    function fmt() {
      loading();
      $.ajax("/fmt", {
        data: {"body": body()},
        type: "POST",
        dataType: "json",
        success: function(data) {
          if (data.Error) {
            setError(data.Error);
          } else {
            setBody(data.Body);
            setError("");
          }
        }
      });
    }

    $(opts.runEl).click(run);
    $(opts.fmtEl).click(fmt);
  
    if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
      var shareURL;
      if (opts.shareURLEl) {
        shareURL = $(opts.shareURLEl).hide();
      }
      var sharing = false;
      $(opts.shareEl).click(function() {
        if (sharing) return;
        sharing = true;
        var sharingData = body();
        $.ajax("/share", {
          processData: false,
          data: sharingData,
          type: "POST",
          complete: function(xhr) {
            sharing = false;
            if (xhr.status != 200) {
              alert("Server error; try again.");
              return;
            }
            if (opts.shareRedirect) {
              window.location = opts.shareRedirect + xhr.responseText;
            }
            if (shareURL) {
              var path = "/p/" + xhr.responseText;
              var url = origin(window.location) + path;
              shareURL.show().val(url).focus().select();
  
              if (rewriteHistory) {
                var historyData = {"code": sharingData};
                window.history.pushState(historyData, "", path);
                pushedEmpty = false;
              }
            }
          }
        });
      });
    }
  
    if (opts.toysEl !== null) {
      $(opts.toysEl).bind('change', function() {
        var toy = $(this).val();
        $.ajax("/doc/play/"+toy, {
          processData: false,
          type: "GET",
          complete: function(xhr) {
            if (xhr.status != 200) {
              alert("Server error; try again.");
              return;
            }
            setBody(xhr.responseText);
          }
        });
      });
    }
  }

  window.playground = playground;
})();
./godoc/static/opensearch.xml0000644000014500017510000000074712246613010015736 0ustar  michaelstaff

  godoc
  The Go Programming Language
  go golang
  
  
  /favicon.ico
  UTF-8
  UTF-8

./godoc/tab.go0000644000014500017510000000267312246613010012673 0ustar  michaelstaff// TODO(bradfitz,adg): move to util

package godoc

import "io"

var spaces = []byte("                                ") // 32 spaces seems like a good number

const (
	indenting = iota
	collecting
)

// A tconv is an io.Writer filter for converting leading tabs into spaces.
type tconv struct {
	output io.Writer
	state  int // indenting or collecting
	indent int // valid if state == indenting
	p      *Presentation
}

func (p *tconv) writeIndent() (err error) {
	i := p.indent
	for i >= len(spaces) {
		i -= len(spaces)
		if _, err = p.output.Write(spaces); err != nil {
			return
		}
	}
	// i < len(spaces)
	if i > 0 {
		_, err = p.output.Write(spaces[0:i])
	}
	return
}

func (p *tconv) Write(data []byte) (n int, err error) {
	if len(data) == 0 {
		return
	}
	pos := 0 // valid if p.state == collecting
	var b byte
	for n, b = range data {
		switch p.state {
		case indenting:
			switch b {
			case '\t':
				p.indent += p.p.TabWidth
			case '\n':
				p.indent = 0
				if _, err = p.output.Write(data[n : n+1]); err != nil {
					return
				}
			case ' ':
				p.indent++
			default:
				p.state = collecting
				pos = n
				if err = p.writeIndent(); err != nil {
					return
				}
			}
		case collecting:
			if b == '\n' {
				p.state = indenting
				p.indent = 0
				if _, err = p.output.Write(data[pos : n+1]); err != nil {
					return
				}
			}
		}
	}
	n = len(data)
	if pos < n && p.state == collecting {
		_, err = p.output.Write(data[pos:])
	}
	return
}
./godoc/template.go0000644000014500017510000001270112246613010013731 0ustar  michaelstaff// 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.

// Template support for writing HTML documents.
// Documents that include Template: true in their
// metadata are executed as input to text/template.
//
// This file defines functions for those templates to invoke.

// The template uses the function "code" to inject program
// source into the output by extracting code from files and
// injecting them as HTML-escaped 
 blocks.
//
// The syntax is simple: 1, 2, or 3 space-separated arguments:
//
// Whole file:
//	{{code "foo.go"}}
// One line (here the signature of main):
//	{{code "foo.go" `/^func.main/`}}
// Block of text, determined by start and end (here the body of main):
//	{{code "foo.go" `/^func.main/` `/^}/`
//
// Patterns can be `/regular expression/`, a decimal number, or "$"
// to signify the end of the file. In multi-line matches,
// lines that end with the four characters
//	OMIT
// are omitted from the output, making it easy to provide marker
// lines in the input that will not appear in the output but are easy
// to identify by pattern.

package godoc

import (
	"bytes"
	"fmt"
	"log"
	"regexp"
	"strings"

	"code.google.com/p/go.tools/godoc/vfs"
)

// Functions in this file panic on error, but the panic is recovered
// to an error by 'code'.

// contents reads and returns the content of the named file
// (from the virtual file system, so for example /doc refers to $GOROOT/doc).
func (c *Corpus) contents(name string) string {
	file, err := vfs.ReadFile(c.fs, name)
	if err != nil {
		log.Panic(err)
	}
	return string(file)
}

// stringFor returns a textual representation of the arg, formatted according to its nature.
func stringFor(arg interface{}) string {
	switch arg := arg.(type) {
	case int:
		return fmt.Sprintf("%d", arg)
	case string:
		if len(arg) > 2 && arg[0] == '/' && arg[len(arg)-1] == '/' {
			return fmt.Sprintf("%#q", arg)
		}
		return fmt.Sprintf("%q", arg)
	default:
		log.Panicf("unrecognized argument: %v type %T", arg, arg)
	}
	return ""
}

func (p *Presentation) code(file string, arg ...interface{}) (s string, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%v", r)
		}
	}()

	text := p.Corpus.contents(file)
	var command string
	switch len(arg) {
	case 0:
		// text is already whole file.
		command = fmt.Sprintf("code %q", file)
	case 1:
		command = fmt.Sprintf("code %q %s", file, stringFor(arg[0]))
		text = p.Corpus.oneLine(file, text, arg[0])
	case 2:
		command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1]))
		text = p.Corpus.multipleLines(file, text, arg[0], arg[1])
	default:
		return "", fmt.Errorf("incorrect code invocation: code %q %q", file, arg)
	}
	// Trim spaces from output.
	text = strings.Trim(text, "\n")
	// Replace tabs by spaces, which work better in HTML.
	text = strings.Replace(text, "\t", "    ", -1)
	var buf bytes.Buffer
	// HTML-escape text and syntax-color comments like elsewhere.
	FormatText(&buf, []byte(text), -1, true, "", nil)
	// Include the command as a comment.
	text = fmt.Sprintf("
%s
", command, buf.Bytes()) return text, nil } // parseArg returns the integer or string value of the argument and tells which it is. func parseArg(arg interface{}, file string, max int) (ival int, sval string, isInt bool) { switch n := arg.(type) { case int: if n <= 0 || n > max { log.Panicf("%q:%d is out of range", file, n) } return n, "", true case string: return 0, n, false } log.Panicf("unrecognized argument %v type %T", arg, arg) return } // oneLine returns the single line generated by a two-argument code invocation. func (c *Corpus) oneLine(file, text string, arg interface{}) string { lines := strings.SplitAfter(c.contents(file), "\n") line, pattern, isInt := parseArg(arg, file, len(lines)) if isInt { return lines[line-1] } return lines[match(file, 0, lines, pattern)-1] } // multipleLines returns the text generated by a three-argument code invocation. func (c *Corpus) multipleLines(file, text string, arg1, arg2 interface{}) string { lines := strings.SplitAfter(c.contents(file), "\n") line1, pattern1, isInt1 := parseArg(arg1, file, len(lines)) line2, pattern2, isInt2 := parseArg(arg2, file, len(lines)) if !isInt1 { line1 = match(file, 0, lines, pattern1) } if !isInt2 { line2 = match(file, line1, lines, pattern2) } else if line2 < line1 { log.Panicf("lines out of order for %q: %d %d", text, line1, line2) } for k := line1 - 1; k < line2; k++ { if strings.HasSuffix(lines[k], "OMIT\n") { lines[k] = "" } } return strings.Join(lines[line1-1:line2], "") } // match identifies the input line that matches the pattern in a code invocation. // If start>0, match lines starting there rather than at the beginning. // The return value is 1-indexed. func match(file string, start int, lines []string, pattern string) int { // $ matches the end of the file. if pattern == "$" { if len(lines) == 0 { log.Panicf("%q: empty file", file) } return len(lines) } // /regexp/ matches the line that matches the regexp. if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' { re, err := regexp.Compile(pattern[1 : len(pattern)-1]) if err != nil { log.Panic(err) } for i := start; i < len(lines); i++ { if re.MatchString(lines[i]) { return i + 1 } } log.Panicf("%s: no match for %#q", file, pattern) } log.Panicf("unrecognized pattern: %q", pattern) return 0 } ./godoc/godoc.go0000644000014500017510000003236712246613010013223 0ustar michaelstaff// 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 godoc is a work-in-progress (2013-07-17) package to // begin splitting up the godoc binary into multiple pieces. // // This package comment will evolve over time as this package splits // into smaller pieces. package godoc import ( "bytes" "fmt" "go/ast" "go/doc" "go/format" "go/printer" "go/token" "io" "log" "os" pathpkg "path" "regexp" "strings" "text/template" "time" "unicode" "unicode/utf8" ) // Fake relative package path for built-ins. Documentation for all globals // (not just exported ones) will be shown for packages in this directory. const builtinPkgPath = "builtin" // FuncMap defines template functions used in godoc templates. // // Convention: template function names ending in "_html" or "_url" produce // HTML- or URL-escaped strings; all other function results may // require explicit escaping in the template. func (p *Presentation) FuncMap() template.FuncMap { p.initFuncMapOnce.Do(p.initFuncMap) return p.funcMap } func (p *Presentation) TemplateFuncs() template.FuncMap { p.initFuncMapOnce.Do(p.initFuncMap) return p.templateFuncs } func (p *Presentation) initFuncMap() { if p.Corpus == nil { panic("nil Presentation.Corpus") } p.templateFuncs = template.FuncMap{ "code": p.code, } p.funcMap = template.FuncMap{ // various helpers "filename": filenameFunc, "repeat": strings.Repeat, // access to FileInfos (directory listings) "fileInfoName": fileInfoNameFunc, "fileInfoTime": fileInfoTimeFunc, // access to search result information "infoKind_html": infoKind_htmlFunc, "infoLine": p.infoLineFunc, "infoSnippet_html": p.infoSnippet_htmlFunc, // formatting of AST nodes "node": p.nodeFunc, "node_html": p.node_htmlFunc, "comment_html": comment_htmlFunc, "comment_text": comment_textFunc, // support for URL attributes "pkgLink": pkgLinkFunc, "srcLink": srcLinkFunc, "posLink_url": newPosLink_urlFunc(srcPosLinkFunc), "docLink": docLinkFunc, // formatting of Examples "example_html": p.example_htmlFunc, "example_text": p.example_textFunc, "example_name": p.example_nameFunc, "example_suffix": p.example_suffixFunc, // formatting of Notes "noteTitle": noteTitle, } if p.URLForSrc != nil { p.funcMap["srcLink"] = p.URLForSrc } if p.URLForSrcPos != nil { p.funcMap["posLink_url"] = newPosLink_urlFunc(p.URLForSrcPos) } } func filenameFunc(path string) string { _, localname := pathpkg.Split(path) return localname } func fileInfoNameFunc(fi os.FileInfo) string { name := fi.Name() if fi.IsDir() { name += "/" } return name } func fileInfoTimeFunc(fi os.FileInfo) string { if t := fi.ModTime(); t.Unix() != 0 { return t.Local().String() } return "" // don't return epoch if time is obviously not set } // The strings in infoKinds must be properly html-escaped. var infoKinds = [nKinds]string{ PackageClause: "package clause", ImportDecl: "import decl", ConstDecl: "const decl", TypeDecl: "type decl", VarDecl: "var decl", FuncDecl: "func decl", MethodDecl: "method decl", Use: "use", } func infoKind_htmlFunc(info SpotInfo) string { return infoKinds[info.Kind()] // infoKind entries are html-escaped } func (p *Presentation) infoLineFunc(info SpotInfo) int { line := info.Lori() if info.IsIndex() { index, _ := p.Corpus.searchIndex.Get() if index != nil { line = index.(*Index).Snippet(line).Line } else { // no line information available because // we don't have an index - this should // never happen; be conservative and don't // crash line = 0 } } return line } func (p *Presentation) infoSnippet_htmlFunc(info SpotInfo) string { if info.IsIndex() { index, _ := p.Corpus.searchIndex.Get() // Snippet.Text was HTML-escaped when it was generated return index.(*Index).Snippet(info.Lori()).Text } return `no snippet text available` } func (p *Presentation) nodeFunc(info *PageInfo, node interface{}) string { var buf bytes.Buffer p.writeNode(&buf, info.FSet, node) return buf.String() } func (p *Presentation) node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string { var buf1 bytes.Buffer p.writeNode(&buf1, info.FSet, node) var buf2 bytes.Buffer if n, _ := node.(ast.Node); n != nil && linkify && p.DeclLinks { LinkifyText(&buf2, buf1.Bytes(), n) } else { FormatText(&buf2, buf1.Bytes(), -1, true, "", nil) } return buf2.String() } func comment_htmlFunc(comment string) string { var buf bytes.Buffer // TODO(gri) Provide list of words (e.g. function parameters) // to be emphasized by ToHTML. doc.ToHTML(&buf, comment, nil) // does html-escaping return buf.String() } // punchCardWidth is the number of columns of fixed-width // characters to assume when wrapping text. Very few people // use terminals or cards smaller than 80 characters, so 80 it is. // We do not try to sniff the environment or the tty to adapt to // the situation; instead, by using a constant we make sure that // godoc always produces the same output regardless of context, // a consistency that is lost otherwise. For example, if we sniffed // the environment or tty, then http://golang.org/pkg/math/?m=text // would depend on the width of the terminal where godoc started, // which is clearly bogus. More generally, the Unix tools that behave // differently when writing to a tty than when writing to a file have // a history of causing confusion (compare `ls` and `ls | cat`), and we // want to avoid that mistake here. const punchCardWidth = 80 func comment_textFunc(comment, indent, preIndent string) string { var buf bytes.Buffer doc.ToText(&buf, comment, indent, preIndent, punchCardWidth-2*len(indent)) return buf.String() } type PageInfo struct { Dirname string // directory containing the package Err error // error or nil // package info FSet *token.FileSet // nil if no package documentation PDoc *doc.Package // nil if no package documentation Examples []*doc.Example // nil if no example code Notes map[string][]*doc.Note // nil if no package Notes PAst *ast.File // nil if no AST with package exports IsMain bool // true for package main // directory info Dirs *DirList // nil if no directory information DirTime time.Time // directory time stamp DirFlat bool // if set, show directory in a flat (non-indented) manner } func (info *PageInfo) IsEmpty() bool { return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil } func pkgLinkFunc(path string) string { relpath := path[1:] // because of the irregular mapping under goroot // we need to correct certain relative paths relpath = strings.TrimPrefix(relpath, "src/pkg/") return "pkg/" + relpath // remove trailing '/' for relative URL } func newPosLink_urlFunc(srcPosLinkFunc func(s string, line, low, high int) string) func(info *PageInfo, n interface{}) string { // n must be an ast.Node or a *doc.Note return func(info *PageInfo, n interface{}) string { var pos, end token.Pos switch n := n.(type) { case ast.Node: pos = n.Pos() end = n.End() case *doc.Note: pos = n.Pos end = n.End default: panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n)) } var relpath string var line int var low, high int // selection offset range if pos.IsValid() { p := info.FSet.Position(pos) relpath = p.Filename line = p.Line low = p.Offset } if end.IsValid() { high = info.FSet.Position(end).Offset } return srcPosLinkFunc(relpath, line, low, high) } } func srcPosLinkFunc(s string, line, low, high int) string { var buf bytes.Buffer template.HTMLEscape(&buf, []byte(s)) // selection ranges are of form "s=low:high" if low < high { fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping // if we have a selection, position the page // such that the selection is a bit below the top line -= 10 if line < 1 { line = 1 } } // line id's in html-printed source are of the // form "L%d" where %d stands for the line number if line > 0 { fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping } return buf.String() } func srcLinkFunc(s string) string { return pathpkg.Clean("/" + s) } func docLinkFunc(s string, ident string) string { s = strings.TrimPrefix(s, "/src") return pathpkg.Clean("/"+s) + "/#" + ident } func (p *Presentation) example_textFunc(info *PageInfo, funcName, indent string) string { if !p.ShowExamples { return "" } var buf bytes.Buffer first := true for _, eg := range info.Examples { name := stripExampleSuffix(eg.Name) if name != funcName { continue } if !first { buf.WriteString("\n") } first = false // print code cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} var buf1 bytes.Buffer p.writeNode(&buf1, info.FSet, cnode) code := buf1.String() // Additional formatting if this is a function body. if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { // remove surrounding braces code = code[1 : n-1] // unindent code = strings.Replace(code, "\n ", "\n", -1) } code = strings.Trim(code, "\n") code = strings.Replace(code, "\n", "\n\t", -1) buf.WriteString(indent) buf.WriteString("Example:\n\t") buf.WriteString(code) buf.WriteString("\n") } return buf.String() } func (p *Presentation) example_htmlFunc(info *PageInfo, funcName string) string { var buf bytes.Buffer for _, eg := range info.Examples { name := stripExampleSuffix(eg.Name) if name != funcName { continue } // print code cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} code := p.node_htmlFunc(info, cnode, true) out := eg.Output wholeFile := true // Additional formatting if this is a function body. if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { wholeFile = false // remove surrounding braces code = code[1 : n-1] // unindent code = strings.Replace(code, "\n ", "\n", -1) // remove output comment if loc := exampleOutputRx.FindStringIndex(code); loc != nil { code = strings.TrimSpace(code[:loc[0]]) } } // Write out the playground code in standard Go style // (use tabs, no comment highlight, etc). play := "" if eg.Play != nil && p.ShowPlayground { var buf bytes.Buffer if err := format.Node(&buf, info.FSet, eg.Play); err != nil { log.Print(err) } else { play = buf.String() } } // Drop output, as the output comment will appear in the code. if wholeFile && play == "" { out = "" } if p.ExampleHTML == nil { out = "" return "" } err := p.ExampleHTML.Execute(&buf, struct { Name, Doc, Code, Play, Output string }{eg.Name, eg.Doc, code, play, out}) if err != nil { log.Print(err) } } return buf.String() } // example_nameFunc takes an example function name and returns its display // name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)". func (p *Presentation) example_nameFunc(s string) string { name, suffix := splitExampleName(s) // replace _ with . for method names name = strings.Replace(name, "_", ".", 1) // use "Package" if no name provided if name == "" { name = "Package" } return name + suffix } // example_suffixFunc takes an example function name and returns its suffix in // parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)". func (p *Presentation) example_suffixFunc(name string) string { _, suffix := splitExampleName(name) return suffix } func noteTitle(note string) string { return strings.Title(strings.ToLower(note)) } func startsWithUppercase(s string) bool { r, _ := utf8.DecodeRuneInString(s) return unicode.IsUpper(r) } var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`) // stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name // while keeping uppercase Braz in Foo_Braz. func stripExampleSuffix(name string) string { if i := strings.LastIndex(name, "_"); i != -1 { if i < len(name)-1 && !startsWithUppercase(name[i+1:]) { name = name[:i] } } return name } func splitExampleName(s string) (name, suffix string) { i := strings.LastIndex(s, "_") if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) { name = s[:i] suffix = " (" + strings.Title(s[i+1:]) + ")" return } name = s return } // Write an AST node to w. func (p *Presentation) writeNode(w io.Writer, fset *token.FileSet, x interface{}) { // convert trailing tabs into spaces using a tconv filter // to ensure a good outcome in most browsers (there may still // be tabs in comments and strings, but converting those into // the right number of spaces is much harder) // // TODO(gri) rethink printer flags - perhaps tconv can be eliminated // with an another printer mode (which is more efficiently // implemented in the printer than here with another layer) mode := printer.TabIndent | printer.UseSpaces err := (&printer.Config{Mode: mode, Tabwidth: p.TabWidth}).Fprint(&tconv{p: p, output: w}, fset, x) if err != nil { log.Print(err) } } // WriteNote writes x to w. func (p *Presentation) WriteNode(w io.Writer, fset *token.FileSet, x interface{}) { p.writeNode(w, fset, x) } ./godoc/parser.go0000644000014500017510000000175012246613010013414 0ustar michaelstaff// 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 contains support functions for parsing .go files // accessed via godoc's file system fs. package godoc import ( "go/ast" "go/parser" "go/token" pathpkg "path" "code.google.com/p/go.tools/godoc/vfs" ) func (c *Corpus) parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) { src, err := vfs.ReadFile(c.fs, filename) if err != nil { return nil, err } return parser.ParseFile(fset, filename, src, mode) } func (c *Corpus) parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) { files := make(map[string]*ast.File) for _, f := range localnames { absname := pathpkg.Join(abspath, f) file, err := c.parseFile(fset, absname, parser.ParseComments) if err != nil { return nil, err } files[absname] = file } return files, nil } ./godoc/corpus.go0000644000014500017510000001137312246613010013435 0ustar michaelstaff// 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 godoc import ( "errors" pathpkg "path" "time" "code.google.com/p/go.tools/godoc/util" "code.google.com/p/go.tools/godoc/vfs" ) // A Corpus holds all the state related to serving and indexing a // collection of Go code. // // Construct a new Corpus with NewCorpus, then modify options, // then call its Init method. type Corpus struct { fs vfs.FileSystem // Verbose logging. Verbose bool // IndexEnabled controls whether indexing is enabled. IndexEnabled bool // IndexFiles specifies a glob pattern specifying index files. // If not empty, the index is read from these files in sorted // order. IndexFiles string // IndexThrottle specifies the indexing throttle value // between 0.0 and 1.0. At 0.0, the indexer always sleeps. // At 1.0, the indexer never sleeps. Because 0.0 is useless // and redundant with setting IndexEnabled to false, the // zero value for IndexThrottle means 0.9. IndexThrottle float64 // IndexInterval specifies the time to sleep between reindexing // all the sources. // If zero, a default is used. If negative, the index is only // built once. IndexInterval time.Duration // IndexDocs enables indexing of Go documentation. // This will produce search results for exported types, functions, // methods, variables, and constants, and will link to the godoc // documentation for those identifiers. IndexDocs bool // IndexGoCode enables indexing of Go source code. // This will produce search results for internal and external identifiers // and will link to both declarations and uses of those identifiers in // source code. IndexGoCode bool // IndexFullText enables full-text indexing. // This will provide search results for any matching text in any file that // is indexed, including non-Go files (see whitelisted in index.go). // Regexp searching is supported via full-text indexing. IndexFullText bool // MaxResults optionally specifies the maximum results for indexing. MaxResults int // SummarizePackage optionally specifies a function to // summarize a package. It exists as an optimization to // avoid reading files to parse package comments. // // If SummarizePackage returns false for ok, the caller // ignores all return values and parses the files in the package // as if SummarizePackage were nil. // // If showList is false, the package is hidden from the // package listing. SummarizePackage func(pkg string) (summary string, showList, ok bool) // IndexDirectory optionally specifies a function to determine // whether the provided directory should be indexed. The dir // will be of the form "/src/cmd/6a", "/doc/play", // "/src/pkg/io", etc. // If nil, all directories are indexed if indexing is enabled. IndexDirectory func(dir string) bool testDir string // TODO(bradfitz,adg): migrate old godoc flag? looks unused. // Send a value on this channel to trigger a metadata refresh. // It is buffered so that if a signal is not lost if sent // during a refresh. refreshMetadataSignal chan bool // file system information fsTree util.RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now) fsModified util.RWValue // timestamp of last call to invalidateIndex docMetadata util.RWValue // mapping from paths to *Metadata // SearchIndex is the search index in use. searchIndex util.RWValue } // NewCorpus returns a new Corpus from a filesystem. // The returned corpus has all indexing enabled and MaxResults set to 1000. // Change or set any options on Corpus before calling the Corpus.Init method. func NewCorpus(fs vfs.FileSystem) *Corpus { c := &Corpus{ fs: fs, refreshMetadataSignal: make(chan bool, 1), MaxResults: 1000, IndexEnabled: true, IndexDocs: true, IndexGoCode: true, IndexFullText: true, } return c } func (c *Corpus) CurrentIndex() (*Index, time.Time) { v, t := c.searchIndex.Get() idx, _ := v.(*Index) return idx, t } func (c *Corpus) FSModifiedTime() time.Time { _, ts := c.fsModified.Get() return ts } // Init initializes Corpus, once options on Corpus are set. // It must be called before any subsequent method calls. func (c *Corpus) Init() error { // TODO(bradfitz): do this in a goroutine because newDirectory might block for a long time? // It used to be sometimes done in a goroutine before, at least in HTTP server mode. if err := c.initFSTree(); err != nil { return err } c.updateMetadata() go c.refreshMetadataLoop() return nil } func (c *Corpus) initFSTree() error { dir := c.newDirectory(pathpkg.Join("/", c.testDir), -1) if dir == nil { return errors.New("godoc: corpus fstree is nil") } c.fsTree.Set(dir) c.invalidateIndex() return nil } ./godoc/page.go0000644000014500017510000000235712246613010013040 0ustar michaelstaff// Copyright 2009 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 godoc import ( "log" "net/http" "runtime" ) // Page describes the contents of the top-level godoc webpage. type Page struct { Title string Tabtitle string Subtitle string Query string Body []byte // filled in by servePage SearchBox bool Playground bool Version string } func (p *Presentation) ServePage(w http.ResponseWriter, page Page) { if page.Tabtitle == "" { page.Tabtitle = page.Title } page.SearchBox = p.Corpus.IndexEnabled page.Playground = p.ShowPlayground page.Version = runtime.Version() if err := p.GodocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed { // Only log if there's an error that's not about writing on HEAD requests. // See Issues 5451 and 5454. log.Printf("GodocHTML.Execute: %s", err) } } func (p *Presentation) ServeError(w http.ResponseWriter, r *http.Request, relpath string, err error) { w.WriteHeader(http.StatusNotFound) p.ServePage(w, Page{ Title: "File " + relpath, Subtitle: relpath, Body: applyTemplate(p.ErrorHTML, "errorHTML", err), // err may contain an absolute path! }) } ./godoc/cmdline_test.go0000644000014500017510000001107712246613010014575 0ustar michaelstaffpackage godoc import ( "bytes" "go/build" "io/ioutil" "os" "path/filepath" "reflect" "regexp" "runtime" "testing" "text/template" "code.google.com/p/go.tools/godoc/vfs" "code.google.com/p/go.tools/godoc/vfs/mapfs" ) // setupGoroot creates temporary directory to act as GOROOT when running tests // that depend upon the build package. It updates build.Default to point to the // new GOROOT. // It returns a function that can be called to reset build.Default and remove // the temporary directory. func setupGoroot(t *testing.T) (cleanup func()) { var stdLib = map[string]string{ "src/pkg/fmt/fmt.go": `// Package fmt implements formatted I/O. package fmt type Stringer interface { String() string } `, } goroot, err := ioutil.TempDir("", "cmdline_test") if err != nil { t.Fatal(err) } origContext := build.Default build.Default = build.Context{ GOROOT: goroot, Compiler: "gc", } for relname, contents := range stdLib { name := filepath.Join(goroot, relname) if err := os.MkdirAll(filepath.Dir(name), 0770); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(name, []byte(contents), 0770); err != nil { t.Fatal(err) } } return func() { if err := os.RemoveAll(goroot); err != nil { t.Log(err) } build.Default = origContext } } func TestPaths(t *testing.T) { cleanup := setupGoroot(t) defer cleanup() pres := &Presentation{ pkgHandler: handlerServer{ fsRoot: "/fsroot", }, } fs := make(vfs.NameSpace) absPath := "/foo/fmt" if runtime.GOOS == "windows" { absPath = `c:\foo\fmt` } for _, tc := range []struct { desc string path string expAbs string expRel string }{ { "Absolute path", absPath, "/target", "/target", }, { "Local import", "../foo/fmt", "/target", "/target", }, { "Import", "fmt", "/target", "fmt", }, { "Default", "unknownpkg", "/fsroot/unknownpkg", "unknownpkg", }, } { abs, rel := paths(fs, pres, tc.path) if abs != tc.expAbs || rel != tc.expRel { t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel) } } } func TestMakeRx(t *testing.T) { for _, tc := range []struct { desc string names []string exp string }{ { desc: "empty string", names: []string{""}, exp: `^$`, }, { desc: "simple text", names: []string{"a"}, exp: `^a$`, }, { desc: "two words", names: []string{"foo", "bar"}, exp: `^foo$|^bar$`, }, { desc: "word & non-trivial", names: []string{"foo", `ab?c`}, exp: `^foo$|ab?c`, }, { desc: "bad regexp", names: []string{`(."`}, exp: `(."`, }, } { expRE, expErr := regexp.Compile(tc.exp) if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) { t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr) } } } func TestCommandLine(t *testing.T) { cleanup := setupGoroot(t) defer cleanup() mfs := mapfs.New(map[string]string{ "src/pkg/bar/bar.go": `// Package bar is an example. package bar `, "src/cmd/go/doc.go": `// The go command package main `, "src/cmd/gofmt/doc.go": `// The gofmt command package main `, }) fs := make(vfs.NameSpace) fs.Bind("/", mfs, "/", vfs.BindReplace) c := NewCorpus(fs) p := &Presentation{Corpus: c} p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"} p.pkgHandler = handlerServer{p, c, "/pkg/", "/src/pkg"} p.initFuncMap() p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{with .PAst}}{{node $ .}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{end}}`)) for _, tc := range []struct { desc string args []string exp string err bool }{ { desc: "standard package", args: []string{"fmt"}, exp: "PACKAGE Package fmt implements formatted I/O.\n", }, { desc: "package", args: []string{"bar"}, exp: "PACKAGE Package bar is an example.\n", }, { desc: "source mode", args: []string{"src/bar"}, exp: "// Package bar is an example.\npackage bar\n", }, { desc: "command", args: []string{"go"}, exp: "COMMAND The go command\n", }, { desc: "forced command", args: []string{"cmd/gofmt"}, exp: "COMMAND The gofmt command\n", }, { desc: "bad arg", args: []string{"doesnotexist"}, err: true, }, } { w := new(bytes.Buffer) err := CommandLine(w, fs, p, tc.args) if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) { t.Errorf("%s: CommandLine(%v) = %q,%v; want %q,%v", tc.desc, tc.args, got, err, want, tc.err) } } } ./godoc/pres.go0000644000014500017510000000705112246613010013071 0ustar michaelstaff// 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 godoc import ( "net/http" "regexp" "sync" "text/template" "code.google.com/p/go.tools/godoc/vfs/httpfs" ) // Presentation generates output from a corpus. type Presentation struct { Corpus *Corpus mux *http.ServeMux fileServer http.Handler cmdHandler handlerServer pkgHandler handlerServer DirlistHTML, ErrorHTML, ExampleHTML, GodocHTML, PackageHTML, PackageText, SearchHTML, SearchText, SearchDescXML *template.Template // TabWidth optionally specifies the tab width. TabWidth int ShowTimestamps bool ShowPlayground bool ShowExamples bool DeclLinks bool // SrcMode outputs source code instead of documentation in command-line mode. SrcMode bool // HTMLMode outputs HTML instead of plain text in command-line mode. HTMLMode bool // NotesRx optionally specifies a regexp to match // notes to render in the output. NotesRx *regexp.Regexp // AdjustPageInfoMode optionally specifies a function to // modify the PageInfoMode of a request. The default chosen // value is provided. AdjustPageInfoMode func(req *http.Request, mode PageInfoMode) PageInfoMode // URLForSrc optionally specifies a function that takes a source file and // returns a URL for it. // The source file argument has the form /src/pkg//. URLForSrc func(src string) string // URLForSrcPos optionally specifies a function to create a URL given a // source file, a line from the source file (1-based), and low & high offset // positions (0-based, bytes from beginning of file). Ideally, the returned // URL will be for the specified line of the file, while the high & low // positions will be used to highlight a section of the file. // The source file argument has the form /src/pkg//. URLForSrcPos func(src string, line, low, high int) string initFuncMapOnce sync.Once funcMap template.FuncMap templateFuncs template.FuncMap } // NewPresentation returns a new Presentation from a corpus. func NewPresentation(c *Corpus) *Presentation { if c == nil { panic("nil Corpus") } p := &Presentation{ Corpus: c, mux: http.NewServeMux(), fileServer: http.FileServer(httpfs.New(c.fs)), TabWidth: 4, ShowExamples: true, DeclLinks: true, } p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"} p.pkgHandler = handlerServer{p, c, "/pkg/", "/src/pkg"} p.cmdHandler.registerWithMux(p.mux) p.pkgHandler.registerWithMux(p.mux) p.mux.HandleFunc("/", p.ServeFile) p.mux.HandleFunc("/search", p.HandleSearch) p.mux.HandleFunc("/opensearch.xml", p.serveSearchDesc) return p } func (p *Presentation) FileServer() http.Handler { return p.fileServer } func (p *Presentation) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.mux.ServeHTTP(w, r) } func (p *Presentation) PkgFSRoot() string { return p.pkgHandler.fsRoot } func (p *Presentation) CmdFSRoot() string { return p.cmdHandler.fsRoot } // TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now, // but this doesn't feel right. func (p *Presentation) GetPkgPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo { return p.pkgHandler.GetPageInfo(abspath, relpath, mode) } // TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now, // but this doesn't feel right. func (p *Presentation) GetCmdPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo { return p.cmdHandler.GetPageInfo(abspath, relpath, mode) } ./godoc/cmdline.go0000644000014500017510000001310012246613010013523 0ustar michaelstaff// 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 godoc import ( "bytes" "fmt" "go/ast" "go/build" "go/printer" "io" "log" "os" pathpkg "path" "path/filepath" "regexp" "strings" "code.google.com/p/go.tools/godoc/vfs" ) const ( target = "/target" cmdPrefix = "cmd/" srcPrefix = "src/" toolsPath = "code.google.com/p/go.tools/cmd/" ) // CommandLine returns godoc results to w. // Note that it may add a /target path to fs. func CommandLine(w io.Writer, fs vfs.NameSpace, pres *Presentation, args []string) error { path := args[0] srcMode := pres.SrcMode cmdMode := strings.HasPrefix(path, cmdPrefix) if strings.HasPrefix(path, srcPrefix) { path = strings.TrimPrefix(path, srcPrefix) srcMode = true } var abspath, relpath string if cmdMode { path = strings.TrimPrefix(path, cmdPrefix) } else { abspath, relpath = paths(fs, pres, path) } var mode PageInfoMode if relpath == "builtin" { // the fake built-in package contains unexported identifiers mode = NoFiltering | NoFactoryFuncs } if srcMode { // only filter exports if we don't have explicit command-line filter arguments if len(args) > 1 { mode |= NoFiltering } mode |= ShowSource } // First, try as package unless forced as command. var info *PageInfo if !cmdMode { info = pres.GetPkgPageInfo(abspath, relpath, mode) } // Second, try as command (if the path is not absolute). var cinfo *PageInfo if !filepath.IsAbs(path) { // First try go.tools/cmd. abspath = pathpkg.Join(pres.PkgFSRoot(), toolsPath+path) cinfo = pres.GetCmdPageInfo(abspath, relpath, mode) if cinfo.IsEmpty() { // Then try $GOROOT/cmd. abspath = pathpkg.Join(pres.CmdFSRoot(), path) cinfo = pres.GetCmdPageInfo(abspath, relpath, mode) } } // determine what to use if info == nil || info.IsEmpty() { if cinfo != nil && !cinfo.IsEmpty() { // only cinfo exists - switch to cinfo info = cinfo } } else if cinfo != nil && !cinfo.IsEmpty() { // both info and cinfo exist - use cinfo if info // contains only subdirectory information if info.PAst == nil && info.PDoc == nil { info = cinfo } else { fmt.Fprintf(w, "use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath) } } if info == nil { return fmt.Errorf("%s: no such directory or package", args[0]) } if info.Err != nil { return info.Err } if info.PDoc != nil && info.PDoc.ImportPath == target { // Replace virtual /target with actual argument from command line. info.PDoc.ImportPath = args[0] } // If we have more than one argument, use the remaining arguments for filtering. if len(args) > 1 { filterInfo(pres, args[1:], info) } packageText := pres.PackageText if pres.HTMLMode { packageText = pres.PackageHTML } if err := packageText.Execute(w, info); err != nil { return err } return nil } // paths determines the paths to use. // // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc, // we need to map that path somewhere in the fs name space so that routines // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target" // for this. That is, if we get passed a directory like the above, we map that // directory so that getPageInfo sees it as /target. // Returns the absolute and relative paths. func paths(fs vfs.NameSpace, pres *Presentation, path string) (string, string) { if filepath.IsAbs(path) { fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace) return target, target } if build.IsLocalImport(path) { cwd, _ := os.Getwd() // ignore errors path = filepath.Join(cwd, path) fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace) return target, target } if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { fs.Bind(target, vfs.OS(bp.Dir), "/", vfs.BindReplace) return target, bp.ImportPath } return pathpkg.Join(pres.PkgFSRoot(), path), path } func filterInfo(pres *Presentation, args []string, info *PageInfo) { rx, err := makeRx(args) if err != nil { log.Fatalf("illegal regular expression from %v: %v", args, err) } filter := func(s string) bool { return rx.MatchString(s) } switch { case info.PAst != nil: cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments) ast.FilterFile(info.PAst, filter) // Special case: Don't use templates for printing // so we only get the filtered declarations without // package clause or extra whitespace. for i, d := range info.PAst.Decls { // determine the comments associated with d only comments := cmap.Filter(d).Comments() cn := &printer.CommentedNode{Node: d, Comments: comments} if i > 0 { fmt.Println() } if pres.HTMLMode { var buf bytes.Buffer pres.WriteNode(&buf, info.FSet, cn) FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil) } else { pres.WriteNode(os.Stdout, info.FSet, cn) } fmt.Println() } return case info.PDoc != nil: info.PDoc.Filter(filter) } } // Does s look like a regular expression? func isRegexp(s string) bool { return strings.IndexAny(s, ".(|)*+?^$[]") >= 0 } // Make a regular expression of the form // names[0]|names[1]|...names[len(names)-1]. // Returns an error if the regular expression is illegal. func makeRx(names []string) (*regexp.Regexp, error) { if len(names) == 0 { return nil, fmt.Errorf("no expression provided") } s := "" for i, name := range names { if i > 0 { s += "|" } if isRegexp(name) { s += name } else { s += "^" + name + "$" // must match exactly } } return regexp.Compile(s) } ./godoc/dirtrees.go0000644000014500017510000002033412246613010013740 0ustar michaelstaff// Copyright 2010 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 contains the code dealing with package directory trees. package godoc import ( "bytes" "go/doc" "go/parser" "go/token" "log" "os" pathpkg "path" "strings" ) // Conventional name for directories containing test data. // Excluded from directory trees. // const testdataDirName = "testdata" type Directory struct { Depth int Path string // directory path; includes Name Name string // directory name HasPkg bool // true if the directory contains at least one package Synopsis string // package documentation, if any Dirs []*Directory // subdirectories } func isGoFile(fi os.FileInfo) bool { name := fi.Name() return !fi.IsDir() && len(name) > 0 && name[0] != '.' && // ignore .files pathpkg.Ext(name) == ".go" } func isPkgFile(fi os.FileInfo) bool { return isGoFile(fi) && !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files } func isPkgDir(fi os.FileInfo) bool { name := fi.Name() return fi.IsDir() && len(name) > 0 && name[0] != '_' && name[0] != '.' // ignore _files and .files } type treeBuilder struct { c *Corpus maxDepth int } func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory { if name == testdataDirName { return nil } if depth >= b.maxDepth { // return a dummy directory so that the parent directory // doesn't get discarded just because we reached the max // directory depth return &Directory{ Depth: depth, Path: path, Name: name, } } var synopses [3]string // prioritized package documentation (0 == highest priority) show := true // show in package listing hasPkgFiles := false haveSummary := false if hook := b.c.SummarizePackage; hook != nil { if summary, show0, ok := hook(strings.TrimPrefix(path, "/src/pkg/")); ok { hasPkgFiles = true show = show0 synopses[0] = summary haveSummary = true } } list, _ := b.c.fs.ReadDir(path) // determine number of subdirectories and if there are package files var dirchs []chan *Directory for _, d := range list { switch { case isPkgDir(d): ch := make(chan *Directory, 1) dirchs = append(dirchs, ch) go func(d os.FileInfo) { name := d.Name() ch <- b.newDirTree(fset, pathpkg.Join(path, name), name, depth+1) }(d) case !haveSummary && isPkgFile(d): // looks like a package file, but may just be a file ending in ".go"; // don't just count it yet (otherwise we may end up with hasPkgFiles even // though the directory doesn't contain any real package files - was bug) // no "optimal" package synopsis yet; continue to collect synopses file, err := b.c.parseFile(fset, pathpkg.Join(path, d.Name()), parser.ParseComments|parser.PackageClauseOnly) if err == nil { hasPkgFiles = true if file.Doc != nil { // prioritize documentation i := -1 switch file.Name.Name { case name: i = 0 // normal case: directory name matches package name case "main": i = 1 // directory contains a main package default: i = 2 // none of the above } if 0 <= i && i < len(synopses) && synopses[i] == "" { synopses[i] = doc.Synopsis(file.Doc.Text()) } } haveSummary = synopses[0] != "" } } } // create subdirectory tree var dirs []*Directory for _, ch := range dirchs { if d := <-ch; d != nil { dirs = append(dirs, d) } } // if there are no package files and no subdirectories // containing package files, ignore the directory if !hasPkgFiles && len(dirs) == 0 { return nil } // select the highest-priority synopsis for the directory entry, if any synopsis := "" for _, synopsis = range synopses { if synopsis != "" { break } } return &Directory{ Depth: depth, Path: path, Name: name, HasPkg: hasPkgFiles && show, // TODO(bradfitz): add proper Hide field? Synopsis: synopsis, Dirs: dirs, } } // newDirectory creates a new package directory tree with at most maxDepth // levels, anchored at root. The result tree is pruned such that it only // contains directories that contain package files or that contain // subdirectories containing package files (transitively). If a non-nil // pathFilter is provided, directory paths additionally must be accepted // by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is // provided for maxDepth, nodes at larger depths are pruned as well; they // are assumed to contain package files even if their contents are not known // (i.e., in this case the tree may contain directories w/o any package files). // func (c *Corpus) newDirectory(root string, maxDepth int) *Directory { // The root could be a symbolic link so use Stat not Lstat. d, err := c.fs.Stat(root) // If we fail here, report detailed error messages; otherwise // is is hard to see why a directory tree was not built. switch { case err != nil: log.Printf("newDirectory(%s): %s", root, err) return nil case !isPkgDir(d): log.Printf("newDirectory(%s): not a package directory", root) return nil } if maxDepth < 0 { maxDepth = 1e6 // "infinity" } b := treeBuilder{c, maxDepth} // the file set provided is only for local parsing, no position // information escapes and thus we don't need to save the set return b.newDirTree(token.NewFileSet(), root, d.Name(), 0) } func (dir *Directory) writeLeafs(buf *bytes.Buffer) { if dir != nil { if len(dir.Dirs) == 0 { buf.WriteString(dir.Path) buf.WriteByte('\n') return } for _, d := range dir.Dirs { d.writeLeafs(buf) } } } func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { if dir != nil { if !skipRoot { c <- dir } for _, d := range dir.Dirs { d.walk(c, false) } } } func (dir *Directory) iter(skipRoot bool) <-chan *Directory { c := make(chan *Directory) go func() { dir.walk(c, skipRoot) close(c) }() return c } func (dir *Directory) lookupLocal(name string) *Directory { for _, d := range dir.Dirs { if d.Name == name { return d } } return nil } func splitPath(p string) []string { p = strings.TrimPrefix(p, "/") if p == "" { return nil } return strings.Split(p, "/") } // lookup looks for the *Directory for a given path, relative to dir. func (dir *Directory) lookup(path string) *Directory { d := splitPath(dir.Path) p := splitPath(path) i := 0 for i < len(d) { if i >= len(p) || d[i] != p[i] { return nil } i++ } for dir != nil && i < len(p) { dir = dir.lookupLocal(p[i]) i++ } return dir } // DirEntry describes a directory entry. The Depth and Height values // are useful for presenting an entry in an indented fashion. // type DirEntry struct { Depth int // >= 0 Height int // = DirList.MaxHeight - Depth, > 0 Path string // directory path; includes Name, relative to DirList root Name string // directory name HasPkg bool // true if the directory contains at least one package Synopsis string // package documentation, if any } type DirList struct { MaxHeight int // directory tree height, > 0 List []DirEntry } // listing creates a (linear) directory listing from a directory tree. // If skipRoot is set, the root directory itself is excluded from the list. // func (root *Directory) listing(skipRoot bool) *DirList { if root == nil { return nil } // determine number of entries n and maximum height n := 0 minDepth := 1 << 30 // infinity maxDepth := 0 for d := range root.iter(skipRoot) { n++ if minDepth > d.Depth { minDepth = d.Depth } if maxDepth < d.Depth { maxDepth = d.Depth } } maxHeight := maxDepth - minDepth + 1 if n == 0 { return nil } // create list list := make([]DirEntry, n) i := 0 for d := range root.iter(skipRoot) { p := &list[i] p.Depth = d.Depth - minDepth p.Height = maxHeight - p.Depth // the path is relative to root.Path - remove the root.Path // prefix (the prefix should always be present but avoid // crashes and check) path := strings.TrimPrefix(d.Path, root.Path) // remove leading separator if any - path must be relative path = strings.TrimPrefix(path, "/") p.Path = path p.Name = d.Name p.HasPkg = d.HasPkg p.Synopsis = d.Synopsis i++ } return &DirList{maxHeight, list} } ./godoc/vfs/0000755000014500017510000000000012246613010012364 5ustar michaelstaff./godoc/vfs/mapfs/0000755000014500017510000000000012246613010013472 5ustar michaelstaff./godoc/vfs/mapfs/mapfs_test.go0000644000014500017510000000442012246613010016166 0ustar michaelstaff// 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 mapfs import ( "io/ioutil" "os" "reflect" "testing" ) func TestOpenRoot(t *testing.T) { fs := New(map[string]string{ "foo/bar/three.txt": "a", "foo/bar.txt": "b", "top.txt": "c", "other-top.txt": "d", }) tests := []struct { path string want string }{ {"/foo/bar/three.txt", "a"}, {"foo/bar/three.txt", "a"}, {"foo/bar.txt", "b"}, {"top.txt", "c"}, {"/top.txt", "c"}, {"other-top.txt", "d"}, {"/other-top.txt", "d"}, } for _, tt := range tests { rsc, err := fs.Open(tt.path) if err != nil { t.Errorf("Open(%q) = %v", tt.path, err) continue } slurp, err := ioutil.ReadAll(rsc) if err != nil { t.Error(err) } if string(slurp) != tt.want { t.Errorf("Read(%q) = %q; want %q", tt.path, tt.want, slurp) } rsc.Close() } _, err := fs.Open("/xxxx") if !os.IsNotExist(err) { t.Errorf("ReadDir /xxxx = %v; want os.IsNotExist error", err) } } func TestReaddir(t *testing.T) { fs := New(map[string]string{ "foo/bar/three.txt": "333", "foo/bar.txt": "22", "top.txt": "top.txt file", "other-top.txt": "other-top.txt file", }) tests := []struct { dir string want []os.FileInfo }{ { dir: "/", want: []os.FileInfo{ mapFI{name: "foo", dir: true}, mapFI{name: "other-top.txt", size: len("other-top.txt file")}, mapFI{name: "top.txt", size: len("top.txt file")}, }, }, { dir: "/foo", want: []os.FileInfo{ mapFI{name: "bar", dir: true}, mapFI{name: "bar.txt", size: 2}, }, }, { dir: "/foo/", want: []os.FileInfo{ mapFI{name: "bar", dir: true}, mapFI{name: "bar.txt", size: 2}, }, }, { dir: "/foo/bar", want: []os.FileInfo{ mapFI{name: "three.txt", size: 3}, }, }, } for _, tt := range tests { fis, err := fs.ReadDir(tt.dir) if err != nil { t.Errorf("ReadDir(%q) = %v", tt.dir, err) continue } if !reflect.DeepEqual(fis, tt.want) { t.Errorf("ReadDir(%q) = %#v; want %#v", tt.dir, fis, tt.want) continue } } _, err := fs.ReadDir("/xxxx") if !os.IsNotExist(err) { t.Errorf("ReadDir /xxxx = %v; want os.IsNotExist error", err) } } ./godoc/vfs/mapfs/mapfs.go0000644000014500017510000000630012246613010015126 0ustar michaelstaff// 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 mapfs file provides an implementation of the FileSystem // interface based on the contents of a map[string]string. package mapfs import ( "io" "os" pathpkg "path" "sort" "strings" "time" "code.google.com/p/go.tools/godoc/vfs" ) // New returns a new FileSystem from the provided map. // Map keys should be forward slash-separated pathnames // and not contain a leading slash. func New(m map[string]string) vfs.FileSystem { return mapFS(m) } // mapFS is the map based implementation of FileSystem type mapFS map[string]string func (fs mapFS) String() string { return "mapfs" } func (fs mapFS) Close() error { return nil } func filename(p string) string { return strings.TrimPrefix(p, "/") } func (fs mapFS) Open(p string) (vfs.ReadSeekCloser, error) { b, ok := fs[filename(p)] if !ok { return nil, os.ErrNotExist } return nopCloser{strings.NewReader(b)}, nil } func fileInfo(name, contents string) os.FileInfo { return mapFI{name: pathpkg.Base(name), size: len(contents)} } func dirInfo(name string) os.FileInfo { return mapFI{name: pathpkg.Base(name), dir: true} } func (fs mapFS) Lstat(p string) (os.FileInfo, error) { b, ok := fs[filename(p)] if ok { return fileInfo(p, b), nil } ents, _ := fs.ReadDir(p) if len(ents) > 0 { return dirInfo(p), nil } return nil, os.ErrNotExist } func (fs mapFS) Stat(p string) (os.FileInfo, error) { return fs.Lstat(p) } // slashdir returns path.Dir(p), but special-cases paths not beginning // with a slash to be in the root. func slashdir(p string) string { d := pathpkg.Dir(p) if d == "." { return "/" } if strings.HasPrefix(p, "/") { return d } return "/" + d } func (fs mapFS) ReadDir(p string) ([]os.FileInfo, error) { p = pathpkg.Clean(p) var ents []string fim := make(map[string]os.FileInfo) // base -> fi for fn, b := range fs { dir := slashdir(fn) isFile := true var lastBase string for { if dir == p { base := lastBase if isFile { base = pathpkg.Base(fn) } if fim[base] == nil { var fi os.FileInfo if isFile { fi = fileInfo(fn, b) } else { fi = dirInfo(base) } ents = append(ents, base) fim[base] = fi } } if dir == "/" { break } else { isFile = false lastBase = pathpkg.Base(dir) dir = pathpkg.Dir(dir) } } } if len(ents) == 0 { return nil, os.ErrNotExist } sort.Strings(ents) var list []os.FileInfo for _, dir := range ents { list = append(list, fim[dir]) } return list, nil } // mapFI is the map-based implementation of FileInfo. type mapFI struct { name string size int dir bool } func (fi mapFI) IsDir() bool { return fi.dir } func (fi mapFI) ModTime() time.Time { return time.Time{} } func (fi mapFI) Mode() os.FileMode { if fi.IsDir() { return 0755 | os.ModeDir } return 0444 } func (fi mapFI) Name() string { return pathpkg.Base(fi.name) } func (fi mapFI) Size() int64 { return int64(fi.size) } func (fi mapFI) Sys() interface{} { return nil } type nopCloser struct { io.ReadSeeker } func (nc nopCloser) Close() error { return nil } ./godoc/vfs/os.go0000644000014500017510000000320412246613010013333 0ustar michaelstaff// 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 vfs import ( "fmt" "io/ioutil" "os" pathpkg "path" "path/filepath" ) // OS returns an implementation of FileSystem reading from the // tree rooted at root. Recording a root is convenient everywhere // but necessary on Windows, because the slash-separated path // passed to Open has no way to specify a drive letter. Using a root // lets code refer to OS(`c:\`), OS(`d:\`) and so on. func OS(root string) FileSystem { return osFS(root) } type osFS string func (root osFS) String() string { return "os(" + string(root) + ")" } func (root osFS) resolve(path string) string { // Clean the path so that it cannot possibly begin with ../. // If it did, the result of filepath.Join would be outside the // tree rooted at root. We probably won't ever see a path // with .. in it, but be safe anyway. path = pathpkg.Clean("/" + path) return filepath.Join(string(root), path) } func (root osFS) Open(path string) (ReadSeekCloser, error) { f, err := os.Open(root.resolve(path)) if err != nil { return nil, err } fi, err := f.Stat() if err != nil { return nil, err } if fi.IsDir() { return nil, fmt.Errorf("Open: %s is a directory", path) } return f, nil } func (root osFS) Lstat(path string) (os.FileInfo, error) { return os.Lstat(root.resolve(path)) } func (root osFS) Stat(path string) (os.FileInfo, error) { return os.Stat(root.resolve(path)) } func (root osFS) ReadDir(path string) ([]os.FileInfo, error) { return ioutil.ReadDir(root.resolve(path)) // is sorted } ./godoc/vfs/vfs.go0000644000014500017510000000221712246613010013513 0ustar michaelstaff// 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 vfs defines types for abstract file system access and provides an // implementation accessing the file system of the underlying OS. package vfs import ( "io" "io/ioutil" "os" ) // The FileSystem interface specifies the methods godoc is using // to access the file system for which it serves documentation. type FileSystem interface { Opener Lstat(path string) (os.FileInfo, error) Stat(path string) (os.FileInfo, error) ReadDir(path string) ([]os.FileInfo, error) String() string } // Opener is a minimal virtual filesystem that can only open regular files. type Opener interface { Open(name string) (ReadSeekCloser, error) } // A ReadSeekCloser can Read, Seek, and Close. type ReadSeekCloser interface { io.Reader io.Seeker io.Closer } // ReadFile reads the file named by path from fs and returns the contents. func ReadFile(fs Opener, path string) ([]byte, error) { rc, err := fs.Open(path) if err != nil { return nil, err } defer rc.Close() return ioutil.ReadAll(rc) } ./godoc/vfs/httpfs/0000755000014500017510000000000012246613010013674 5ustar michaelstaff./godoc/vfs/httpfs/httpfs.go0000644000014500017510000000412612246613010015536 0ustar michaelstaff// 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 httpfs implements http.FileSystem using a godoc vfs.FileSystem. package httpfs import ( "fmt" "io" "net/http" "os" "code.google.com/p/go.tools/godoc/vfs" ) func New(fs vfs.FileSystem) http.FileSystem { return &httpFS{fs} } type httpFS struct { fs vfs.FileSystem } func (h *httpFS) Open(name string) (http.File, error) { fi, err := h.fs.Stat(name) if err != nil { return nil, err } if fi.IsDir() { return &httpDir{h.fs, name, nil}, nil } f, err := h.fs.Open(name) if err != nil { return nil, err } return &httpFile{h.fs, f, name}, nil } // httpDir implements http.File for a directory in a FileSystem. type httpDir struct { fs vfs.FileSystem name string pending []os.FileInfo } func (h *httpDir) Close() error { return nil } func (h *httpDir) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) } func (h *httpDir) Read([]byte) (int, error) { return 0, fmt.Errorf("cannot Read from directory %s", h.name) } func (h *httpDir) Seek(offset int64, whence int) (int64, error) { if offset == 0 && whence == 0 { h.pending = nil return 0, nil } return 0, fmt.Errorf("unsupported Seek in directory %s", h.name) } func (h *httpDir) Readdir(count int) ([]os.FileInfo, error) { if h.pending == nil { d, err := h.fs.ReadDir(h.name) if err != nil { return nil, err } if d == nil { d = []os.FileInfo{} // not nil } h.pending = d } if len(h.pending) == 0 && count > 0 { return nil, io.EOF } if count <= 0 || count > len(h.pending) { count = len(h.pending) } d := h.pending[:count] h.pending = h.pending[count:] return d, nil } // httpFile implements http.File for a file (not directory) in a FileSystem. type httpFile struct { fs vfs.FileSystem vfs.ReadSeekCloser name string } func (h *httpFile) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) } func (h *httpFile) Readdir(int) ([]os.FileInfo, error) { return nil, fmt.Errorf("cannot Readdir from file %s", h.name) } ./godoc/vfs/zipfs/0000755000014500017510000000000012246613010013517 5ustar michaelstaff./godoc/vfs/zipfs/zipfs.go0000644000014500017510000001323312246613010015203 0ustar michaelstaff// 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. // Package zipfs file provides an implementation of the FileSystem // interface based on the contents of a .zip file. // // Assumptions: // // - The file paths stored in the zip file must use a slash ('/') as path // separator; and they must be relative (i.e., they must not start with // a '/' - this is usually the case if the file was created w/o special // options). // - The zip file system treats the file paths found in the zip internally // like absolute paths w/o a leading '/'; i.e., the paths are considered // relative to the root of the file system. // - All path arguments to file system methods must be absolute paths. package zipfs import ( "archive/zip" "fmt" "io" "os" "path" "sort" "strings" "time" "code.google.com/p/go.tools/godoc/vfs" ) // zipFI is the zip-file based implementation of FileInfo type zipFI struct { name string // directory-local name file *zip.File // nil for a directory } func (fi zipFI) Name() string { return fi.name } func (fi zipFI) Size() int64 { if f := fi.file; f != nil { return int64(f.UncompressedSize) } return 0 // directory } func (fi zipFI) ModTime() time.Time { if f := fi.file; f != nil { return f.ModTime() } return time.Time{} // directory has no modified time entry } func (fi zipFI) Mode() os.FileMode { if fi.file == nil { // Unix directories typically are executable, hence 555. return os.ModeDir | 0555 } return 0444 } func (fi zipFI) IsDir() bool { return fi.file == nil } func (fi zipFI) Sys() interface{} { return nil } // zipFS is the zip-file based implementation of FileSystem type zipFS struct { *zip.ReadCloser list zipList name string } func (fs *zipFS) String() string { return "zip(" + fs.name + ")" } func (fs *zipFS) Close() error { fs.list = nil return fs.ReadCloser.Close() } func zipPath(name string) string { name = path.Clean(name) if !path.IsAbs(name) { panic(fmt.Sprintf("stat: not an absolute path: %s", name)) } return name[1:] // strip leading '/' } 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) } _, name := path.Split(abspath) var file *zip.File if exact { file = fs.list[i] // exact match found - must be a file } return i, zipFI{name, file}, nil } func (fs *zipFS) Open(abspath string) (vfs.ReadSeekCloser, error) { _, fi, err := fs.stat(zipPath(abspath)) if err != nil { return nil, err } if fi.IsDir() { return nil, fmt.Errorf("Open: %s is a directory", abspath) } r, err := fi.file.Open() if err != nil { return nil, err } return &zipSeek{fi.file, r}, nil } type zipSeek struct { file *zip.File io.ReadCloser } func (f *zipSeek) Seek(offset int64, whence int) (int64, error) { if whence == 0 && offset == 0 { r, err := f.file.Open() if err != nil { return 0, err } f.Close() f.ReadCloser = r return 0, nil } return 0, fmt.Errorf("unsupported Seek in %s", f.file.Name) } func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) { _, fi, err := fs.stat(zipPath(abspath)) return fi, err } func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) { _, fi, err := fs.stat(zipPath(abspath)) return fi, err } func (fs *zipFS) ReadDir(abspath string) ([]os.FileInfo, error) { path := zipPath(abspath) i, fi, err := fs.stat(path) if err != nil { return nil, err } if !fi.IsDir() { return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath) } var list []os.FileInfo dirname := path + "/" prevname := "" for _, e := range fs.list[i:] { if !strings.HasPrefix(e.Name, dirname) { break // not in the same directory anymore } name := e.Name[len(dirname):] // local name file := e if i := strings.IndexRune(name, '/'); i >= 0 { // We infer directories from files in subdirectories. // If we have x/y, return a directory entry for x. name = name[0:i] // keep local directory name only file = nil } // If we have x/y and x/z, don't return two directory entries for x. // TODO(gri): It should be possible to do this more efficiently // by determining the (fs.list) range of local directory entries // (via two binary searches). if name != prevname { list = append(list, zipFI{name, file}) prevname = name } } return list, nil } func New(rc *zip.ReadCloser, name string) vfs.FileSystem { list := make(zipList, len(rc.File)) copy(list, rc.File) // sort a copy of rc.File sort.Sort(list) return &zipFS{rc, list, name} } type zipList []*zip.File // zipList implements sort.Interface func (z zipList) Len() int { return len(z) } func (z zipList) Less(i, j int) bool { return z[i].Name < z[j].Name } func (z zipList) Swap(i, j int) { z[i], z[j] = z[j], z[i] } // lookup returns the smallest index of an entry with an exact match // for name, or an inexact match starting with name/. If there is no // such entry, the result is -1, false. func (z zipList) lookup(name string) (index int, exact bool) { // look for exact match first (name comes before name/ in z) i := sort.Search(len(z), func(i int) bool { return name <= z[i].Name }) if i >= len(z) { return -1, false } // 0 <= i < len(z) if z[i].Name == name { return i, true } // look for inexact match (must be in z[i:], if present) z = z[i:] name += "/" j := sort.Search(len(z), func(i int) bool { return name <= z[i].Name }) if j >= len(z) { return -1, false } // 0 <= j < len(z) if strings.HasPrefix(z[j].Name, name) { return i + j, false } return -1, false } ./godoc/vfs/namespace.go0000644000014500017510000002546512246613010014663 0ustar michaelstaff// 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. package vfs import ( "fmt" "io" "os" pathpkg "path" "sort" "strings" "time" ) // Setting debugNS = true will enable debugging prints about // name space translations. const debugNS = false // A NameSpace is a file system made up of other file systems // mounted at specific locations in the name space. // // The representation is a map from mount point locations // to the list of file systems mounted at that location. A traditional // Unix mount table would use a single file system per mount point, // but we want to be able to mount multiple file systems on a single // mount point and have the system behave as if the union of those // file systems were present at the mount point. // For example, if the OS file system has a Go installation in // c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then // this name space creates the view we want for the godoc server: // // NameSpace{ // "/": { // {old: "/", fs: OS(`c:\Go`), new: "/"}, // }, // "/src/pkg": { // {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, // {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, // {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, // }, // } // // This is created by executing: // // ns := NameSpace{} // ns.Bind("/", OS(`c:\Go`), "/", BindReplace) // ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", BindAfter) // ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", BindAfter) // // A particular mount point entry is a triple (old, fs, new), meaning that to // operate on a path beginning with old, replace that prefix (old) with new // and then pass that path to the FileSystem implementation fs. // // Given this name space, a ReadDir of /src/pkg/code will check each prefix // of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src, // then /), stopping when it finds one. For the above example, /src/pkg/code // will find the mount point at /src/pkg: // // {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, // {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, // {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, // // ReadDir will when execute these three calls and merge the results: // // OS(`c:\Go`).ReadDir("/src/pkg/code") // OS(`d:\Work1').ReadDir("/src/code") // OS(`d:\Work2').ReadDir("/src/code") // // Note that the "/src/pkg" in "/src/pkg/code" has been replaced by // just "/src" in the final two calls. // // OS is itself an implementation of a file system: it implements // OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`). // // Because the new path is evaluated by fs (here OS(root)), another way // to read the mount table is to mentally combine fs+new, so that this table: // // {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, // {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, // {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, // // reads as: // // "/src/pkg" -> c:\Go\src\pkg // "/src/pkg" -> d:\Work1\src // "/src/pkg" -> d:\Work2\src // // An invariant (a redundancy) of the name space representation is that // ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s // mount table entries always have old == "/src/pkg"). The 'old' field is // useful to callers, because they receive just a []mountedFS and not any // other indication of which mount point was found. // type NameSpace map[string][]mountedFS // A mountedFS handles requests for path by replacing // a prefix 'old' with 'new' and then calling the fs methods. type mountedFS struct { old string fs FileSystem new string } // hasPathPrefix returns true if x == y or x == y + "/" + more func hasPathPrefix(x, y string) bool { return x == y || strings.HasPrefix(x, y) && (strings.HasSuffix(y, "/") || strings.HasPrefix(x[len(y):], "/")) } // translate translates path for use in m, replacing old with new. // // mountedFS{"/src/pkg", fs, "/src"}.translate("/src/pkg/code") == "/src/code". func (m mountedFS) translate(path string) string { path = pathpkg.Clean("/" + path) if !hasPathPrefix(path, m.old) { panic("translate " + path + " but old=" + m.old) } return pathpkg.Join(m.new, path[len(m.old):]) } func (NameSpace) String() string { return "ns" } // Fprint writes a text representation of the name space to w. func (ns NameSpace) Fprint(w io.Writer) { fmt.Fprint(w, "name space {\n") var all []string for mtpt := range ns { all = append(all, mtpt) } sort.Strings(all) for _, mtpt := range all { fmt.Fprintf(w, "\t%s:\n", mtpt) for _, m := range ns[mtpt] { fmt.Fprintf(w, "\t\t%s %s\n", m.fs, m.new) } } fmt.Fprint(w, "}\n") } // clean returns a cleaned, rooted path for evaluation. // It canonicalizes the path so that we can use string operations // to analyze it. func (NameSpace) clean(path string) string { return pathpkg.Clean("/" + path) } type BindMode int const ( BindReplace BindMode = iota BindBefore BindAfter ) // Bind causes references to old to redirect to the path new in newfs. // If mode is BindReplace, old redirections are discarded. // If mode is BindBefore, this redirection takes priority over existing ones, // but earlier ones are still consulted for paths that do not exist in newfs. // If mode is BindAfter, this redirection happens only after existing ones // have been tried and failed. func (ns NameSpace) Bind(old string, newfs FileSystem, new string, mode BindMode) { old = ns.clean(old) new = ns.clean(new) m := mountedFS{old, newfs, new} var mtpt []mountedFS switch mode { case BindReplace: mtpt = append(mtpt, m) case BindAfter: mtpt = append(mtpt, ns.resolve(old)...) mtpt = append(mtpt, m) case BindBefore: mtpt = append(mtpt, m) mtpt = append(mtpt, ns.resolve(old)...) } // Extend m.old, m.new in inherited mount point entries. for i := range mtpt { m := &mtpt[i] if m.old != old { if !hasPathPrefix(old, m.old) { // This should not happen. If it does, panic so // that we can see the call trace that led to it. panic(fmt.Sprintf("invalid Bind: old=%q m={%q, %s, %q}", old, m.old, m.fs.String(), m.new)) } suffix := old[len(m.old):] m.old = pathpkg.Join(m.old, suffix) m.new = pathpkg.Join(m.new, suffix) } } ns[old] = mtpt } // resolve resolves a path to the list of mountedFS to use for path. func (ns NameSpace) resolve(path string) []mountedFS { path = ns.clean(path) for { if m := ns[path]; m != nil { if debugNS { fmt.Printf("resolve %s: %v\n", path, m) } return m } if path == "/" { break } path = pathpkg.Dir(path) } return nil } // Open implements the FileSystem Open method. func (ns NameSpace) Open(path string) (ReadSeekCloser, error) { var err error for _, m := range ns.resolve(path) { if debugNS { fmt.Printf("tx %s: %v\n", path, m.translate(path)) } r, err1 := m.fs.Open(m.translate(path)) if err1 == nil { return r, nil } if err == nil { err = err1 } } if err == nil { err = &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} } return nil, err } // stat implements the FileSystem Stat and Lstat methods. func (ns NameSpace) stat(path string, f func(FileSystem, string) (os.FileInfo, error)) (os.FileInfo, error) { var err error for _, m := range ns.resolve(path) { fi, err1 := f(m.fs, m.translate(path)) if err1 == nil { return fi, nil } if err == nil { err = err1 } } if err == nil { err = &os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist} } return nil, err } func (ns NameSpace) Stat(path string) (os.FileInfo, error) { return ns.stat(path, FileSystem.Stat) } func (ns NameSpace) Lstat(path string) (os.FileInfo, error) { return ns.stat(path, FileSystem.Lstat) } // dirInfo is a trivial implementation of os.FileInfo for a directory. type dirInfo string func (d dirInfo) Name() string { return string(d) } func (d dirInfo) Size() int64 { return 0 } func (d dirInfo) Mode() os.FileMode { return os.ModeDir | 0555 } func (d dirInfo) ModTime() time.Time { return startTime } func (d dirInfo) IsDir() bool { return true } func (d dirInfo) Sys() interface{} { return nil } var startTime = time.Now() // ReadDir implements the FileSystem ReadDir method. It's where most of the magic is. // (The rest is in resolve.) // // Logically, ReadDir must return the union of all the directories that are named // by path. In order to avoid misinterpreting Go packages, of all the directories // that contain Go source code, we only include the files from the first, // but we include subdirectories from all. // // ReadDir must also return directory entries needed to reach mount points. // If the name space looks like the example in the type NameSpace comment, // but c:\Go does not have a src/pkg subdirectory, we still want to be able // to find that subdirectory, because we've mounted d:\Work1 and d:\Work2 // there. So if we don't see "src" in the directory listing for c:\Go, we add an // entry for it before returning. // func (ns NameSpace) ReadDir(path string) ([]os.FileInfo, error) { path = ns.clean(path) var ( haveGo = false haveName = map[string]bool{} all []os.FileInfo err error first []os.FileInfo ) for _, m := range ns.resolve(path) { dir, err1 := m.fs.ReadDir(m.translate(path)) if err1 != nil { if err == nil { err = err1 } continue } if dir == nil { dir = []os.FileInfo{} } if first == nil { first = dir } // If we don't yet have Go files in 'all' and this directory // has some, add all the files from this directory. // Otherwise, only add subdirectories. useFiles := false if !haveGo { for _, d := range dir { if strings.HasSuffix(d.Name(), ".go") { useFiles = true haveGo = true break } } } for _, d := range dir { name := d.Name() if (d.IsDir() || useFiles) && !haveName[name] { haveName[name] = true all = append(all, d) } } } // We didn't find any directories containing Go files. // If some directory returned successfully, use that. if !haveGo { for _, d := range first { if !haveName[d.Name()] { haveName[d.Name()] = true all = append(all, d) } } } // Built union. Add any missing directories needed to reach mount points. for old := range ns { if hasPathPrefix(old, path) && old != path { // Find next element after path in old. elem := old[len(path):] elem = strings.TrimPrefix(elem, "/") if i := strings.Index(elem, "/"); i >= 0 { elem = elem[:i] } if !haveName[elem] { haveName[elem] = true all = append(all, dirInfo(elem)) } } } if len(all) == 0 { return nil, err } sort.Sort(byName(all)) return all, nil } // byName implements sort.Interface. type byName []os.FileInfo func (f byName) Len() int { return len(f) } func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } ./blog/0000755000014500017510000000000012246613010011416 5ustar michaelstaff./blog/atom/0000755000014500017510000000000012246613010012356 5ustar michaelstaff./blog/atom/atom.go0000644000014500017510000000246212246613010013651 0ustar michaelstaff// Copyright 2009 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. // Adapted from encoding/xml/read_test.go. // Package atom defines XML data structures for an Atom feed. package atom import ( "encoding/xml" "time" ) type Feed struct { XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"` Title string `xml:"title"` ID string `xml:"id"` Link []Link `xml:"link"` Updated TimeStr `xml:"updated"` Author *Person `xml:"author"` Entry []*Entry `xml:"entry"` } type Entry struct { Title string `xml:"title"` ID string `xml:"id"` Link []Link `xml:"link"` Published TimeStr `xml:"published"` Updated TimeStr `xml:"updated"` Author *Person `xml:"author"` Summary *Text `xml:"summary"` Content *Text `xml:"content"` } type Link struct { Rel string `xml:"rel,attr"` Href string `xml:"href,attr"` } type Person struct { Name string `xml:"name"` URI string `xml:"uri,omitempty"` Email string `xml:"email,omitempty"` InnerXML string `xml:",innerxml"` } type Text struct { Type string `xml:"type,attr"` Body string `xml:",chardata"` } type TimeStr string func Time(t time.Time) TimeStr { return TimeStr(t.Format("2006-01-02T15:04:05-07:00")) } ./blog/blog.go0000644000014500017510000002413312246613010012673 0ustar michaelstaff// 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 blog implements a web server for articles written in present format. package blog import ( "bytes" "encoding/json" "encoding/xml" "fmt" "html/template" "log" "net/http" "os" "path/filepath" "regexp" "sort" "strings" "time" "code.google.com/p/go.tools/blog/atom" "code.google.com/p/go.tools/present" ) var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`) // Config specifies Server configuration values. type Config struct { ContentPath string // Relative or absolute location of article files and related content. TemplatePath string // Relative or absolute location of template files. BaseURL string // Absolute base URL (for permalinks; no trailing slash). BasePath string // Base URL path relative to server root (no trailing slash). GodocURL string // The base URL of godoc (for menu bar; no trailing slash). Hostname string // Server host name, used for rendering ATOM feeds. HomeArticles int // Articles to display on the home page. FeedArticles int // Articles to include in Atom and JSON feeds. FeedTitle string // The title of the Atom XML feed PlayEnabled bool } // Doc represents an article adorned with presentation data. type Doc struct { *present.Doc Permalink string // Canonical URL for this document. Path string // Path relative to server root (including base). HTML template.HTML // rendered article Related []*Doc Newer, Older *Doc } // Server implements an http.Handler that serves blog articles. type Server struct { cfg Config docs []*Doc tags []string docPaths map[string]*Doc // key is path without BasePath. docTags map[string][]*Doc template struct { home, index, article, doc *template.Template } atomFeed []byte // pre-rendered Atom feed jsonFeed []byte // pre-rendered JSON feed content http.Handler } // NewServer constructs a new Server using the specified config. func NewServer(cfg Config) (*Server, error) { present.PlayEnabled = cfg.PlayEnabled root := filepath.Join(cfg.TemplatePath, "root.tmpl") parse := func(name string) (*template.Template, error) { t := template.New("").Funcs(funcMap) return t.ParseFiles(root, filepath.Join(cfg.TemplatePath, name)) } s := &Server{cfg: cfg} // Parse templates. var err error s.template.home, err = parse("home.tmpl") if err != nil { return nil, err } s.template.index, err = parse("index.tmpl") if err != nil { return nil, err } s.template.article, err = parse("article.tmpl") if err != nil { return nil, err } p := present.Template().Funcs(funcMap) s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl")) if err != nil { return nil, err } // Load content. err = s.loadDocs(filepath.Clean(cfg.ContentPath)) if err != nil { return nil, err } err = s.renderAtomFeed() if err != nil { return nil, err } err = s.renderJSONFeed() if err != nil { return nil, err } // Set up content file server. s.content = http.StripPrefix(s.cfg.BasePath, http.FileServer(http.Dir(cfg.ContentPath))) return s, nil } var funcMap = template.FuncMap{ "sectioned": sectioned, "authors": authors, } // sectioned returns true if the provided Doc contains more than one section. // This is used to control whether to display the table of contents and headings. func sectioned(d *present.Doc) bool { return len(d.Sections) > 1 } // authors returns a comma-separated list of author names. func authors(authors []present.Author) string { var b bytes.Buffer last := len(authors) - 1 for i, a := range authors { if i > 0 { if i == last { b.WriteString(" and ") } else { b.WriteString(", ") } } b.WriteString(authorName(a)) } return b.String() } // authorName returns the first line of the Author text: the author's name. func authorName(a present.Author) string { el := a.TextElem() if len(el) == 0 { return "" } text, ok := el[0].(present.Text) if !ok || len(text.Lines) == 0 { return "" } return text.Lines[0] } // loadDocs reads all content from the provided file system root, renders all // the articles it finds, adds them to the Server's docs field, computes the // denormalized docPaths, docTags, and tags fields, and populates the various // helper fields (Next, Previous, Related) for each Doc. func (s *Server) loadDocs(root string) error { // Read content into docs field. const ext = ".article" fn := func(p string, info os.FileInfo, err error) error { if filepath.Ext(p) != ext { return nil } f, err := os.Open(p) if err != nil { return err } defer f.Close() d, err := present.Parse(f, p, 0) if err != nil { return err } html := new(bytes.Buffer) err = d.Render(html, s.template.doc) if err != nil { return err } p = p[len(root) : len(p)-len(ext)] // trim root and extension p = filepath.ToSlash(p) s.docs = append(s.docs, &Doc{ Doc: d, Path: s.cfg.BasePath + p, Permalink: s.cfg.BaseURL + p, HTML: template.HTML(html.String()), }) return nil } err := filepath.Walk(root, fn) if err != nil { return err } sort.Sort(docsByTime(s.docs)) // Pull out doc paths and tags and put in reverse-associating maps. s.docPaths = make(map[string]*Doc) s.docTags = make(map[string][]*Doc) for _, d := range s.docs { s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d for _, t := range d.Tags { s.docTags[t] = append(s.docTags[t], d) } } // Pull out unique sorted list of tags. for t := range s.docTags { s.tags = append(s.tags, t) } sort.Strings(s.tags) // Set up presentation-related fields, Newer, Older, and Related. for _, doc := range s.docs { // Newer, Older: docs adjacent to doc for i := range s.docs { if s.docs[i] != doc { continue } if i > 0 { doc.Newer = s.docs[i-1] } if i+1 < len(s.docs) { doc.Older = s.docs[i+1] } break } // Related: all docs that share tags with doc. related := make(map[*Doc]bool) for _, t := range doc.Tags { for _, d := range s.docTags[t] { if d != doc { related[d] = true } } } for d := range related { doc.Related = append(doc.Related, d) } sort.Sort(docsByTime(doc.Related)) } return nil } // renderAtomFeed generates an XML Atom feed and stores it in the Server's // atomFeed field. func (s *Server) renderAtomFeed() error { var updated time.Time if len(s.docs) > 0 { updated = s.docs[0].Time } feed := atom.Feed{ Title: s.cfg.FeedTitle, ID: "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname, Updated: atom.Time(updated), Link: []atom.Link{{ Rel: "self", Href: s.cfg.BaseURL + "/feed.atom", }}, } for i, doc := range s.docs { if i >= s.cfg.FeedArticles { break } e := &atom.Entry{ Title: doc.Title, ID: feed.ID + doc.Path, Link: []atom.Link{{ Rel: "alternate", Href: doc.Permalink, }}, Published: atom.Time(doc.Time), Updated: atom.Time(doc.Time), Summary: &atom.Text{ Type: "html", Body: summary(doc), }, Content: &atom.Text{ Type: "html", Body: string(doc.HTML), }, Author: &atom.Person{ Name: authors(doc.Authors), }, } feed.Entry = append(feed.Entry, e) } data, err := xml.Marshal(&feed) if err != nil { return err } s.atomFeed = data return nil } type jsonItem struct { Title string Link string Time time.Time Summary string Content string Author string } // renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed // field. func (s *Server) renderJSONFeed() error { var feed []jsonItem for i, doc := range s.docs { if i >= s.cfg.FeedArticles { break } item := jsonItem{ Title: doc.Title, Link: doc.Permalink, Time: doc.Time, Summary: summary(doc), Content: string(doc.HTML), Author: authors(doc.Authors), } feed = append(feed, item) } data, err := json.Marshal(feed) if err != nil { return err } s.jsonFeed = data return nil } // summary returns the first paragraph of text from the provided Doc. func summary(d *Doc) string { if len(d.Sections) == 0 { return "" } for _, elem := range d.Sections[0].Elem { text, ok := elem.(present.Text) if !ok || text.Pre { // skip everything but non-text elements continue } var buf bytes.Buffer for _, s := range text.Lines { buf.WriteString(string(present.Style(s))) buf.WriteByte('\n') } return buf.String() } return "" } // rootData encapsulates data destined for the root template. type rootData struct { Doc *Doc BasePath string GodocURL string Data interface{} } // ServeHTTP serves the front, index, and article pages // as well as the ATOM and JSON feeds. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { var ( d = rootData{BasePath: s.cfg.BasePath, GodocURL: s.cfg.GodocURL} t *template.Template ) switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p { case "/": d.Data = s.docs if len(s.docs) > s.cfg.HomeArticles { d.Data = s.docs[:s.cfg.HomeArticles] } t = s.template.home case "/index": d.Data = s.docs t = s.template.index case "/feed.atom", "/feeds/posts/default": w.Header().Set("Content-type", "application/atom+xml; charset=utf-8") w.Write(s.atomFeed) return case "/.json": if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) { w.Header().Set("Content-type", "application/javascript; charset=utf-8") fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed) return } w.Header().Set("Content-type", "application/json; charset=utf-8") w.Write(s.jsonFeed) return default: doc, ok := s.docPaths[p] if !ok { // Not a doc; try to just serve static content. s.content.ServeHTTP(w, r) return } d.Doc = doc t = s.template.article } err := t.ExecuteTemplate(w, "root", d) if err != nil { log.Println(err) } } // docsByTime implements sort.Interface, sorting Docs by their Time field. type docsByTime []*Doc func (s docsByTime) Len() int { return len(s) } func (s docsByTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) } ./AUTHORS0000644000014500017510000000025512246613010011545 0ustar michaelstaff# This source code refers to The Go Authors for copyright purposes. # The master list of authors is in the main Go distribution, # visible at http://tip.golang.org/AUTHORS. ./cmd/0000755000014500017510000000000012246613010011236 5ustar michaelstaff./cmd/gotype/0000755000014500017510000000000012246613010012545 5ustar michaelstaff./cmd/gotype/gotype.go0000644000014500017510000001216612246613010014411 0ustar michaelstaff// 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. package main import ( "flag" "fmt" "go/ast" "go/build" "go/parser" "go/scanner" "go/token" "io/ioutil" "os" "path/filepath" "runtime" "time" _ "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.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") // 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("", 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, } 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()) // remove this once runtime is smarter 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)) } } ./cmd/gotype/doc.go0000644000014500017510000000266412246613010013651 0ustar michaelstaff// 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. /* The gotype command does syntactic and semantic analysis of Go files and packages like the front-end of a Go compiler. Errors are reported if the analysis fails; otherwise gotype is quiet (unless -v is set). Without a list of paths, gotype reads from standard input, which must provide a single Go source file defining a complete package. If a single path is specified that is a directory, gotype checks the Go files in that directory; they must all belong to the same package. Otherwise, each path must be the filename of Go file belonging to the same package. Usage: gotype [flags] [path...] The flags are: -a use all (incl. _test.go) files when processing a directory -e report all errors (not just the first 10) -v verbose mode Debugging flags: -seq parse sequentially, rather than in parallel -ast print AST (forces -seq) -trace print parse trace (forces -seq) -comments parse comments (ignored unless -ast or -trace is provided) Examples: To check the files a.go, b.go, and c.go: gotype a.go b.go c.go To check an entire package in the directory dir and print the processed files: gotype -v dir To check an entire package including tests in the local directory: gotype -a . To verify the output of a pipe: echo "package foo" | gotype */ package main ./cmd/html2article/0000755000014500017510000000000012246613010013630 5ustar michaelstaff./cmd/html2article/conv.go0000644000014500017510000001543712246613010015136 0ustar michaelstaff// 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. // This program takes an HTML file and outputs a corresponding article file in // present format. See: code.google.com/p/go.tools/present package main import ( "bufio" "bytes" "errors" "flag" "fmt" "io" "log" "os" "regexp" "strings" "code.google.com/p/go.net/html" "code.google.com/p/go.net/html/atom" ) func main() { flag.Parse() err := convert(os.Stdout, os.Stdin) if err != nil { log.Fatal(err) } } func convert(w io.Writer, r io.Reader) error { root, err := html.Parse(r) if err != nil { return err } style := find(root, isTag(atom.Style)) parseStyles(style) body := find(root, isTag(atom.Body)) if body == nil { return errors.New("couldn't find body") } article := limitNewlineRuns(makeHeadings(strings.TrimSpace(text(body)))) _, err = fmt.Fprintf(w, "Title\n\n%s", article) return err } type Style string const ( Bold Style = "*" Italic Style = "_" Code Style = "`" ) var cssRules = make(map[string]Style) func parseStyles(style *html.Node) { if style == nil || style.FirstChild == nil { log.Println("couldn't find styles") return } s := bufio.NewScanner(strings.NewReader(style.FirstChild.Data)) findRule := func(b []byte, atEOF bool) (advance int, token []byte, err error) { if i := bytes.Index(b, []byte("{")); i >= 0 { token = bytes.TrimSpace(b[:i]) advance = i } return } findBody := func(b []byte, atEOF bool) (advance int, token []byte, err error) { if len(b) == 0 { return } if b[0] != '{' { err = fmt.Errorf("expected {, got %c", b[0]) return } if i := bytes.Index(b, []byte("}")); i < 0 { err = fmt.Errorf("can't find closing }") return } else { token = b[1:i] advance = i + 1 } return } s.Split(findRule) for s.Scan() { rule := s.Text() s.Split(findBody) if !s.Scan() { break } b := strings.ToLower(s.Text()) switch { case strings.Contains(b, "italic"): cssRules[rule] = Italic case strings.Contains(b, "bold"): cssRules[rule] = Bold case strings.Contains(b, "Consolas") || strings.Contains(b, "Courier New"): cssRules[rule] = Code } s.Split(findRule) } if err := s.Err(); err != nil { log.Println(err) } } var newlineRun = regexp.MustCompile(`\n\n+`) func limitNewlineRuns(s string) string { return newlineRun.ReplaceAllString(s, "\n\n") } func makeHeadings(body string) string { buf := new(bytes.Buffer) lines := strings.Split(body, "\n") for i, s := range lines { if i == 0 && !isBoldTitle(s) { buf.WriteString("* Introduction\n\n") } if isBoldTitle(s) { s = strings.TrimSpace(strings.Replace(s, "*", " ", -1)) s = "* " + s } buf.WriteString(s) buf.WriteByte('\n') } return buf.String() } func isBoldTitle(s string) bool { return !strings.Contains(s, " ") && strings.HasPrefix(s, "*") && strings.HasSuffix(s, "*") } func indent(buf *bytes.Buffer, s string) { for _, l := range strings.Split(s, "\n") { if l != "" { buf.WriteByte('\t') buf.WriteString(l) } buf.WriteByte('\n') } } func unwrap(buf *bytes.Buffer, s string) { var cont bool for _, l := range strings.Split(s, "\n") { l = strings.TrimSpace(l) if len(l) == 0 { if cont { buf.WriteByte('\n') buf.WriteByte('\n') } cont = false } else { if cont { buf.WriteByte(' ') } buf.WriteString(l) cont = true } } } func text(n *html.Node) string { var buf bytes.Buffer walk(n, func(n *html.Node) bool { switch n.Type { case html.TextNode: buf.WriteString(n.Data) return false case html.ElementNode: // no-op default: return true } a := n.DataAtom if a == atom.Span { switch { case hasStyle(Code)(n): a = atom.Code case hasStyle(Bold)(n): a = atom.B case hasStyle(Italic)(n): a = atom.I } } switch a { case atom.Br: buf.WriteByte('\n') case atom.P: unwrap(&buf, childText(n)) buf.WriteString("\n\n") case atom.Li: buf.WriteString("- ") unwrap(&buf, childText(n)) buf.WriteByte('\n') case atom.Pre: indent(&buf, childText(n)) buf.WriteByte('\n') case atom.A: fmt.Fprintf(&buf, "[[%s][%s]]", attr(n, "href"), childText(n)) case atom.Code: buf.WriteString(highlight(n, "`")) case atom.B: buf.WriteString(highlight(n, "*")) case atom.I: buf.WriteString(highlight(n, "_")) case atom.Img: src := attr(n, "src") fmt.Fprintf(&buf, ".image %s\n", src) case atom.Iframe: src, w, h := attr(n, "src"), attr(n, "width"), attr(n, "height") fmt.Fprintf(&buf, "\n.iframe %s %s %s\n", src, h, w) case atom.Param: if attr(n, "name") == "movie" { // Old style YouTube embed. u := attr(n, "value") u = strings.Replace(u, "/v/", "/embed/", 1) if i := strings.Index(u, "&"); i >= 0 { u = u[:i] } fmt.Fprintf(&buf, "\n.iframe %s 540 304\n", u) } default: return true } return false }) return buf.String() } func childText(node *html.Node) string { var buf bytes.Buffer for n := node.FirstChild; n != nil; n = n.NextSibling { fmt.Fprint(&buf, text(n)) } return buf.String() } func highlight(node *html.Node, char string) string { t := strings.Replace(childText(node), " ", char, -1) return fmt.Sprintf("%s%s%s", char, t, char) } type selector func(*html.Node) bool func isTag(a atom.Atom) selector { return func(n *html.Node) bool { return n.DataAtom == a } } func hasClass(name string) selector { return func(n *html.Node) bool { for _, a := range n.Attr { if a.Key == "class" { for _, c := range strings.Fields(a.Val) { if c == name { return true } } } } return false } } func hasStyle(s Style) selector { return func(n *html.Node) bool { for rule, s2 := range cssRules { if s2 != s { continue } if strings.HasPrefix(rule, ".") && hasClass(rule[1:])(n) { return true } if n.DataAtom.String() == rule { return true } } return false } } func hasAttr(key, val string) selector { return func(n *html.Node) bool { for _, a := range n.Attr { if a.Key == key && a.Val == val { return true } } return false } } func attr(node *html.Node, key string) (value string) { for _, attr := range node.Attr { if attr.Key == key { return attr.Val } } return "" } func findAll(node *html.Node, fn selector) (nodes []*html.Node) { walk(node, func(n *html.Node) bool { if fn(n) { nodes = append(nodes, n) } return true }) return } func find(n *html.Node, fn selector) *html.Node { var result *html.Node walk(n, func(n *html.Node) bool { if result != nil { return false } if fn(n) { result = n return false } return true }) return result } func walk(n *html.Node, fn selector) { if fn(n) { for c := n.FirstChild; c != nil; c = c.NextSibling { walk(c, fn) } } } ./cmd/godoc/0000755000014500017510000000000012246613010012331 5ustar michaelstaff./cmd/godoc/appinit.go0000644000014500017510000000313312246613010014324 0ustar michaelstaff// 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 appengine package main // This file replaces main.go when running godoc under app-engine. // See README.godoc-app for details. import ( "archive/zip" "log" "path" "code.google.com/p/go.tools/godoc" "code.google.com/p/go.tools/godoc/static" "code.google.com/p/go.tools/godoc/vfs" "code.google.com/p/go.tools/godoc/vfs/mapfs" "code.google.com/p/go.tools/godoc/vfs/zipfs" ) func init() { playEnabled = true log.Println("initializing godoc ...") log.Printf(".zip file = %s", zipFilename) log.Printf(".zip GOROOT = %s", zipGoroot) log.Printf("index files = %s", indexFilenames) goroot := path.Join("/", zipGoroot) // fsHttp paths are relative to '/' // read .zip file and set up file systems const zipfile = zipFilename rc, err := zip.OpenReader(zipfile) if err != nil { log.Fatalf("%s: %s\n", zipfile, err) } // rc is never closed (app running forever) fs.Bind("/", zipfs.New(rc, zipFilename), goroot, vfs.BindReplace) fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace) corpus := godoc.NewCorpus(fs) corpus.Verbose = false corpus.IndexEnabled = true corpus.IndexFiles = indexFilenames if err := corpus.Init(); err != nil { log.Fatal(err) } go corpus.RunIndexer() pres = godoc.NewPresentation(corpus) pres.TabWidth = 8 pres.ShowPlayground = true pres.ShowExamples = true pres.DeclLinks = true readTemplates(pres, true) registerHandlers(pres) log.Println("godoc initialization complete") } ./cmd/godoc/remotesearch.go0000644000014500017510000000277112246613010015350 0ustar michaelstaff// Copyright 2009 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 main import ( "errors" "flag" "io" "log" "net/http" "net/url" "os" ) func handleRemoteSearch() { // Command-line queries. for i := 0; i < flag.NArg(); i++ { res, err := remoteSearch(flag.Arg(i)) if err != nil { log.Fatalf("remoteSearch: %s", err) } io.Copy(os.Stdout, res.Body) } return } // remoteSearchURL returns the search URL for a given query as needed by // remoteSearch. If html is set, an html result is requested; otherwise // the result is in textual form. // Adjust this function as necessary if modeNames or FormValue parameters // change. func remoteSearchURL(query string, html bool) string { s := "/search?m=text&q=" if html { s = "/search?q=" } return s + url.QueryEscape(query) } func remoteSearch(query string) (res *http.Response, err error) { // list of addresses to try var addrs []string if *serverAddr != "" { // explicit server address - only try this one addrs = []string{*serverAddr} } else { addrs = []string{ defaultAddr, "golang.org", } } // remote search search := remoteSearchURL(query, *html) for _, addr := range addrs { url := "http://" + addr + search res, err = http.Get(url) if err == nil && res.StatusCode == http.StatusOK { break } } if err == nil && res.StatusCode != http.StatusOK { err = errors.New(res.Status) } return } ./cmd/godoc/handlers.go0000644000014500017510000000425612246613010014467 0ustar michaelstaff// Copyright 2010 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. // The /doc/codewalk/ tree is synthesized from codewalk descriptions, // files named $GOROOT/doc/codewalk/*.xml. // For an example and a description of the format, see // http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060 // and see http://localhost:6060/doc/codewalk/codewalk . // That page is itself a codewalk; the source code for it is // $GOROOT/doc/codewalk/codewalk.xml. package main import ( "log" "net/http" "text/template" "code.google.com/p/go.tools/godoc" "code.google.com/p/go.tools/godoc/redirect" "code.google.com/p/go.tools/godoc/vfs" ) var ( pres *godoc.Presentation fs = vfs.NameSpace{} ) func registerHandlers(pres *godoc.Presentation) { 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) redirect.Register(nil) } func readTemplate(name string) *template.Template { if pres == nil { panic("no global Presentation set yet") } path := "lib/godoc/" + name // use underlying file system fs to read the template file // (cannot use template ParseFile functions directly) data, err := vfs.ReadFile(fs, path) if err != nil { log.Fatal("readTemplate: ", err) } // be explicit with errors (for app engine use) t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data)) if err != nil { log.Fatal("readTemplate: ", err) } return t } func readTemplates(p *godoc.Presentation, html bool) { p.PackageText = readTemplate("package.txt") p.SearchText = readTemplate("search.txt") if html || p.HTMLMode { codewalkHTML = readTemplate("codewalk.html") codewalkdirHTML = readTemplate("codewalkdir.html") p.DirlistHTML = readTemplate("dirlist.html") p.ErrorHTML = readTemplate("error.html") p.ExampleHTML = readTemplate("example.html") p.GodocHTML = readTemplate("godoc.html") p.PackageHTML = readTemplate("package.html") p.SearchHTML = readTemplate("search.html") p.SearchDescXML = readTemplate("opensearch.xml") } } ./cmd/godoc/codewalk.go0000644000014500017510000002661112246613010014457 0ustar michaelstaff// Copyright 2010 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. // The /doc/codewalk/ tree is synthesized from codewalk descriptions, // files named $GOROOT/doc/codewalk/*.xml. // For an example and a description of the format, see // http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060 // and see http://localhost:6060/doc/codewalk/codewalk . // That page is itself a codewalk; the source code for it is // $GOROOT/doc/codewalk/codewalk.xml. package main import ( "bytes" "encoding/xml" "errors" "fmt" "io" "log" "net/http" "os" pathpkg "path" "regexp" "sort" "strconv" "strings" "text/template" "unicode/utf8" "code.google.com/p/go.tools/godoc" "code.google.com/p/go.tools/godoc/vfs" ) var codewalkHTML, codewalkdirHTML *template.Template // Handler for /doc/codewalk/ and below. func codewalk(w http.ResponseWriter, r *http.Request) { relpath := r.URL.Path[len("/doc/codewalk/"):] abspath := r.URL.Path r.ParseForm() if f := r.FormValue("fileprint"); f != "" { codewalkFileprint(w, r, f) return } // If directory exists, serve list of code walks. dir, err := fs.Lstat(abspath) if err == nil && dir.IsDir() { codewalkDir(w, r, relpath, abspath) return } // If file exists, serve using standard file server. if err == nil { pres.ServeFile(w, r) return } // Otherwise append .xml and hope to find // a codewalk description, but before trim // the trailing /. abspath = strings.TrimRight(abspath, "/") cw, err := loadCodewalk(abspath + ".xml") if err != nil { log.Print(err) pres.ServeError(w, r, relpath, err) return } // Canonicalize the path and redirect if changed if redir(w, r) { return } pres.ServePage(w, godoc.Page{ Title: "Codewalk: " + cw.Title, Tabtitle: cw.Title, Body: applyTemplate(codewalkHTML, "codewalk", cw), }) } func redir(w http.ResponseWriter, r *http.Request) (redirected bool) { canonical := pathpkg.Clean(r.URL.Path) if !strings.HasSuffix(canonical, "/") { canonical += "/" } if r.URL.Path != canonical { url := *r.URL url.Path = canonical http.Redirect(w, r, url.String(), http.StatusMovedPermanently) redirected = true } return } func applyTemplate(t *template.Template, name string, data interface{}) []byte { var buf bytes.Buffer if err := t.Execute(&buf, data); err != nil { log.Printf("%s.Execute: %s", name, err) } return buf.Bytes() } // A Codewalk represents a single codewalk read from an XML file. type Codewalk struct { Title string `xml:"title,attr"` File []string `xml:"file"` Step []*Codestep `xml:"step"` } // A Codestep is a single step in a codewalk. type Codestep struct { // Filled in from XML Src string `xml:"src,attr"` Title string `xml:"title,attr"` XML string `xml:",innerxml"` // Derived from Src; not in XML. Err error File string Lo int LoByte int Hi int HiByte int Data []byte } // String method for printing in template. // Formats file address nicely. func (st *Codestep) String() string { s := st.File if st.Lo != 0 || st.Hi != 0 { s += fmt.Sprintf(":%d", st.Lo) if st.Lo != st.Hi { s += fmt.Sprintf(",%d", st.Hi) } } return s } // loadCodewalk reads a codewalk from the named XML file. func loadCodewalk(filename string) (*Codewalk, error) { f, err := fs.Open(filename) if err != nil { return nil, err } defer f.Close() cw := new(Codewalk) d := xml.NewDecoder(f) d.Entity = xml.HTMLEntity err = d.Decode(cw) if err != nil { return nil, &os.PathError{Op: "parsing", Path: filename, Err: err} } // Compute file list, evaluate line numbers for addresses. m := make(map[string]bool) for _, st := range cw.Step { i := strings.Index(st.Src, ":") if i < 0 { i = len(st.Src) } filename := st.Src[0:i] data, err := vfs.ReadFile(fs, filename) if err != nil { st.Err = err continue } if i < len(st.Src) { lo, hi, err := addrToByteRange(st.Src[i+1:], 0, data) if err != nil { st.Err = err continue } // Expand match to line boundaries. for lo > 0 && data[lo-1] != '\n' { lo-- } for hi < len(data) && (hi == 0 || data[hi-1] != '\n') { hi++ } st.Lo = byteToLine(data, lo) st.Hi = byteToLine(data, hi-1) } st.Data = data st.File = filename m[filename] = true } // Make list of files cw.File = make([]string, len(m)) i := 0 for f := range m { cw.File[i] = f i++ } sort.Strings(cw.File) return cw, nil } // codewalkDir serves the codewalk directory listing. // It scans the directory for subdirectories or files named *.xml // and prepares a table. func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) { type elem struct { Name string Title string } dir, err := fs.ReadDir(abspath) if err != nil { log.Print(err) pres.ServeError(w, r, relpath, err) return } var v []interface{} for _, fi := range dir { name := fi.Name() if fi.IsDir() { v = append(v, &elem{name + "/", ""}) } else if strings.HasSuffix(name, ".xml") { cw, err := loadCodewalk(abspath + "/" + name) if err != nil { continue } v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title}) } } pres.ServePage(w, godoc.Page{ Title: "Codewalks", Body: applyTemplate(codewalkdirHTML, "codewalkdir", v), }) } // codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi. // The filename f has already been retrieved and is passed as an argument. // Lo and hi are the numbers of the first and last line to highlight // in the response. This format is used for the middle window pane // of the codewalk pages. It is a separate iframe and does not get // the usual godoc HTML wrapper. func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { abspath := f data, err := vfs.ReadFile(fs, abspath) if err != nil { log.Print(err) pres.ServeError(w, r, f, err) return } lo, _ := strconv.Atoi(r.FormValue("lo")) hi, _ := strconv.Atoi(r.FormValue("hi")) if hi < lo { hi = lo } lo = lineToByte(data, lo) hi = lineToByte(data, hi+1) // Put the mark 4 lines before lo, so that the iframe // shows a few lines of context before the highlighted // section. n := 4 mark := lo for ; mark > 0 && n > 0; mark-- { if data[mark-1] == '\n' { if n--; n == 0 { break } } } io.WriteString(w, `
`)
	template.HTMLEscape(w, data[0:mark])
	io.WriteString(w, "")
	template.HTMLEscape(w, data[mark:lo])
	if lo < hi {
		io.WriteString(w, "
") template.HTMLEscape(w, data[lo:hi]) io.WriteString(w, "
") } template.HTMLEscape(w, data[hi:]) io.WriteString(w, "
") } // addrToByte evaluates the given address starting at offset start in data. // It returns the lo and hi byte offset of the matched region within data. // See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II // for details on the syntax. func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) { var ( dir byte prevc byte charOffset bool ) lo = start hi = start for addr != "" && err == nil { c := addr[0] switch c { default: err = errors.New("invalid address syntax near " + string(c)) case ',': if len(addr) == 1 { hi = len(data) } else { _, hi, err = addrToByteRange(addr[1:], hi, data) } return case '+', '-': if prevc == '+' || prevc == '-' { lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset) } dir = c case '$': lo = len(data) hi = len(data) if len(addr) > 1 { dir = '+' } case '#': charOffset = true case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': var i int for i = 1; i < len(addr); i++ { if addr[i] < '0' || addr[i] > '9' { break } } var n int n, err = strconv.Atoi(addr[0:i]) if err != nil { break } lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset) dir = 0 charOffset = false prevc = c addr = addr[i:] continue case '/': var i, j int Regexp: for i = 1; i < len(addr); i++ { switch addr[i] { case '\\': i++ case '/': j = i + 1 break Regexp } } if j == 0 { j = i } pattern := addr[1:i] lo, hi, err = addrRegexp(data, lo, hi, dir, pattern) prevc = c addr = addr[j:] continue } prevc = c addr = addr[1:] } if err == nil && dir != 0 { lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset) } if err != nil { return 0, 0, err } return lo, hi, nil } // addrNumber applies the given dir, n, and charOffset to the address lo, hi. // dir is '+' or '-', n is the count, and charOffset is true if the syntax // used was #n. Applying +n (or +#n) means to advance n lines // (or characters) after hi. Applying -n (or -#n) means to back up n lines // (or characters) before lo. // The return value is the new lo, hi. func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) { switch dir { case 0: lo = 0 hi = 0 fallthrough case '+': if charOffset { pos := hi for ; n > 0 && pos < len(data); n-- { _, size := utf8.DecodeRune(data[pos:]) pos += size } if n == 0 { return pos, pos, nil } break } // find next beginning of line if hi > 0 { for hi < len(data) && data[hi-1] != '\n' { hi++ } } lo = hi if n == 0 { return lo, hi, nil } for ; hi < len(data); hi++ { if data[hi] != '\n' { continue } switch n--; n { case 1: lo = hi + 1 case 0: return lo, hi + 1, nil } } case '-': if charOffset { // Scan backward for bytes that are not UTF-8 continuation bytes. pos := lo for ; pos > 0 && n > 0; pos-- { if data[pos]&0xc0 != 0x80 { n-- } } if n == 0 { return pos, pos, nil } break } // find earlier beginning of line for lo > 0 && data[lo-1] != '\n' { lo-- } hi = lo if n == 0 { return lo, hi, nil } for ; lo >= 0; lo-- { if lo > 0 && data[lo-1] != '\n' { continue } switch n--; n { case 1: hi = lo case 0: return lo, hi, nil } } } return 0, 0, errors.New("address out of range") } // addrRegexp searches for pattern in the given direction starting at lo, hi. // The direction dir is '+' (search forward from hi) or '-' (search backward from lo). // Backward searches are unimplemented. func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) { re, err := regexp.Compile(pattern) if err != nil { return 0, 0, err } if dir == '-' { // Could implement reverse search using binary search // through file, but that seems like overkill. return 0, 0, errors.New("reverse search not implemented") } m := re.FindIndex(data[hi:]) if len(m) > 0 { m[0] += hi m[1] += hi } else if hi > 0 { // No match. Wrap to beginning of data. m = re.FindIndex(data) } if len(m) == 0 { return 0, 0, errors.New("no match for " + pattern) } return m[0], m[1], nil } // lineToByte returns the byte index of the first byte of line n. // Line numbers begin at 1. func lineToByte(data []byte, n int) int { if n <= 1 { return 0 } n-- for i, c := range data { if c == '\n' { if n--; n == 0 { return i + 1 } } } return len(data) } // byteToLine returns the number of the line containing the byte at index i. func byteToLine(data []byte, i int) int { l := 1 for j, c := range data { if j == i { return l } if c == '\n' { l++ } } return l } ./cmd/godoc/doc.go0000644000014500017510000001242612246613010013432 0ustar michaelstaff// Copyright 2009 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. /* Godoc extracts and generates documentation for Go programs. It has two modes. Without the -http flag, it runs in command-line mode and prints plain text documentation to standard output and exits. If both a library package and a command with the same name exists, using the prefix cmd/ will force documentation on the command rather than the library package. If the -src flag is specified, godoc prints the exported interface of a package in Go source form, or the implementation of a specific exported language entity: godoc fmt # documentation for package fmt godoc fmt Printf # documentation for fmt.Printf godoc cmd/go # force documentation for the go command godoc -src fmt # fmt package interface in Go source form godoc -src fmt Printf # implementation of fmt.Printf In command-line mode, the -q flag enables search queries against a godoc running as a webserver. If no explicit server address is specified with the -server flag, godoc first tries localhost:6060 and then http://golang.org. godoc -q Reader godoc -q math.Sin godoc -server=:6060 -q sin With the -http flag, it runs as a web server and presents the documentation as a web page. godoc -http=:6060 Usage: godoc [flag] package [name ...] The flags are: -v verbose mode -q arguments are considered search queries: a legal query is a single identifier (such as ToLower) or a qualified identifier (such as math.Sin). -src print (exported) source in command-line mode -tabwidth=4 width of tabs in units of spaces -timestamps=true show timestamps with directory listings -index enable identifier and full text search index (no search box is shown if -index is not set) -index_files="" glob pattern specifying index files; if not empty, the index is read from these files in sorted order -index_throttle=0.75 index throttle value; a value of 0 means no time is allocated to the indexer (the indexer will never finish), a value of 1.0 means that index creation is running at full throttle (other goroutines may get no time while the index is built) -links=true: link identifiers to their declarations -write_index=false write index to a file; the file name must be specified with -index_files -maxresults=10000 maximum number of full text search results shown (no full text index is built if maxresults <= 0) -notes="BUG" regular expression matching note markers to show (e.g., "BUG|TODO", ".*") -html print HTML in command-line mode -goroot=$GOROOT Go root directory -http=addr HTTP service address (e.g., '127.0.0.1:6060' or just ':6060') -server=addr webserver address for command line searches -templates="" directory containing alternate template files; if set, the directory may provide alternative template files for the files in $GOROOT/lib/godoc -url=path print to standard output the data that would be served by an HTTP request for path -zip="" zip file providing the file system to serve; disabled if empty By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set). This behavior can be altered by providing an alternative $GOROOT with the -goroot flag. When godoc runs as a web server and -index is set, a search index is maintained. The index is created at startup. The index contains both identifier and full text search information (searchable via regular expressions). The maximum number of full text search results shown can be set with the -maxresults flag; if set to 0, no full text results are shown, and only an identifier index but no full text search index is created. The presentation mode of web pages served by godoc can be controlled with the "m" URL parameter; it accepts a comma-separated list of flag names as value: all show documentation for all declarations, not just the exported ones methods show all embedded methods, not just those of unexported anonymous fields src show the original source code rather then the extracted documentation text present the page in textual (command-line) form rather than HTML flat present flat (not indented) directory listings using full paths For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation for all (not just the exported) declarations of package big, in textual form (as it would appear when using godoc from the command line: "godoc -src math/big .*"). By default, godoc serves files from the file system of the underlying OS. Instead, a .zip file may be provided via the -zip flag, which contains the file system to serve. The file paths stored in the .zip file must use slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot) must be set to the .zip file directory path containing the Go root directory. For instance, for a .zip file created by the command: zip go.zip $HOME/go one may run godoc as follows: godoc -http=:6060 -zip=go.zip -goroot=$HOME/go Godoc documentation is converted to HTML or to text using the go/doc package; see http://golang.org/pkg/go/doc/#ToHTML for the exact rules. See "Godoc: documenting Go code" for how to write good comments for godoc: http://golang.org/doc/articles/godoc_documenting_go_code.html */ package main ./cmd/godoc/godoc_test.go0000644000014500017510000000270512246613010015016 0ustar michaelstaff// 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 main_test import ( "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "runtime" "testing" ) var godocTests = []struct { args []string matches []string // regular expressions }{ { []string{"fmt"}, []string{ `import "fmt"`, `Package fmt implements formatted I/O`, }, }, { []string{"io", "WriteString"}, []string{ `import "io"`, `func WriteString\(`, }, }, } // Basic regression test for godoc command-line tool. func TestGodoc(t *testing.T) { tmp, err := ioutil.TempDir("", "godoc-regtest-") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) bin := filepath.Join(tmp, "godoc") if runtime.GOOS == "windows" { bin += ".exe" } cmd := exec.Command("go", "build", "-o", bin) if err := cmd.Run(); err != nil { t.Fatalf("Building godoc: %v", err) } for _, test := range godocTests { cmd := exec.Command(bin, test.args...) cmd.Args[0] = "godoc" out, err := cmd.CombinedOutput() if err != nil { t.Errorf("Running with args %#v: %v", test.args, err) continue } logged := false for _, pat := range test.matches { re := regexp.MustCompile(pat) if !re.Match(out) { if !logged { t.Logf("Output of running with args %#v:\n%s", test.args, out) logged = true } t.Errorf("Did not match /%v/", pat) } } } } ./cmd/godoc/main.go0000644000014500017510000002031312246613010013603 0ustar michaelstaff// Copyright 2009 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. // godoc: Go Documentation Server // Web server tree: // // http://godoc/ main landing page // http://godoc/doc/ serve from $GOROOT/doc - spec, mem, etc. // http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed // http://godoc/cmd/ serve documentation about commands // http://godoc/pkg/ serve documentation about packages // (idea is if you say import "compress/zlib", you go to // http://godoc/pkg/compress/zlib) // // Command-line interface: // // godoc packagepath [name ...] // // godoc compress/zlib // - prints doc for package compress/zlib // godoc crypto/block Cipher NewCMAC // - prints doc for Cipher and NewCMAC in package crypto/block // +build !appengine package main import ( "archive/zip" _ "expvar" // to serve /debug/vars "flag" "fmt" "go/build" "log" "net/http" "net/http/httptest" _ "net/http/pprof" // to serve /debug/pprof/* "net/url" "os" "path/filepath" "regexp" "runtime" "code.google.com/p/go.tools/godoc" "code.google.com/p/go.tools/godoc/static" "code.google.com/p/go.tools/godoc/vfs" "code.google.com/p/go.tools/godoc/vfs/mapfs" "code.google.com/p/go.tools/godoc/vfs/zipfs" ) const ( defaultAddr = ":6060" // default webserver address toolsPath = "code.google.com/p/go.tools/cmd/" ) var ( // file system to serve // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico) zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty") // file-based index writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files") // network httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')") serverAddr = flag.String("server", "", "webserver address for command line searches") // layout control html = flag.Bool("html", false, "print HTML in command-line mode") srcMode = flag.Bool("src", false, "print (exported) source in command-line mode") urlFlag = flag.String("url", "", "print HTML for named URL") // command-line searches query = flag.Bool("q", false, "arguments are considered search queries") verbose = flag.Bool("v", false, "verbose mode") // file system roots // TODO(gri) consider the invariant that goroot always end in '/' goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") // layout control tabWidth = flag.Int("tabwidth", 4, "tab width") showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") templateDir = flag.String("templates", "", "directory containing alternate template files") showPlayground = flag.Bool("play", false, "enable playground in web interface") showExamples = flag.Bool("ex", false, "show examples in command line mode") declLinks = flag.Bool("links", true, "link identifiers to their declarations") // search index indexEnabled = flag.Bool("index", false, "enable search index") indexFiles = flag.String("index_files", "", "glob pattern specifying index files;"+ "if not empty, the index is read from these files in sorted order") maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle") // source code notes notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show") ) func usage() { fmt.Fprintf(os.Stderr, "usage: godoc package [name ...]\n"+ " godoc -http="+defaultAddr+"\n") flag.PrintDefaults() os.Exit(2) } func loggingHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { log.Printf("%s\t%s", req.RemoteAddr, req.URL) h.ServeHTTP(w, req) }) } func handleURLFlag() { // Try up to 10 fetches, following redirects. urlstr := *urlFlag for i := 0; i < 10; i++ { // Prepare request. u, err := url.Parse(urlstr) if err != nil { log.Fatal(err) } req := &http.Request{ URL: u, } // Invoke default HTTP handler to serve request // to our buffering httpWriter. w := httptest.NewRecorder() http.DefaultServeMux.ServeHTTP(w, req) // Return data, error, or follow redirect. switch w.Code { case 200: // ok os.Stdout.Write(w.Body.Bytes()) return case 301, 302, 303, 307: // redirect redirect := w.HeaderMap.Get("Location") if redirect == "" { log.Fatalf("HTTP %d without Location header", w.Code) } urlstr = redirect default: log.Fatalf("HTTP error %d", w.Code) } } log.Fatalf("too many redirects") } func main() { flag.Usage = usage flag.Parse() playEnabled = *showPlayground // Check usage: either server and no args, command line and args, or index creation mode if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex { usage() } // Determine file system to use. if *zipfile == "" { // use file system of underlying OS fs.Bind("/", vfs.OS(*goroot), "/", vfs.BindReplace) } else { // use file system specified via .zip file (path separator must be '/') rc, err := zip.OpenReader(*zipfile) if err != nil { log.Fatalf("%s: %s\n", *zipfile, err) } defer rc.Close() // be nice (e.g., -writeIndex mode) fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace) } if *templateDir != "" { fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore) } else { fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace) } // Bind $GOPATH trees into Go root. for _, p := range filepath.SplitList(build.Default.GOPATH) { fs.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter) } httpMode := *httpAddr != "" corpus := godoc.NewCorpus(fs) corpus.Verbose = *verbose corpus.MaxResults = *maxResults corpus.IndexEnabled = *indexEnabled && httpMode if *maxResults == 0 { corpus.IndexFullText = false } corpus.IndexFiles = *indexFiles corpus.IndexThrottle = *indexThrottle if *writeIndex { corpus.IndexThrottle = 1.0 } if *writeIndex || httpMode || *urlFlag != "" { if err := corpus.Init(); err != nil { log.Fatal(err) } } pres = godoc.NewPresentation(corpus) pres.TabWidth = *tabWidth pres.ShowTimestamps = *showTimestamps pres.ShowPlayground = *showPlayground pres.ShowExamples = *showExamples pres.DeclLinks = *declLinks pres.SrcMode = *srcMode pres.HTMLMode = *html if *notesRx != "" { pres.NotesRx = regexp.MustCompile(*notesRx) } readTemplates(pres, httpMode || *urlFlag != "") registerHandlers(pres) if *writeIndex { // Write search index and exit. if *indexFiles == "" { log.Fatal("no index file specified") } log.Println("initialize file systems") *verbose = true // want to see what happens corpus.UpdateIndex() log.Println("writing index file", *indexFiles) f, err := os.Create(*indexFiles) if err != nil { log.Fatal(err) } index, _ := corpus.CurrentIndex() _, err = index.WriteTo(f) if err != nil { log.Fatal(err) } log.Println("done") return } // Print content that would be served at the URL *urlFlag. if *urlFlag != "" { handleURLFlag() return } if httpMode { // HTTP server mode. var handler http.Handler = http.DefaultServeMux if *verbose { log.Printf("Go Documentation Server") log.Printf("version = %s", runtime.Version()) log.Printf("address = %s", *httpAddr) log.Printf("goroot = %s", *goroot) log.Printf("tabwidth = %d", *tabWidth) switch { case !*indexEnabled: log.Print("search index disabled") case *maxResults > 0: log.Printf("full text index enabled (maxresults = %d)", *maxResults) default: log.Print("identifier search index enabled") } fs.Fprint(os.Stderr) handler = loggingHandler(handler) } // Initialize search index. if *indexEnabled { go corpus.RunIndexer() } // Start http server. if err := http.ListenAndServe(*httpAddr, handler); err != nil { log.Fatalf("ListenAndServe %s: %v", *httpAddr, err) } return } if *query { handleRemoteSearch() return } if err := godoc.CommandLine(os.Stdout, fs, pres, flag.Args()); err != nil { log.Print(err) } } ./cmd/godoc/setup-godoc-app.bash0000755000014500017510000000604212246613010016204 0ustar michaelstaff#!/usr/bin/env bash # 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 script creates a complete godoc app in $APPDIR. # It copies the cmd/godoc and src/pkg/go/... sources from GOROOT, # synthesizes an app.yaml file, and creates the .zip, index, and # configuration files. # # If an argument is provided it is assumed to be the app-engine godoc directory. # Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env" # is consulted to find the $GOROOT. # # The script creates a .zip file representing the $GOROOT file system # and computes the correspondig search index files. These files are then # copied to $APPDIR. A corresponding godoc configuration file is created # in $APPDIR/appconfig.go. ZIPFILE=godoc.zip INDEXFILE=godoc.index SPLITFILES=index.split. GODOC=code.google.com/p/go.tools/cmd/godoc CONFIGFILE=$GODOC/appconfig.go error() { echo "error: $1" exit 2 } getArgs() { if [ -z $APPENGINE_SDK ]; then error "APPENGINE_SDK environment variable not set" fi if [ ! -x $APPENGINE_SDK/go ]; then error "couldn't find go comment in $APPENGINE_SDK" fi if [ -z $GOROOT ]; then GOROOT=$(go env GOROOT) echo "GOROOT not set explicitly, using go env value instead" fi if [ -z $APPDIR ]; then if [ $# == 0 ]; then error "APPDIR not set, and no argument provided" fi APPDIR=$1 echo "APPDIR not set, using argument instead" fi # safety checks if [ ! -d $GOROOT ]; then error "$GOROOT is not a directory" fi if [ -e $APPDIR ]; then error "$APPDIR exists; check and remove it before trying again" fi # reporting echo "GOROOT = $GOROOT" echo "APPDIR = $APPDIR" } fetchGodoc() { echo "*** Fetching godoc (if not already in GOPATH)" unset GOBIN go=$APPENGINE_SDK/go $go get -d -tags appengine $GODOC mkdir -p $APPDIR/$GODOC cp $(find $($go list -f '{{.Dir}}' $GODOC) -type f -depth 1) $APPDIR/$GODOC/ } makeAppYaml() { echo "*** make $APPDIR/app.yaml" cat > $APPDIR/app.yaml < $APPDIR/$CONFIGFILE </dev_appserver.py $APPDIR godoc should come up at http://localhost:8080 . ./cmd/godoc/blog.go0000644000014500017510000000360212246613010013604 0ustar michaelstaff// 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 main import ( "fmt" "go/build" "log" "net/http" "os" "path/filepath" "runtime" "strings" "sync" "code.google.com/p/go.tools/blog" "code.google.com/p/go.tools/godoc/redirect" ) const ( blogRepo = "code.google.com/p/go.blog" blogURL = "http://blog.golang.org/" blogPath = "/blog/" ) var ( blogServer http.Handler // set by blogInit blogInitOnce sync.Once playEnabled bool ) func init() { // Initialize blog only when first accessed. http.HandleFunc(blogPath, func(w http.ResponseWriter, r *http.Request) { blogInitOnce.Do(blogInit) blogServer.ServeHTTP(w, r) }) } func blogInit() { // Binary distributions will include the blog content in "/blog". root := filepath.Join(runtime.GOROOT(), "blog") // Prefer content from go.blog repository if present. if pkg, err := build.Import(blogRepo, "", build.FindOnly); err == nil { root = pkg.Dir } // If content is not available fall back to redirect. if fi, err := os.Stat(root); err != nil || !fi.IsDir() { fmt.Fprintf(os.Stderr, "Blog content not available locally. "+ "To install, run \n\tgo get %v\n", blogRepo) blogServer = http.HandlerFunc(blogRedirectHandler) return } s, err := blog.NewServer(blog.Config{ BaseURL: blogPath, BasePath: strings.TrimSuffix(blogPath, "/"), ContentPath: filepath.Join(root, "content"), TemplatePath: filepath.Join(root, "template"), HomeArticles: 5, PlayEnabled: playEnabled, }) if err != nil { log.Fatal(err) } blogServer = s } func blogRedirectHandler(w http.ResponseWriter, r *http.Request) { if r.URL.Path == blogPath { http.Redirect(w, r, blogURL, http.StatusFound) return } blogPrefixHandler.ServeHTTP(w, r) } var blogPrefixHandler = redirect.PrefixHandler(blogPath, blogURL) ./cmd/ssadump/0000755000014500017510000000000012246613010012712 5ustar michaelstaff./cmd/ssadump/main.go0000644000014500017510000001075112246613010014171 0ustar michaelstaff// 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. // ssadump: a tool for displaying and interpreting the SSA form of Go programs. package main import ( "flag" "fmt" "go/build" "log" "os" "runtime" "runtime/pprof" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa/interp" ) var buildFlag = flag.String("build", "", `Options controlling the SSA builder. The value is a sequence of zero or more of these letters: C perform sanity [C]hecking of the SSA form. D include [D]ebug info for every function. P log [P]ackage inventory. F log [F]unction SSA code. S log [S]ource locations as SSA builder progresses. G use binary object files from gc to provide imports (no code). L build distinct packages seria[L]ly instead of in parallel. N build [N]aive SSA form: don't replace local loads/stores with registers. `) var runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.") var 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 [ ...] ... Use -help flag to display options. Examples: % ssadump -build=FPG hello.go # quickly dump SSA form of a single package % ssadump -run -interp=T hello.go # interpret a program, with tracing % ssadump -run unicode -- -test.v # interpret the unicode package's tests, verbosely ` + importer.InitialPackagesUsage + ` When -run is specified, ssadump will find the first package that defines a main function and run it in the interpreter. If none is found, the tests of each package will be run instead. ` var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") func init() { // 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() { flag.Parse() args := flag.Args() impctx := importer.Config{Build: &build.Default} var debugMode bool var mode ssa.BuilderMode for _, c := range *buildFlag { switch c { case 'D': debugMode = true case 'P': mode |= ssa.LogPackages | ssa.BuildSerially case 'F': mode |= ssa.LogFunctions | ssa.BuildSerially case 'S': mode |= ssa.LogSource | ssa.BuildSerially case 'C': mode |= ssa.SanityCheckFunctions case 'N': mode |= ssa.NaiveForm case 'G': impctx.Build = nil case 'L': mode |= ssa.BuildSerially default: log.Fatalf("Unknown -build option: '%c'.", c) } } var interpMode interp.Mode for _, c := range *interpFlag { switch c { case 'T': interpMode |= interp.EnableTracing case 'R': interpMode |= interp.DisableRecover default: log.Fatalf("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 { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // Load, parse and type-check the program. imp := importer.New(&impctx) infos, args, err := imp.LoadInitialPackages(args) if err != nil { log.Fatal(err) } // The interpreter needs the runtime package. if *runFlag { if _, err := imp.LoadPackage("runtime"); err != nil { log.Fatalf("LoadPackage(runtime) failed: %s", err) } } // Create and build SSA-form program representation. prog := ssa.NewProgram(imp.Fset, mode) if err := prog.CreatePackages(imp); err != nil { log.Fatal(err) } if debugMode { for _, pkg := range prog.AllPackages() { pkg.SetDebugMode(true) } } prog.BuildAll() // Run the interpreter. if *runFlag { // If some package defines main, run that. // Otherwise run all package's tests. var main *ssa.Package var pkgs []*ssa.Package for _, info := range infos { pkg := prog.Package(info.Pkg) if pkg.Func("main") != nil { main = pkg break } pkgs = append(pkgs, pkg) } if main == nil && pkgs != nil { main = prog.CreateTestMainPackage(pkgs...) } if main == nil { log.Fatal("No main package and no tests") } interp.Interpret(main, interpMode, main.Object.Path(), args) } } ./cmd/cover/0000755000014500017510000000000012246613010012354 5ustar michaelstaff./cmd/cover/cover_test.go0000644000014500017510000000433112246613010015061 0ustar michaelstaff// 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 main_test import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "testing" ) const ( // Data directory, also the package directory for the test. testdata = "testdata" // Binaries we compile. testcover = "./testcover.exe" ) var ( // Files we use. testMain = filepath.Join(testdata, "main.go") testTest = filepath.Join(testdata, "test.go") coverInput = filepath.Join(testdata, "test_line.go") coverOutput = filepath.Join(testdata, "test_cover.go") ) var debug = false // Keeps the rewritten files around if set. // Run this shell script, but do it in Go so it can be run by "go test". // // replace the word LINE with the line number < testdata/test.go > testdata/test_line.go // go build -o ./testcover // ./testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go // go run ./testdata/main.go ./testdata/test.go // func TestCover(t *testing.T) { // Read in the test file (testTest) and write it, with LINEs specified, to coverInput. file, err := ioutil.ReadFile(testTest) if err != nil { t.Fatal(err) } lines := bytes.Split(file, []byte("\n")) for i, line := range lines { lines[i] = bytes.Replace(line, []byte("LINE"), []byte(fmt.Sprint(i+1)), -1) } err = ioutil.WriteFile(coverInput, bytes.Join(lines, []byte("\n")), 0666) // defer removal of test_line.go if !debug { defer os.Remove(coverInput) } // go build -o testcover cmd := exec.Command("go", "build", "-o", testcover) run(cmd, t) // defer removal of testcover defer os.Remove(testcover) // ./testcover -mode=count -var=coverTest -o ./testdata/test_cover.go testdata/test_line.go cmd = exec.Command(testcover, "-mode=count", "-var=coverTest", "-o", coverOutput, coverInput) run(cmd, t) // defer removal of ./testdata/test_cover.go if !debug { defer os.Remove(coverOutput) } // go run ./testdata/main.go ./testdata/test.go cmd = exec.Command("go", "run", testMain, coverOutput) run(cmd, t) } func run(c *exec.Cmd, t *testing.T) { c.Stdout = os.Stdout c.Stderr = os.Stderr err := c.Run() if err != nil { t.Fatal(err) } } ./cmd/cover/func.go0000644000014500017510000000774512246613010013653 0ustar michaelstaff// 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. // This file implements the visitor that computes the (line, column)-(line-column) range for each function. package main import ( "bufio" "fmt" "go/ast" "go/parser" "go/token" "os" "text/tabwriter" ) // funcOutput takes two file names as arguments, a coverage profile to read as input and an output // file to write ("" means to write to standard output). The function reads the profile and produces // as output the coverage data broken down by function, like this: // // fmt/format.go: init 100.0% // fmt/format.go: computePadding 84.6% // ... // fmt/scan.go: doScan 100.0% // fmt/scan.go: advance 96.2% // fmt/scan.go: doScanf 96.8% // total: (statements) 91.4% func funcOutput(profile, outputFile string) error { profiles, err := ParseProfiles(profile) if err != nil { return err } var out *bufio.Writer if outputFile == "" { out = bufio.NewWriter(os.Stdout) } else { fd, err := os.Create(outputFile) if err != nil { return err } defer fd.Close() out = bufio.NewWriter(fd) } defer out.Flush() tabber := tabwriter.NewWriter(out, 1, 8, 1, '\t', 0) defer tabber.Flush() var total, covered int64 for _, profile := range profiles { fn := profile.FileName file, err := findFile(fn) if err != nil { return err } funcs, err := findFuncs(file) if err != nil { return err } // Now match up functions and profile blocks. for _, f := range funcs { c, t := f.coverage(profile) fmt.Fprintf(tabber, "%s:\t%s\t%.1f%%\n", fn, f.name, 100.0*float64(c)/float64(t)) total += t covered += c } } fmt.Fprintf(tabber, "total:\t(statements)\t%.1f%%\n", 100.0*float64(covered)/float64(total)) return nil } // findFuncs parses the file and returns a slice of FuncExtent descriptors. func findFuncs(name string) ([]*FuncExtent, error) { fset := token.NewFileSet() parsedFile, err := parser.ParseFile(fset, name, nil, 0) if err != nil { return nil, err } visitor := &FuncVisitor{ fset: fset, name: name, astFile: parsedFile, } ast.Walk(visitor, visitor.astFile) return visitor.funcs, nil } // FuncExtent describes a function's extent in the source by file and position. type FuncExtent struct { name string startLine int startCol int endLine int endCol int } // FuncVisitor implements the visitor that builds the function position list for a file. type FuncVisitor struct { fset *token.FileSet name string // Name of file. astFile *ast.File funcs []*FuncExtent } // Visit implements the ast.Visitor interface. func (v *FuncVisitor) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.FuncDecl: start := v.fset.Position(n.Pos()) end := v.fset.Position(n.End()) fe := &FuncExtent{ name: n.Name.Name, startLine: start.Line, startCol: start.Column, endLine: end.Line, endCol: end.Column, } v.funcs = append(v.funcs, fe) } return v } // coverage returns the fraction of the statements in the function that were covered, as a numerator and denominator. func (f *FuncExtent) coverage(profile *Profile) (num, den int64) { // We could avoid making this n^2 overall by doing a single scan and annotating the functions, // but the sizes of the data structures is never very large and the scan is almost instantaneous. var covered, total int64 // The blocks are sorted, so we can stop counting as soon as we reach the end of the relevant block. for _, b := range profile.Blocks { if b.StartLine > f.endLine || (b.StartLine == f.endLine && b.StartCol >= f.endCol) { // Past the end of the function. break } if b.EndLine < f.startLine || (b.EndLine == f.startLine && b.EndCol <= f.startCol) { // Before the beginning of the function continue } total += int64(b.NumStmt) if b.Count > 0 { covered += int64(b.NumStmt) } } if total == 0 { total = 1 // Avoid zero denominator. } return covered, total } ./cmd/cover/doc.go0000644000014500017510000000151612246613010013453 0ustar michaelstaff// 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. /* Cover is a program for analyzing the coverage profiles generated by 'go test -coverprofile=cover.out'. Cover is also used by 'go test -cover' to rewrite the source code with annotations to track which parts of each function are executed. It operates on one Go source file at a time, computing approximate basic block information by studying the source. It is thus more portable than binary-rewriting coverage tools, but also a little less capable. For instance, it does not probe inside && and || expressions, and can be mildly confused by single statements with multiple function literals. For usage information, please see: go help testflag go tool cover -help */ package main ./cmd/cover/html.go0000644000014500017510000001314112246613010013647 0ustar michaelstaff// 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 main import ( "bufio" "bytes" "fmt" "html/template" "io" "io/ioutil" "math" "os" "os/exec" "path/filepath" "runtime" ) // htmlOutput reads the profile data from profile and generates an HTML // coverage report, writing it to outfile. If outfile is empty, // it writes the report to a temporary file and opens it in a web browser. func htmlOutput(profile, outfile string) error { profiles, err := ParseProfiles(profile) if err != nil { return err } var d templateData for _, profile := range profiles { fn := profile.FileName if profile.Mode == "set" { d.Set = true } file, err := findFile(fn) if err != nil { return err } src, err := ioutil.ReadFile(file) if err != nil { return fmt.Errorf("can't read %q: %v", fn, err) } var buf bytes.Buffer err = htmlGen(&buf, src, profile.Boundaries(src)) if err != nil { return err } d.Files = append(d.Files, &templateFile{ Name: fn, Body: template.HTML(buf.String()), }) } var out *os.File if outfile == "" { var dir string dir, err = ioutil.TempDir("", "cover") if err != nil { return err } out, err = os.Create(filepath.Join(dir, "coverage.html")) } else { out, err = os.Create(outfile) } err = htmlTemplate.Execute(out, d) if err == nil { err = out.Close() } if err != nil { return err } if outfile == "" { if !startBrowser("file://" + out.Name()) { fmt.Fprintf(os.Stderr, "HTML output written to %s\n", out.Name()) } } return nil } // htmlGen generates an HTML coverage report with the provided filename, // source code, and tokens, and writes it to the given Writer. func htmlGen(w io.Writer, src []byte, boundaries []Boundary) error { dst := bufio.NewWriter(w) for i := range src { for len(boundaries) > 0 && boundaries[0].Offset == i { b := boundaries[0] if b.Start { n := 0 if b.Count > 0 { n = int(math.Floor(b.Norm*9)) + 1 } fmt.Fprintf(dst, ``, n, b.Count) } else { dst.WriteString("") } boundaries = boundaries[1:] } switch b := src[i]; b { case '>': dst.WriteString(">") case '<': dst.WriteString("<") case '&': dst.WriteString("&") case '\t': dst.WriteString(" ") default: dst.WriteByte(b) } } return dst.Flush() } // startBrowser tries to open the URL in a browser // and reports whether it succeeds. func startBrowser(url string) bool { // try to start the browser var args []string switch runtime.GOOS { case "darwin": args = []string{"open"} case "windows": args = []string{"cmd", "/c", "start"} default: args = []string{"xdg-open"} } cmd := exec.Command(args[0], append(args[1:], url)...) return cmd.Start() == nil } // rgb returns an rgb value for the specified coverage value // between 0 (no coverage) and 10 (max coverage). func rgb(n int) string { if n == 0 { return "rgb(192, 0, 0)" // Red } // Gradient from gray to green. r := 128 - 12*(n-1) g := 128 + 12*(n-1) b := 128 + 3*(n-1) return fmt.Sprintf("rgb(%v, %v, %v)", r, g, b) } // colors generates the CSS rules for coverage colors. func colors() template.CSS { var buf bytes.Buffer for i := 0; i < 11; i++ { fmt.Fprintf(&buf, ".cov%v { color: %v }\n", i, rgb(i)) } return template.CSS(buf.String()) } var htmlTemplate = template.Must(template.New("html").Funcs(template.FuncMap{ "colors": colors, }).Parse(tmplHTML)) type templateData struct { Files []*templateFile Set bool } type templateFile struct { Name string Body template.HTML } const tmplHTML = `
not tracked {{if .Set}} not covered covered {{else}} no coverage low coverage * * * * * * * * high coverage {{end}}
{{range $i, $f := .Files}}
{{$f.Body}}
{{end}}
` ./cmd/cover/profile.go0000644000014500017510000001244712246613010014353 0ustar michaelstaff// 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 main import ( "bufio" "fmt" "go/build" "math" "os" "path/filepath" "regexp" "sort" "strconv" "strings" ) // Profile represents the profiling data for a specific file. type Profile struct { FileName string Mode string Blocks []ProfileBlock } // ProfileBlock represents a single block of profiling data. type ProfileBlock struct { StartLine, StartCol int EndLine, EndCol int NumStmt, Count int } type byFileName []*Profile func (p byFileName) Len() int { return len(p) } func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // ParseProfiles parses profile data from the given Reader and returns a // Profile for each file. func ParseProfiles(fileName string) ([]*Profile, error) { pf, err := os.Open(profile) if err != nil { return nil, err } defer pf.Close() files := make(map[string]*Profile) buf := bufio.NewReader(pf) // First line is "mode: foo", where foo is "set", "count", or "atomic". // Rest of file is in the format // encoding/base64/base64.go:34.44,37.40 3 1 // where the fields are: name.go:line.column,line.column numberOfStatements count s := bufio.NewScanner(buf) mode := "" for s.Scan() { line := s.Text() if mode == "" { const p = "mode: " if !strings.HasPrefix(line, p) || line == p { return nil, fmt.Errorf("bad mode line: %v", line) } mode = line[len(p):] continue } m := lineRe.FindStringSubmatch(line) if m == nil { return nil, fmt.Errorf("line %q doesn't match expected format: %v", m, lineRe) } fn := m[1] p := files[fn] if p == nil { p = &Profile{ FileName: fn, Mode: mode, } files[fn] = p } p.Blocks = append(p.Blocks, ProfileBlock{ StartLine: toInt(m[2]), StartCol: toInt(m[3]), EndLine: toInt(m[4]), EndCol: toInt(m[5]), NumStmt: toInt(m[6]), Count: toInt(m[7]), }) } if err := s.Err(); err != nil { return nil, err } for _, p := range files { sort.Sort(blocksByStart(p.Blocks)) } // Generate a sorted slice. profiles := make([]*Profile, 0, len(files)) for _, profile := range files { profiles = append(profiles, profile) } sort.Sort(byFileName(profiles)) return profiles, nil } type blocksByStart []ProfileBlock func (b blocksByStart) Len() int { return len(b) } func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b blocksByStart) Less(i, j int) bool { bi, bj := b[i], b[j] return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol } var lineRe = regexp.MustCompile(`^(.+):([0-9]+).([0-9]+),([0-9]+).([0-9]+) ([0-9]+) ([0-9]+)$`) func toInt(s string) int { i, err := strconv.Atoi(s) if err != nil { panic(err) } return i } // Boundary represents the position in a source file of the beginning or end of a // block as reported by the coverage profile. In HTML mode, it will correspond to // the opening or closing of a tag and will be used to colorize the source type Boundary struct { Offset int // Location as a byte offset in the source file. Start bool // Is this the start of a block? Count int // Event count from the cover profile. Norm float64 // Count normalized to [0..1]. } // Boundaries returns a Profile as a set of Boundary objects within the provided src. func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) { // Find maximum count. max := 0 for _, b := range p.Blocks { if b.Count > max { max = b.Count } } // Divisor for normalization. divisor := math.Log(float64(max)) // boundary returns a Boundary, populating the Norm field with a normalized Count. boundary := func(offset int, start bool, count int) Boundary { b := Boundary{Offset: offset, Start: start, Count: count} if !start || count == 0 { return b } if max <= 1 { b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS. } else if count > 0 { b.Norm = math.Log(float64(count)) / divisor } return b } line, col := 1, 2 // TODO: Why is this 2? for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); { b := p.Blocks[bi] if b.StartLine == line && b.StartCol == col { boundaries = append(boundaries, boundary(si, true, b.Count)) } if b.EndLine == line && b.EndCol == col { boundaries = append(boundaries, boundary(si, false, 0)) bi++ continue // Don't advance through src; maybe the next block starts here. } if src[si] == '\n' { line++ col = 0 } col++ si++ } sort.Sort(boundariesByPos(boundaries)) return } type boundariesByPos []Boundary func (b boundariesByPos) Len() int { return len(b) } func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b boundariesByPos) Less(i, j int) bool { if b[i].Offset == b[j].Offset { return !b[i].Start && b[j].Start } return b[i].Offset < b[j].Offset } // findFile finds the location of the named file in GOROOT, GOPATH etc. func findFile(file string) (string, error) { dir, file := filepath.Split(file) pkg, err := build.Import(dir, ".", build.FindOnly) if err != nil { return "", fmt.Errorf("can't find %q: %v", file, err) } return filepath.Join(pkg.Dir, file), nil } ./cmd/cover/cover.go0000644000014500017510000004324512246613010014031 0ustar michaelstaff// 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 main import ( "bytes" "flag" "fmt" "go/ast" "go/parser" "go/printer" "go/token" "io" "io/ioutil" "log" "os" "path/filepath" "sort" "strconv" ) const usageMessage = "" + `Usage of 'go tool cover': Given a coverage profile produced by 'go test': go test -coverprofile=c.out Open a web browser displaying annotated source code: go tool cover -html=c.out Write out an HTML file instead of launching a web browser: go tool cover -html=c.out -o coverage.html Display coverage percentages to stdout for each function: go tool cover -func=c.out Finally, to generate modified source code with coverage annotations (what go test -cover does): go tool cover -mode=set -var=CoverageVariableName program.go ` func usage() { fmt.Fprintln(os.Stderr, usageMessage) fmt.Fprintln(os.Stderr, "Flags:") flag.PrintDefaults() fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.") os.Exit(2) } var ( mode = flag.String("mode", "", "coverage mode: set, count, atomic") varVar = flag.String("var", "GoCover", "name of coverage variable to generate") output = flag.String("o", "", "file for output; default: stdout") htmlOut = flag.String("html", "", "generate HTML representation of coverage profile") funcOut = flag.String("func", "", "output coverage profile information for each function") ) var profile string // The profile to read; the value of -html or -func var counterStmt func(*File, ast.Expr) ast.Stmt const ( atomicPackagePath = "sync/atomic" atomicPackageName = "_cover_atomic_" ) func main() { flag.Usage = usage flag.Parse() // Usage information when no arguments. if flag.NFlag() == 0 && flag.NArg() == 0 { flag.Usage() } err := parseFlags() if err != nil { fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`) os.Exit(2) } // Generate coverage-annotated source. if *mode != "" { cover(flag.Arg(0)) return } // Output HTML or function coverage information. if *htmlOut != "" { err = htmlOutput(profile, *output) } else { err = funcOutput(profile, *output) } if err != nil { fmt.Fprintf(os.Stderr, "cover: %v\n", err) os.Exit(2) } } // parseFlags sets the profile and counterStmt globals and performs validations. func parseFlags() error { profile = *htmlOut if *funcOut != "" { if profile != "" { return fmt.Errorf("too many options") } profile = *funcOut } // Must either display a profile or rewrite Go source. if (profile == "") == (*mode == "") { return fmt.Errorf("too many options") } if *mode != "" { switch *mode { case "set": counterStmt = setCounterStmt case "count": counterStmt = incCounterStmt case "atomic": counterStmt = atomicCounterStmt default: return fmt.Errorf("unknown -mode %v", *mode) } if flag.NArg() == 0 { return fmt.Errorf("missing source file") } else if flag.NArg() == 1 { return nil } } else if flag.NArg() == 0 { return nil } return fmt.Errorf("too many arguments") } // Block represents the information about a basic block to be recorded in the analysis. // Note: Our definition of basic block is based on control structures; we don't break // apart && and ||. We could but it doesn't seem important enough to bother. type Block struct { startByte token.Pos endByte token.Pos numStmt int } // File is a wrapper for the state of a file used in the parser. // The basic parse tree walker is a method of this type. type File struct { fset *token.FileSet name string // Name of file. astFile *ast.File blocks []Block atomicPkg string // Package name for "sync/atomic" in this file. } // Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.BlockStmt: // If it's a switch or select, the body is a list of case clauses; don't tag the block itself. if len(n.List) > 0 { switch n.List[0].(type) { case *ast.CaseClause: // switch for _, n := range n.List { clause := n.(*ast.CaseClause) clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false) } return f case *ast.CommClause: // select for _, n := range n.List { clause := n.(*ast.CommClause) clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false) } return f } } n.List = f.addCounters(n.Lbrace, n.Rbrace+1, n.List, true) // +1 to step past closing brace. case *ast.IfStmt: ast.Walk(f, n.Body) if n.Else == nil { return nil } // The elses are special, because if we have // if x { // } else if y { // } // we want to cover the "if y". To do this, we need a place to drop the counter, // so we add a hidden block: // if x { // } else { // if y { // } // } const backupToElse = token.Pos(len("else ")) // The AST doesn't remember the else location. We can make an accurate guess. switch stmt := n.Else.(type) { case *ast.IfStmt: block := &ast.BlockStmt{ Lbrace: stmt.If - backupToElse, // So the covered part looks like it starts at the "else". List: []ast.Stmt{stmt}, Rbrace: stmt.End(), } n.Else = block case *ast.BlockStmt: stmt.Lbrace -= backupToElse // So the block looks like it starts at the "else". default: panic("unexpected node type in if") } ast.Walk(f, n.Else) return nil case *ast.SelectStmt: // Don't annotate an empty select - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { return nil } case *ast.SwitchStmt: // Don't annotate an empty switch - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { return nil } } return f } // unquote returns the unquoted string. func unquote(s string) string { t, err := strconv.Unquote(s) if err != nil { log.Fatalf("cover: improperly quoted string %q\n", s) } return t } // addImport adds an import for the specified path, if one does not already exist, and returns // the local package name. func (f *File) addImport(path string) string { // Does the package already import it? for _, s := range f.astFile.Imports { if unquote(s.Path.Value) == path { if s.Name != nil { return s.Name.Name } return filepath.Base(path) } } newImport := &ast.ImportSpec{ Name: ast.NewIdent(atomicPackageName), Path: &ast.BasicLit{ Kind: token.STRING, Value: fmt.Sprintf("%q", path), }, } impDecl := &ast.GenDecl{ Tok: token.IMPORT, Specs: []ast.Spec{ newImport, }, } // Make the new import the first Decl in the file. astFile := f.astFile astFile.Decls = append(astFile.Decls, nil) copy(astFile.Decls[1:], astFile.Decls[0:]) astFile.Decls[0] = impDecl astFile.Imports = append(astFile.Imports, newImport) // Now refer to the package, just in case it ends up unused. // That is, append to the end of the file the declaration // var _ = _cover_atomic_.AddUint32 reference := &ast.GenDecl{ Tok: token.VAR, Specs: []ast.Spec{ &ast.ValueSpec{ Names: []*ast.Ident{ ast.NewIdent("_"), }, Values: []ast.Expr{ &ast.SelectorExpr{ X: ast.NewIdent(atomicPackageName), Sel: ast.NewIdent("AddUint32"), }, }, }, }, } astFile.Decls = append(astFile.Decls, reference) return atomicPackageName } var slashslash = []byte("//") // initialComments returns the prefix of content containing only // whitepace and line comments. Any +build directives must appear // within this region. This approach is more reliable than using // go/printer to print a modified AST containing comments. // func initialComments(content []byte) []byte { // Derived from go/build.Context.shouldBuild. end := 0 p := content for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { line, p = line[:i], p[i+1:] } else { p = p[len(p):] } line = bytes.TrimSpace(line) if len(line) == 0 { // Blank line. end = len(content) - len(p) continue } if !bytes.HasPrefix(line, slashslash) { // Not comment line. break } } return content[:end] } func cover(name string) { fset := token.NewFileSet() content, err := ioutil.ReadFile(name) if err != nil { log.Fatalf("cover: %s: %s", name, err) } parsedFile, err := parser.ParseFile(fset, name, content, 0) if err != nil { log.Fatalf("cover: %s: %s", name, err) } file := &File{ fset: fset, name: name, astFile: parsedFile, } if *mode == "atomic" { file.atomicPkg = file.addImport(atomicPackagePath) } ast.Walk(file, file.astFile) fd := os.Stdout if *output != "" { var err error fd, err = os.Create(*output) if err != nil { log.Fatalf("cover: %s", err) } } fd.Write(initialComments(content)) // Retain '// +build' directives. file.print(fd) // After printing the source tree, add some declarations for the counters etc. // We could do this by adding to the tree, but it's easier just to print the text. file.addVariables(fd) } func (f *File) print(w io.Writer) { printer.Fprint(w, f.fset, f.astFile) } // intLiteral returns an ast.BasicLit representing the integer value. func (f *File) intLiteral(i int) *ast.BasicLit { node := &ast.BasicLit{ Kind: token.INT, Value: fmt.Sprint(i), } return node } // index returns an ast.BasicLit representing the number of counters present. func (f *File) index() *ast.BasicLit { return f.intLiteral(len(f.blocks)) } // setCounterStmt returns the expression: __count[23] = 1. func setCounterStmt(f *File, counter ast.Expr) ast.Stmt { return &ast.AssignStmt{ Lhs: []ast.Expr{counter}, Tok: token.ASSIGN, Rhs: []ast.Expr{f.intLiteral(1)}, } } // incCounterStmt returns the expression: __count[23]++. func incCounterStmt(f *File, counter ast.Expr) ast.Stmt { return &ast.IncDecStmt{ X: counter, Tok: token.INC, } } // atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1) func atomicCounterStmt(f *File, counter ast.Expr) ast.Stmt { return &ast.ExprStmt{ X: &ast.CallExpr{ Fun: &ast.SelectorExpr{ X: ast.NewIdent(f.atomicPkg), Sel: ast.NewIdent("AddUint32"), }, Args: []ast.Expr{&ast.UnaryExpr{ Op: token.AND, X: counter, }, f.intLiteral(1), }, }, } } // newCounter creates a new counter expression of the appropriate form. func (f *File) newCounter(start, end token.Pos, numStmt int) ast.Stmt { counter := &ast.IndexExpr{ X: &ast.SelectorExpr{ X: ast.NewIdent(*varVar), Sel: ast.NewIdent("Count"), }, Index: f.index(), } stmt := counterStmt(f, counter) f.blocks = append(f.blocks, Block{start, end, numStmt}) return stmt } // addCounters takes a list of statements and adds counters to the beginning of // each basic block at the top level of that list. For instance, given // // S1 // if cond { // S2 // } // S3 // // counters will be added before S1 and before S3. The block containing S2 // will be visited in a separate call. // TODO: Nested simple blocks get unecessary (but correct) counters func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) []ast.Stmt { // Special case: make sure we add a counter to an empty block. Can't do this below // or we will add a counter to an empty statement list after, say, a return statement. if len(list) == 0 { return []ast.Stmt{f.newCounter(pos, blockEnd, 0)} } // We have a block (statement list), but it may have several basic blocks due to the // appearance of statements that affect the flow of control. var newList []ast.Stmt for { // Find first statement that affects flow of control (break, continue, if, etc.). // It will be the last statement of this basic block. var last int end := blockEnd for last = 0; last < len(list); last++ { end = f.statementBoundary(list[last]) if f.endsBasicSourceBlock(list[last]) { extendToClosingBrace = false // Block is broken up now. last++ break } } if extendToClosingBrace { end = blockEnd } if pos != end { // Can have no source to cover if e.g. blocks abut. newList = append(newList, f.newCounter(pos, end, last)) } newList = append(newList, list[0:last]...) list = list[last:] if len(list) == 0 { break } pos = list[0].Pos() } return newList } // hasFuncLiteral reports the existence and position of the first func literal // in the node, if any. If a func literal appears, it usually marks the termination // of a basic block because the function body is itself a block. // Therefore we draw a line at the start of the body of the first function literal we find. // TODO: what if there's more than one? Probably doesn't matter much. func hasFuncLiteral(n ast.Node) (bool, token.Pos) { var literal funcLitFinder ast.Walk(&literal, n) return literal.found(), token.Pos(literal) } // statementBoundary finds the location in s that terminates the current basic // block in the source. func (f *File) statementBoundary(s ast.Stmt) token.Pos { // Control flow statements are easy. switch s := s.(type) { case *ast.BlockStmt: // Treat blocks like basic blocks to avoid overlapping counters. return s.Lbrace case *ast.IfStmt: return s.Body.Lbrace case *ast.ForStmt: return s.Body.Lbrace case *ast.LabeledStmt: return f.statementBoundary(s.Stmt) case *ast.RangeStmt: // Ranges might loop over things with function literals.: for _ = range []func(){ ... } {. // TODO: There are a few other such possibilities, but they're extremely unlikely. found, pos := hasFuncLiteral(s.X) if found { return pos } return s.Body.Lbrace case *ast.SwitchStmt: return s.Body.Lbrace case *ast.SelectStmt: return s.Body.Lbrace case *ast.TypeSwitchStmt: return s.Body.Lbrace } // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal. // If it does, that's tricky because we want to exclude the body of the function from this block. // Draw a line at the start of the body of the first function literal we find. // TODO: what if there's more than one? Probably doesn't matter much. found, pos := hasFuncLiteral(s) if found { return pos } return s.End() } // endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc., // or if it's just problematic, for instance contains a function literal, which will complicate // accounting due to the block-within-an expression. func (f *File) endsBasicSourceBlock(s ast.Stmt) bool { switch s := s.(type) { case *ast.BlockStmt: // Treat blocks like basic blocks to avoid overlapping counters. return true case *ast.BranchStmt: return true case *ast.ForStmt: return true case *ast.IfStmt: return true case *ast.LabeledStmt: return f.endsBasicSourceBlock(s.Stmt) case *ast.RangeStmt: return true case *ast.SwitchStmt: return true case *ast.SelectStmt: return true case *ast.TypeSwitchStmt: return true } found, _ := hasFuncLiteral(s) return found } // funcLitFinder implements the ast.Visitor pattern to find the location of any // function literal in a subtree. type funcLitFinder token.Pos func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) { if f.found() { return nil // Prune search. } switch n := node.(type) { case *ast.FuncLit: *f = funcLitFinder(n.Body.Lbrace) return nil // Prune search. } return f } func (f *funcLitFinder) found() bool { return token.Pos(*f) != token.NoPos } // Sort interface for []block1; used for self-check in addVariables. type block1 struct { Block index int } type blockSlice []block1 func (b blockSlice) Len() int { return len(b) } func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte } func (b blockSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // addVariables adds to the end of the file the declarations to set up the counter and position variables. func (f *File) addVariables(w io.Writer) { // Self-check: Verify that the instrumented basic blocks are disjoint. t := make([]block1, len(f.blocks)) for i := range f.blocks { t[i].Block = f.blocks[i] t[i].index = i } sort.Sort(blockSlice(t)) for i := 1; i < len(t); i++ { if t[i-1].endByte > t[i].startByte { fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index) fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n", f.name, t[i-1].startByte, t[i-1].endByte, f.name, t[i].startByte, t[i].endByte) } } // Declare the coverage struct as a package-level variable. fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar) fmt.Fprintf(w, "\tCount [%d]uint32\n", len(f.blocks)) fmt.Fprintf(w, "\tPos [3 * %d]uint32\n", len(f.blocks)) fmt.Fprintf(w, "\tNumStmt [%d]uint16\n", len(f.blocks)) fmt.Fprintf(w, "} {\n") // Initialize the position array field. fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks)) // A nice long list of positions. Each position is encoded as follows to reduce size: // - 32-bit starting line number // - 32-bit ending line number // - (16 bit ending column number << 16) | (16-bit starting column number). for i, block := range f.blocks { start := f.fset.Position(block.startByte) end := f.fset.Position(block.endByte) fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i) } // Close the position array. fmt.Fprintf(w, "\t},\n") // Initialize the position array field. fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks)) // A nice long list of statements-per-block, so we can give a conventional // valuation of "percent covered". To save space, it's a 16-bit number, so we // clamp it if it overflows - won't matter in practice. for i, block := range f.blocks { n := block.numStmt if n > 1<<16-1 { n = 1<<16 - 1 } fmt.Fprintf(w, "\t\t%d, // %d\n", n, i) } // Close the statements-per-block array. fmt.Fprintf(w, "\t},\n") // Close the struct initialization. fmt.Fprintf(w, "}\n") } ./cmd/cover/testdata/0000755000014500017510000000000012246613010014165 5ustar michaelstaff./cmd/cover/testdata/test.go0000644000014500017510000000540012246613010015472 0ustar michaelstaff// 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. // This program is processed by the cover command, and then testAll is called. // The test driver in main.go can then compare the coverage statistics with expectation. // The word LINE is replaced by the line number in this file. When the file is executed, // the coverage processing has changed the line numbers, so we can't use runtime.Caller. package main const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" func testAll() { testSimple() testBlockRun() testIf() testFor() testRange() testSwitch() testTypeSwitch() testSelect1() testSelect2() } func testSimple() { check(LINE, 1) } func testIf() { if true { check(LINE, 1) } else { check(LINE, 0) } if false { check(LINE, 0) } else { check(LINE, 1) } for i := 0; i < 3; i++ { if checkVal(LINE, 3, i) <= 2 { check(LINE, 3) } if checkVal(LINE, 3, i) <= 1 { check(LINE, 2) } if checkVal(LINE, 3, i) <= 0 { check(LINE, 1) } } for i := 0; i < 3; i++ { if checkVal(LINE, 3, i) <= 1 { check(LINE, 2) } else { check(LINE, 1) } } for i := 0; i < 3; i++ { if checkVal(LINE, 3, i) <= 0 { check(LINE, 1) } else if checkVal(LINE, 2, i) <= 1 { check(LINE, 1) } else if checkVal(LINE, 1, i) <= 2 { check(LINE, 1) } else if checkVal(LINE, 0, i) <= 3 { check(LINE, 0) } } } func testFor() { for i := 0; i < 10; i++ { check(LINE, 10) } } func testRange() { for _, f := range []func(){ func() { check(LINE, 1) }, } { f() check(LINE, 1) } } func testBlockRun() { check(LINE, 1) { check(LINE, 1) } { check(LINE, 1) } check(LINE, 1) { check(LINE, 1) } { check(LINE, 1) } check(LINE, 1) } func testSwitch() { for i := 0; i < 5; i++ { switch i { case 0: check(LINE, 1) case 1: check(LINE, 1) case 2: check(LINE, 1) default: check(LINE, 2) } } } func testTypeSwitch() { var x = []interface{}{1, 2.0, "hi"} for _, v := range x { switch v.(type) { case int: check(LINE, 1) case float64: check(LINE, 1) case string: check(LINE, 1) case complex128: check(LINE, 0) default: check(LINE, 0) } } } func testSelect1() { c := make(chan int) go func() { for i := 0; i < 1000; i++ { c <- i } }() for { select { case <-c: check(LINE, anything) case <-c: check(LINE, anything) default: check(LINE, 1) return } } } func testSelect2() { c1 := make(chan int, 1000) c2 := make(chan int, 1000) for i := 0; i < 1000; i++ { c1 <- i c2 <- i } for { select { case <-c1: check(LINE, 1000) case <-c2: check(LINE, 1000) default: check(LINE, 1) return } } } ./cmd/cover/testdata/main.go0000644000014500017510000000437412246613010015450 0ustar michaelstaff// 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. // Test runner for coverage test. This file is not coverage-annotated; test.go is. // It knows the coverage counter is called "coverTest". package main import ( "fmt" "os" ) func main() { testAll() verify() } type block struct { count uint32 line uint32 } var counters = make(map[block]bool) // check records the location and expected value for a counter. func check(line, count uint32) { b := block{ count, line, } counters[b] = true } // checkVal is a version of check that returns its extra argument, // so it can be used in conditionals. func checkVal(line, count uint32, val int) int { b := block{ count, line, } counters[b] = true return val } var PASS = true // verify checks the expected counts against the actual. It runs after the test has completed. func verify() { for b := range counters { got, index := count(b.line) if b.count == anything && got != 0 { got = anything } if got != b.count { fmt.Fprintf(os.Stderr, "test_go:%d expected count %d got %d [counter %d]\n", b.line, b.count, got, index) PASS = false } } if !PASS { fmt.Fprintf(os.Stderr, "FAIL\n") os.Exit(2) } } // count returns the count and index for the counter at the specified line. func count(line uint32) (uint32, int) { // Linear search is fine. Choose perfect fit over approximate. // We can have a closing brace for a range on the same line as a condition for an "else if" // and we don't want that brace to steal the count for the condition on the "if". // Therefore we test for a perfect (lo==line && hi==line) match, but if we can't // find that we take the first imperfect match. index := -1 indexLo := uint32(1e9) for i := range coverTest.Count { lo, hi := coverTest.Pos[3*i], coverTest.Pos[3*i+1] if lo == line && line == hi { return coverTest.Count[i], i } // Choose the earliest match (the counters are in unpredictable order). if lo <= line && line <= hi && indexLo > lo { index = i indexLo = lo } } if index == -1 { fmt.Fprintln(os.Stderr, "cover_test: no counter for line", line) PASS = false return 0, 0 } return coverTest.Count[index], index } ./cmd/vet/0000755000014500017510000000000012246613010012034 5ustar michaelstaff./cmd/vet/rangeloop.go0000644000014500017510000000305212246613010014351 0ustar michaelstaff// Copyright 2012 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 contains the code to check range loop variables bound inside function literals that are deferred or launched in new goroutines. We only check instances where the defer or go statement is the last statement in the loop body, as otherwise we would need whole program analysis. For example: for i, v := range s { go func() { println(i, v) // not what you might expect }() } See: http://golang.org/doc/go_faq.html#closures_and_goroutines */ package main import "go/ast" // checkRangeLoop walks the body of the provided range statement, checking if // its index or value variables are used unsafely inside goroutines or deferred // function literals. func checkRangeLoop(f *File, n *ast.RangeStmt) { if !vet("rangeloops") { return } key, _ := n.Key.(*ast.Ident) val, _ := n.Value.(*ast.Ident) if key == nil && val == nil { return } sl := n.Body.List if len(sl) == 0 { return } var last *ast.CallExpr switch s := sl[len(sl)-1].(type) { case *ast.GoStmt: last = s.Call case *ast.DeferStmt: last = s.Call default: return } lit, ok := last.Fun.(*ast.FuncLit) if !ok { return } ast.Inspect(lit.Body, func(n ast.Node) bool { id, ok := n.(*ast.Ident) if !ok || id.Obj == nil { return true } if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function") } return true }) } ./cmd/vet/assign.go0000644000014500017510000000213612246613010013651 0ustar michaelstaff// 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. /* This file contains the code to check for useless assignments. */ package main import ( "go/ast" "go/token" "reflect" ) // TODO: should also check for assignments to struct fields inside methods // that are on T instead of *T. // checkAssignStmt checks for assignments of the form " = ". // These are almost always useless, and even when they aren't they are usually a mistake. func (f *File) checkAssignStmt(stmt *ast.AssignStmt) { if !vet("assign") { return } if stmt.Tok != token.ASSIGN { return // ignore := } if len(stmt.Lhs) != len(stmt.Rhs) { // If LHS and RHS have different cardinality, they can't be the same. return } for i, lhs := range stmt.Lhs { rhs := stmt.Rhs[i] if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { continue // short-circuit the heavy-weight gofmt check } le := f.gofmt(lhs) re := f.gofmt(rhs) if le == re { f.Warnf(stmt.Pos(), "self-assignment of %s to %s", re, le) } } } ./cmd/vet/nilfunc.go0000644000014500017510000000246512246613010014030 0ustar michaelstaff// 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. /* This file contains the code to check for useless function comparisons. A useless comparison is one like f == nil as opposed to f() == nil. */ package main import ( "go/ast" "go/token" "code.google.com/p/go.tools/go/types" ) func (f *File) checkNilFuncComparison(e *ast.BinaryExpr) { if !vet("nilfunc") { return } // Only want == or != comparisons. if e.Op != token.EQL && e.Op != token.NEQ { return } // Only want comparisons with a nil identifier on one side. var e2 ast.Expr switch { case f.isNil(e.X): e2 = e.Y case f.isNil(e.Y): e2 = e.X default: return } // Only want identifiers or selector expressions. var obj types.Object switch v := e2.(type) { case *ast.Ident: obj = f.pkg.idents[v] case *ast.SelectorExpr: obj = f.pkg.idents[v.Sel] default: return } // Only want functions. if _, ok := obj.(*types.Func); !ok { return } f.Badf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ) } // isNil reports whether the provided expression is the built-in nil // identifier. func (f *File) isNil(e ast.Expr) bool { return f.pkg.types[e] == types.Typ[types.UntypedNil] } ./cmd/vet/whitelist/0000755000014500017510000000000012246613010014050 5ustar michaelstaff./cmd/vet/whitelist/whitelist.go0000644000014500017510000000436012246613010016416 0ustar michaelstaff// 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 whitelist defines exceptions for the vet tool. package whitelist // UnkeyedLiteral are types that are actually slices, but // syntactically, we cannot tell whether the Typ in pkg.Typ{1, 2, 3} // is a slice or a struct, so we whitelist all the standard package // library's exported slice types. var UnkeyedLiteral = map[string]bool{ /* find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \ grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \ sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \ sort | awk '{ print "\"" $0 "\": true," }' */ "crypto/x509/pkix.RDNSequence": true, "crypto/x509/pkix.RelativeDistinguishedNameSET": true, "database/sql.RawBytes": true, "debug/macho.LoadBytes": true, "encoding/asn1.ObjectIdentifier": true, "encoding/asn1.RawContent": true, "encoding/json.RawMessage": true, "encoding/xml.CharData": true, "encoding/xml.Comment": true, "encoding/xml.Directive": true, "go/scanner.ErrorList": true, "image/color.Palette": true, "net.HardwareAddr": true, "net.IP": true, "net.IPMask": true, "sort.Float64Slice": true, "sort.IntSlice": true, "sort.StringSlice": true, "unicode.SpecialCase": true, // These image and image/color struct types are frozen. We will never add fields to them. "image/color.Alpha16": true, "image/color.Alpha": true, "image/color.Gray16": true, "image/color.Gray": true, "image/color.NRGBA64": true, "image/color.NRGBA": true, "image/color.RGBA64": true, "image/color.RGBA": true, "image/color.YCbCr": true, "image.Point": true, "image.Rectangle": true, "image.Uniform": true, } ./cmd/vet/doc.go0000644000014500017510000000471112246613010013133 0ustar michaelstaff// Copyright 2010 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. /* Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string. Vet uses heuristics that do not guarantee all reports are genuine problems, but it can find errors not caught by the compilers. Its exit code is 2 for erroneous invocation of the tool, 1 if a problem was reported, and 0 otherwise. Note that the tool does not check every possible problem and depends on unreliable heuristics so it should be used as guidance only, not as a firm indicator of program correctness. By default all checks are performed, but if explicit flags are provided, only those identified by the flags are performed. Available checks: 1. Printf family, flag -printf Suspicious calls to functions in the Printf family, including any functions with these names: Print Printf Println Fprint Fprintf Fprintln Sprint Sprintf Sprintln Error Errorf Fatal Fatalf 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. 2. 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 3. Struct tags, flag -structtags Struct tags that do not follow the format understood by reflect.StructTag.Get. 4. Unkeyed composite literals, flag -composites Composite struct literals that do not use the field-keyed syntax. Usage: go tool vet [flag] [file.go ...] go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively The other flags are: -v Verbose mode -printfuncs A comma-separated list of print-like functions to supplement the standard list. Each entry is in the form Name:N where N is the zero-based argument position of the first argument involved in the print: either the format or the first print argument for non-formatted prints. For example, if you have Warn and Warnf functions that take an io.Writer as their first argument, like Fprintf, -printfuncs=Warn:1,Warnf:1 */ package main ./cmd/vet/composite.go0000644000014500017510000000607112246613010014371 0ustar michaelstaff// Copyright 2012 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 contains the test for unkeyed struct literals. package main import ( "flag" "go/ast" "strings" "code.google.com/p/go.tools/cmd/vet/whitelist" ) var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") // checkUnkeyedLiteral checks if a composite literal is a struct literal with // unkeyed fields. func (f *File) checkUnkeyedLiteral(c *ast.CompositeLit) { if !vet("composites") { return } typ := c.Type for { if typ1, ok := c.Type.(*ast.ParenExpr); ok { typ = typ1 continue } break } switch typ.(type) { case *ast.ArrayType: return case *ast.MapType: return case *ast.StructType: return // a literal struct type does not need to use keys case *ast.Ident: // A simple type name like t or T does not need keys either, // since it is almost certainly declared in the current package. // (The exception is names being used via import . "pkg", but // those are already breaking the Go 1 compatibility promise, // so not reporting potential additional breakage seems okay.) return } // Otherwise the type is a selector like pkg.Name. // We only care if pkg.Name is a struct, not if it's a map, array, or slice. isStruct, typeString := f.pkg.isStruct(c) if !isStruct { return } if typeString == "" { // isStruct doesn't know typeString = f.gofmt(typ) } // It's a struct, or we can't tell it's not a struct because we don't have types. // Check if the CompositeLit contains an unkeyed field. allKeyValue := true for _, e := range c.Elts { if _, ok := e.(*ast.KeyValueExpr); !ok { allKeyValue = false break } } if allKeyValue { return } // Check that the CompositeLit's type has the form pkg.Typ. s, ok := c.Type.(*ast.SelectorExpr) if !ok { return } pkg, ok := s.X.(*ast.Ident) if !ok { return } // Convert the package name to an import path, and compare to a whitelist. path := pkgPath(f, pkg.Name) if path == "" { f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) return } typeName := path + "." + s.Sel.Name if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] { return } f.Warn(c.Pos(), typeString+" composite literal uses unkeyed fields") } // pkgPath returns the import path "image/png" for the package name "png". // // This is based purely on syntax and convention, and not on the imported // package's contents. It will be incorrect if a package name differs from the // leaf element of the import path, or if the package was a dot import. func pkgPath(f *File, pkgName string) (path string) { for _, x := range f.file.Imports { s := strings.Trim(x.Path.Value, `"`) if x.Name != nil { // Catch `import pkgName "foo/bar"`. if x.Name.Name == pkgName { return s } } else { // Catch `import "pkgName"` or `import "foo/bar/pkgName"`. if s == pkgName || strings.HasSuffix(s, "/"+pkgName) { return s } } } return "" } ./cmd/vet/types.go0000644000014500017510000002357112246613010013537 0ustar michaelstaff// Copyright 2010 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 contains the pieces of the tool that use typechecking from the go/types package. package main import ( "go/ast" "go/token" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" ) func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { pkg.idents = make(map[*ast.Ident]types.Object) pkg.spans = make(map[types.Object]Span) pkg.types = make(map[ast.Expr]types.Type) pkg.values = make(map[ast.Expr]exact.Value) // By providing a Config with our own error function, it will continue // past the first error. There is no need for that function to do anything. config := types.Config{ Error: func(error) {}, } info := &types.Info{ Types: pkg.types, Values: pkg.values, Objects: pkg.idents, } _, err := config.Check(pkg.path, fs, astFiles, info) // update spans for id, obj := range pkg.idents { pkg.growSpan(id, obj) } return err } // isStruct reports whether the composite literal c is a struct. // If it is not (probably a struct), it returns a printable form of the type. func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) { // Check that the CompositeLit's type is a slice or array (which needs no field keys), if possible. typ := pkg.types[c] // If it's a named type, pull out the underlying type. If it's not, the Underlying // method returns the type itself. actual := typ if actual != nil { actual = actual.Underlying() } if actual == nil { // No type information available. Assume true, so we do the check. return true, "" } switch actual.(type) { case *types.Struct: return true, typ.String() default: return false, "" } } var ( stringerMethodType = types.New("func() string") errorType = types.New("interface{ Error() string }").(*types.Interface) stringerType = types.New("interface{ String() string }").(*types.Interface) // One day this might work. See issue 6259. // formatterType = types.New("interface{Format(f fmt.State, c rune)}") ) // matchArgType reports an error if printf verb t is not appropriate // for operand arg. // // typ is used only for recursive calls; external callers must supply nil. // // (Recursion arises from the compound types {map,chan,slice} which // may be printed with %d etc. if that is appropriate for their element // types.) func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool { return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool)) } // matchArgTypeInternal is the internal version of matchArgType. It carries a map // remembering what types are in progress so we don't recur when faced with recursive // types or mutually recursive types. func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool { // %v, %T accept any argument type. if t == anyType { return true } if typ == nil { // external call typ = f.pkg.types[arg] if typ == nil { return true // probably a type check problem } } // If the type implements fmt.Formatter, we have nothing to check. // But (see issue 6259) that's not easy to verify, so instead we see // if its method set contains a Format function. We could do better, // even now, but we don't need to be 100% accurate. Wait for 6259 to // be fixed instead. TODO. if hasMethod(typ, "Format") { return true } // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? if t&argString != 0 { if types.Implements(typ, errorType, false) || types.Implements(typ, stringerType, false) { return true } } typ = typ.Underlying() if inProgress[typ] { // We're already looking at this type. The call that started it will take care of it. return true } inProgress[typ] = true switch typ := typ.(type) { case *types.Signature: return t&argPointer != 0 case *types.Map: // Recur: map[int]int matches %d. return t&argPointer != 0 || (f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)) case *types.Chan: return t&argPointer != 0 case *types.Array: // Same as slice. if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { return true // %s matches []byte } // Recur: []int matches %d. return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) case *types.Slice: // Same as array. if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { return true // %s matches []byte } // Recur: []int matches %d. But watch out for // type T []T // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below. return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) case *types.Pointer: // Ugly, but dealing with an edge case: a known pointer to an invalid type, // probably something from a failed import. if typ.Elem().String() == "invalid type" { if *verbose { f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg)) } return true // special case } // If it's actually a pointer with %p, it prints as one. if t == argPointer { return true } // If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct. if str, ok := typ.Elem().Underlying().(*types.Struct); ok { return f.matchStructArgType(t, str, arg, inProgress) } // The rest can print with %p as pointers, or as integers with %x etc. return t&(argInt|argPointer) != 0 case *types.Struct: return f.matchStructArgType(t, typ, arg, inProgress) case *types.Interface: // If the static type of the argument is empty interface, there's little we can do. // Example: // func f(x interface{}) { fmt.Printf("%s", x) } // Whether x is valid for %s depends on the type of the argument to f. One day // we will be able to do better. For now, we assume that empty interface is OK // but non-empty interfaces, with Stringer and Error handled above, are errors. return typ.NumMethods() == 0 case *types.Basic: switch typ.Kind() { case types.UntypedBool, types.Bool: return t&argBool != 0 case types.UntypedInt, types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: return t&argInt != 0 case types.UntypedFloat, types.Float32, types.Float64: return t&argFloat != 0 case types.UntypedComplex, types.Complex64, types.Complex128: return t&argComplex != 0 case types.UntypedString, types.String: return t&argString != 0 case types.UnsafePointer: return t&(argPointer|argInt) != 0 case types.UntypedRune: return t&(argInt|argRune) != 0 case types.UntypedNil: return t&argPointer != 0 // TODO? case types.Invalid: if *verbose { f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg)) } return true // Probably a type check problem. } panic("unreachable") } return false } // matchStructArgType reports whether all the elements of the struct match the expected // type. For instance, with "%d" all the elements must be printable with the "%d" format. func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool { for i := 0; i < typ.NumFields(); i++ { if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) { return false } } return true } // numArgsInSignature tells how many formal arguments the function type // being called has. func (f *File) numArgsInSignature(call *ast.CallExpr) int { // Check the type of the function or method declaration typ := f.pkg.types[call.Fun] if typ == nil { return 0 } // The type must be a signature, but be sure for safety. sig, ok := typ.(*types.Signature) if !ok { return 0 } return sig.Params().Len() } // isErrorMethodCall reports whether the call is of a method with signature // func Error() string // where "string" is the universe's string type. We know the method is called "Error". func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { typ := f.pkg.types[call] if typ != nil { // We know it's called "Error", so just check the function signature. return types.IsIdentical(f.pkg.types[call.Fun], stringerMethodType) } // Without types, we can still check by hand. // Is it a selector expression? Otherwise it's a function call, not a method call. sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return false } // The package is type-checked, so if there are no arguments, we're done. if len(call.Args) > 0 { return false } // Check the type of the method declaration typ = f.pkg.types[sel] if typ == nil { return false } // The type must be a signature, but be sure for safety. sig, ok := typ.(*types.Signature) if !ok { return false } // There must be a receiver for it to be a method call. Otherwise it is // a function, not something that satisfies the error interface. if sig.Recv() == nil { return false } // There must be no arguments. Already verified by type checking, but be thorough. if sig.Params().Len() > 0 { return false } // Finally the real questions. // There must be one result. if sig.Results().Len() != 1 { return false } // It must have return type "string" from the universe. return sig.Results().At(0).Type() == types.Typ[types.String] } // hasMethod reports whether the type contains a method with the given name. // It is part of the workaround for Formatters and should be deleted when // that workaround is no longer necessary. TODO: delete when fixed. func hasMethod(typ types.Type, name string) bool { set := typ.MethodSet() for i := 0; i < set.Len(); i++ { if set.At(i).Obj().Name() == name { return true } } return false } ./cmd/vet/structtag.go0000644000014500017510000000161512246613010014406 0ustar michaelstaff// Copyright 2010 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 contains the test for canonical struct tags. package main import ( "go/ast" "reflect" "strconv" ) // checkField checks a struct field tag. func (f *File) checkCanonicalFieldTag(field *ast.Field) { if !vet("structtags") { return } if field.Tag == nil { return } tag, err := strconv.Unquote(field.Tag.Value) if err != nil { f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) return } // Check tag for validity by appending // new key:value to end and checking that // the tag parsing code can find it. if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" { f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value) return } } ./cmd/vet/main.go0000644000014500017510000003177712246613010013326 0ustar michaelstaff// Copyright 2010 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. // Vet is a simple checker for static errors in Go source code. // See doc.go for more information. package main import ( "bytes" "flag" "fmt" "go/ast" "go/build" "go/parser" "go/printer" "go/token" "io/ioutil" "os" "path/filepath" "strconv" "strings" "code.google.com/p/go.tools/go/exact" _ "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) var verbose = flag.Bool("v", false, "verbose") var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy") var testFlag = flag.Bool("test", false, "for testing only: sets -all and -shadow") var exitCode = 0 // Flags to control which checks to perform. "all" is set to true here, and disabled later if // a flag is set explicitly. var report = map[string]*bool{ "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"), "asmdecl": flag.Bool("asmdecl", false, "check assembly against Go declarations"), "assign": flag.Bool("assign", false, "check for useless assignments"), "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"), "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"), "composites": flag.Bool("composites", false, "check that composite literals used field-keyed elements"), "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"), "nilfunc": flag.Bool("nilfunc", false, "check for comparisons between functions and nil"), "printf": flag.Bool("printf", false, "check printf-like invocations"), "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"), "shadow": flag.Bool("shadow", false, "check for shadowed variables (experimental; must be set explicitly)"), "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"), "unreachable": flag.Bool("unreachable", false, "check for unreachable code"), } // TODO: Need a flag to set build tags when parsing the package. // vet tells whether to report errors for the named check, a flag name. func vet(name string) bool { if *testFlag { return true } if name == "shadow" { return *report["shadow"] } return *report["all"] || *report[name] } // setExit sets the value for os.Exit when it is called, later. It // remembers the highest value. func setExit(err int) { if err > exitCode { exitCode = err } } // 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, "\tvet [flags] directory...\n") fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") flag.PrintDefaults() os.Exit(2) } // File is a wrapper for the state of a file used in the parser. // The parse tree walkers are all methods of this type. type File struct { pkg *Package fset *token.FileSet name string content []byte file *ast.File b bytes.Buffer // for use by methods // The last "String() string" method receiver we saw while walking. // This is used by the recursiveStringer method in print.go. lastStringerReceiver *ast.Object } func main() { flag.Usage = Usage flag.Parse() // If a check is named explicitly, turn off the 'all' flag. for name, ptr := range report { if name != "all" && *ptr { *report["all"] = false break } } if *printfuncs != "" { for _, name := range strings.Split(*printfuncs, ",") { if len(name) == 0 { flag.Usage() } skip := 0 if colon := strings.LastIndex(name, ":"); colon > 0 { var err error skip, err = strconv.Atoi(name[colon+1:]) if err != nil { errorf(`illegal format for "Func:N" argument %q; %s`, name, err) } name = name[:colon] } name = strings.ToLower(name) if name[len(name)-1] == 'f' { printfList[name] = skip } else { printList[name] = skip } } } if flag.NArg() == 0 { Usage() } dirs := false files := false for _, name := range flag.Args() { // Is it a directory? fi, err := os.Stat(name) if err != nil { warnf("error walking tree: %s", err) continue } if fi.IsDir() { dirs = true } else { files = true } } if dirs && files { Usage() } if dirs { for _, name := range flag.Args() { walkDir(name) } return } if !doPackage(".", flag.Args()) { warnf("no files checked") } os.Exit(exitCode) } // prefixDirectory places the directory name on the beginning of each name in the list. func prefixDirectory(directory string, names []string) { if directory != "." { for i, name := range names { names[i] = filepath.Join(directory, name) } } } // doPackageDir analyzes the single package found in the directory, if there is one, // plus a test package, if there is one. func doPackageDir(directory string) { pkg, err := build.Default.ImportDir(directory, 0) if err != nil { // If it's just that there are no go source files, that's fine. if _, nogo := err.(*build.NoGoError); nogo { return } // Non-fatal: we are doing a recursive walk and there may be other directories. warnf("cannot process directory %s: %s", directory, err) return } var names []string names = append(names, pkg.GoFiles...) names = append(names, pkg.CgoFiles...) names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. names = append(names, pkg.SFiles...) prefixDirectory(directory, names) doPackage(directory, names) // Is there also a "foo_test" package? If so, do that one as well. if len(pkg.XTestGoFiles) > 0 { names = pkg.XTestGoFiles prefixDirectory(directory, names) doPackage(directory, names) } } type Package struct { path string idents map[*ast.Ident]types.Object types map[ast.Expr]types.Type values map[ast.Expr]exact.Value spans map[types.Object]Span files []*File } // doPackage analyzes the single package constructed from the named files. // It returns whether any files were checked. func doPackage(directory string, names []string) bool { var files []*File var astFiles []*ast.File fs := token.NewFileSet() for _, name := range names { f, err := os.Open(name) if err != nil { // Warn but continue to next package. warnf("%s: %s", name, err) return false } defer f.Close() data, err := ioutil.ReadAll(f) if err != nil { warnf("%s: %s", name, err) return false } checkBuildTag(name, data) var parsedFile *ast.File if strings.HasSuffix(name, ".go") { parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0) if err != nil { warnf("%s: %s", name, err) return false } astFiles = append(astFiles, parsedFile) } files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile}) } if len(astFiles) == 0 { return false } pkg := new(Package) pkg.path = astFiles[0].Name.Name pkg.files = files // Type check the package. err := pkg.check(fs, astFiles) if err != nil && *verbose { warnf("%s", err) } for _, file := range files { file.pkg = pkg if file.file != nil { file.walkFile(file.name, file.file) } } asmCheck(pkg) return true } func visit(path string, f os.FileInfo, err error) error { if err != nil { warnf("walk error: %s", err) return err } // One package per directory. Ignore the files themselves. if !f.IsDir() { return nil } doPackageDir(path) return nil } func (pkg *Package) hasFileWithSuffix(suffix string) bool { for _, f := range pkg.files { if strings.HasSuffix(f.name, suffix) { return true } } return false } // walkDir recursively walks the tree looking for Go packages. func walkDir(root string) { filepath.Walk(root, visit) } // errorf formats the error to standard error, adding program // identification and a newline, and exits. func errorf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) os.Exit(2) } // warnf formats the error to standard error, adding program // identification and a newline, but does not exit. func warnf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) setExit(1) } // Println is fmt.Println guarded by -v. func Println(args ...interface{}) { if !*verbose { return } fmt.Println(args...) } // Printf is fmt.Printf guarded by -v. func Printf(format string, args ...interface{}) { if !*verbose { return } fmt.Printf(format+"\n", args...) } // Bad reports an error and sets the exit code.. func (f *File) Bad(pos token.Pos, args ...interface{}) { f.Warn(pos, args...) setExit(1) } // Badf reports a formatted error and sets the exit code. func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { f.Warnf(pos, format, args...) setExit(1) } // loc returns a formatted representation of the position. func (f *File) loc(pos token.Pos) string { if pos == token.NoPos { return "" } // Do not print columns. Because the pos often points to the start of an // expression instead of the inner part with the actual error, the // precision can mislead. posn := f.fset.Position(pos) return fmt.Sprintf("%s:%d", posn.Filename, posn.Line) } // Warn reports an error but does not set the exit code. func (f *File) Warn(pos token.Pos, args ...interface{}) { fmt.Fprint(os.Stderr, f.loc(pos)+": "+fmt.Sprintln(args...)) } // Warnf reports a formatted error but does not set the exit code. func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { fmt.Fprintf(os.Stderr, f.loc(pos)+": "+format+"\n", args...) } // walkFile walks the file's tree. func (f *File) walkFile(name string, file *ast.File) { Println("Checking file", name) ast.Walk(f, file) } // Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.AssignStmt: f.walkAssignStmt(n) case *ast.BinaryExpr: f.walkBinaryExpr(n) case *ast.CallExpr: f.walkCallExpr(n) case *ast.CompositeLit: f.walkCompositeLit(n) case *ast.Field: f.walkFieldTag(n) case *ast.FuncDecl: f.walkFuncDecl(n) case *ast.FuncLit: f.walkFuncLit(n) case *ast.GenDecl: f.walkGenDecl(n) case *ast.InterfaceType: f.walkInterfaceType(n) case *ast.RangeStmt: f.walkRangeStmt(n) } return f } // walkAssignStmt walks an assignment statement func (f *File) walkAssignStmt(stmt *ast.AssignStmt) { f.checkAssignStmt(stmt) f.checkAtomicAssignment(stmt) f.checkShadowAssignment(stmt) } func (f *File) walkBinaryExpr(expr *ast.BinaryExpr) { f.checkNilFuncComparison(expr) } // walkCall walks a call expression. func (f *File) walkCall(call *ast.CallExpr, name string) { f.checkFmtPrintfCall(call, name) } // walkCallExpr walks a call expression. func (f *File) walkCallExpr(call *ast.CallExpr) { switch x := call.Fun.(type) { case *ast.Ident: f.walkCall(call, x.Name) case *ast.SelectorExpr: f.walkCall(call, x.Sel.Name) } } // walkCompositeLit walks a composite literal. func (f *File) walkCompositeLit(c *ast.CompositeLit) { f.checkUnkeyedLiteral(c) } // walkFieldTag walks a struct field tag. func (f *File) walkFieldTag(field *ast.Field) { if field.Tag == nil { return } f.checkCanonicalFieldTag(field) } // walkMethod walks the method's signature. func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) { f.checkCanonicalMethod(id, t) } // walkFuncDecl walks a function declaration. func (f *File) walkFuncDecl(d *ast.FuncDecl) { f.checkUnreachable(d.Body) if d.Recv != nil { f.walkMethod(d.Name, d.Type) } f.prepStringerReceiver(d) } // prepStringerReceiver checks whether the given declaration is a fmt.Stringer // implementation, and if so sets the File's lastStringerReceiver field to the // declaration's receiver object. func (f *File) prepStringerReceiver(d *ast.FuncDecl) { if !f.isStringer(d) { return } if l := d.Recv.List; len(l) == 1 { if n := l[0].Names; len(n) == 1 { f.lastStringerReceiver = n[0].Obj } } } // isStringer returns true if the provided declaration is a "String() string" // method; an implementation of fmt.Stringer. func (f *File) isStringer(d *ast.FuncDecl) bool { return d.Recv != nil && d.Name.Name == "String" && len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 && f.pkg.types[d.Type.Results.List[0].Type] == types.Typ[types.String] } // walkGenDecl walks a general declaration. func (f *File) walkGenDecl(d *ast.GenDecl) { f.checkShadowDecl(d) } // walkFuncLit walks a function literal. func (f *File) walkFuncLit(x *ast.FuncLit) { f.checkUnreachable(x.Body) } // walkInterfaceType walks the method signatures of an interface. func (f *File) walkInterfaceType(t *ast.InterfaceType) { for _, field := range t.Methods.List { for _, id := range field.Names { f.walkMethod(id, field.Type.(*ast.FuncType)) } } } // walkRangeStmt walks a range statement. func (f *File) walkRangeStmt(n *ast.RangeStmt) { checkRangeLoop(f, n) } // gofmt returns a string representation of the expression. func (f *File) gofmt(x ast.Expr) string { f.b.Reset() printer.Fprint(&f.b, f.fset, x) return f.b.String() } ./cmd/vet/asmdecl.go0000644000014500017510000003045212246613010013777 0ustar michaelstaff// 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. // Identify mismatches between assembly files and Go func declarations. package main import ( "bytes" "fmt" "go/ast" "go/token" "regexp" "strconv" "strings" ) // 'kind' is a kind of assembly variable. // The kinds 1, 2, 4, 8 stand for values of that size. type asmKind int // These special kinds are not valid sizes. const ( asmString asmKind = 100 + iota asmSlice asmInterface asmEmptyInterface ) // An asmArch describes assembly parameters for an architecture type asmArch struct { name string ptrSize int intSize int bigEndian bool } // An asmFunc describes the expected variables for a function on a given architecture. type asmFunc struct { arch *asmArch size int // size of all arguments vars map[string]*asmVar varByOffset map[int]*asmVar } // An asmVar describes a single assembly variable. type asmVar struct { name string kind asmKind typ string off int size int inner []*asmVar } var ( asmArch386 = asmArch{"386", 4, 4, false} asmArchArm = asmArch{"arm", 4, 4, false} asmArchAmd64 = asmArch{"amd64", 8, 8, false} arches = []*asmArch{ &asmArch386, &asmArchArm, &asmArchAmd64, } ) var ( re = regexp.MustCompile asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`) asmTEXT = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$([0-9]+)(?:-([0-9]+))?)?`) asmDATA = re(`\b(DATA|GLOBL)\b`) asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`) asmUnnamedFP = re(`[^+\-0-9]](([0-9]+)\(FP\))`) asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`) ) func asmCheck(pkg *Package) { if !vet("asmdecl") { return } // No work if no assembly files. if !pkg.hasFileWithSuffix(".s") { return } // Gather declarations. knownFunc[name][arch] is func description. knownFunc := make(map[string]map[string]*asmFunc) for _, f := range pkg.files { if f.file != nil { for _, decl := range f.file.Decls { if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil { knownFunc[decl.Name.Name] = f.asmParseDecl(decl) } } } } var fn *asmFunc for _, f := range pkg.files { if !strings.HasSuffix(f.name, ".s") { continue } Println("Checking file", f.name) // Determine architecture from file name if possible. var arch string for _, a := range arches { if strings.HasSuffix(f.name, "_"+a.name+".s") { arch = a.name break } } lines := strings.SplitAfter(string(f.content), "\n") for lineno, line := range lines { lineno++ warnf := func(format string, args ...interface{}) { f.Warnf(token.NoPos, "%s:%d: [%s] %s", f.name, lineno, arch, fmt.Sprintf(format, args...)) } if arch == "" { // Determine architecture from +build line if possible. if m := asmPlusBuild.FindStringSubmatch(line); m != nil { Fields: for _, fld := range strings.Fields(m[1]) { for _, a := range arches { if a.name == fld { arch = a.name break Fields } } } } } if m := asmTEXT.FindStringSubmatch(line); m != nil { if arch == "" { f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name) return } fn = knownFunc[m[1]][arch] if fn != nil { size, _ := strconv.Atoi(m[4]) if size != fn.size && (m[2] != "7" && !strings.Contains(m[2], "NOSPLIT") || size != 0) { warnf("wrong argument size %d; expected $...-%d", size, fn.size) } } continue } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") { // function, but not visible from Go (didn't match asmTEXT), so stop checking fn = nil continue } if asmDATA.FindStringSubmatch(line) != nil { fn = nil } if fn == nil { continue } for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) { warnf("use of unnamed argument %s", m[1]) } for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) { name := m[1] off := 0 if m[2] != "" { off, _ = strconv.Atoi(m[2]) } v := fn.vars[name] if v == nil { // Allow argframe+0(FP). if name == "argframe" && off == 0 { continue } v = fn.varByOffset[off] if v != nil { warnf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off) } else { warnf("unknown variable %s", name) } continue } asmCheckVar(warnf, fn, line, m[0], off, v) } } } } // asmParseDecl parses a function decl for expected assembly variables. func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc { var ( arch *asmArch fn *asmFunc offset int failed bool ) addVar := func(outer string, v asmVar) { if vo := fn.vars[outer]; vo != nil { vo.inner = append(vo.inner, &v) } fn.vars[v.name] = &v for i := 0; i < v.size; i++ { fn.varByOffset[v.off+i] = &v } } addParams := func(list []*ast.Field) { for i, fld := range list { // Determine alignment, size, and kind of type in declaration. var align, size int var kind asmKind names := fld.Names typ := f.gofmt(fld.Type) switch t := fld.Type.(type) { default: switch typ { default: f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ) failed = true return case "int8", "uint8", "byte", "bool": size = 1 case "int16", "uint16": size = 2 case "int32", "uint32", "float32": size = 4 case "int64", "uint64", "float64": align = arch.ptrSize size = 8 case "int", "uint": size = arch.intSize case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer": size = arch.ptrSize case "string": size = arch.ptrSize * 2 align = arch.ptrSize kind = asmString } case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr: size = arch.ptrSize case *ast.InterfaceType: align = arch.ptrSize size = 2 * arch.ptrSize if len(t.Methods.List) > 0 { kind = asmInterface } else { kind = asmEmptyInterface } case *ast.ArrayType: if t.Len == nil { size = arch.ptrSize + 2*arch.intSize align = arch.ptrSize kind = asmSlice break } f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) failed = true case *ast.StructType: f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) failed = true } if align == 0 { align = size } if kind == 0 { kind = asmKind(size) } offset += -offset & (align - 1) // Create variable for each name being declared with this type. if len(names) == 0 { name := "unnamed" if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 { // Assume assembly will refer to single unnamed result as r. name = "ret" } names = []*ast.Ident{{Name: name}} } for _, id := range names { name := id.Name addVar("", asmVar{ name: name, kind: kind, typ: typ, off: offset, size: size, }) switch kind { case 8: if arch.ptrSize == 4 { w1, w2 := "lo", "hi" if arch.bigEndian { w1, w2 = w2, w1 } addVar(name, asmVar{ name: name + "_" + w1, kind: 4, typ: "half " + typ, off: offset, size: 4, }) addVar(name, asmVar{ name: name + "_" + w2, kind: 4, typ: "half " + typ, off: offset + 4, size: 4, }) } case asmEmptyInterface: addVar(name, asmVar{ name: name + "_type", kind: asmKind(arch.ptrSize), typ: "interface type", off: offset, size: arch.ptrSize, }) addVar(name, asmVar{ name: name + "_data", kind: asmKind(arch.ptrSize), typ: "interface data", off: offset + arch.ptrSize, size: arch.ptrSize, }) case asmInterface: addVar(name, asmVar{ name: name + "_itable", kind: asmKind(arch.ptrSize), typ: "interface itable", off: offset, size: arch.ptrSize, }) addVar(name, asmVar{ name: name + "_data", kind: asmKind(arch.ptrSize), typ: "interface data", off: offset + arch.ptrSize, size: arch.ptrSize, }) case asmSlice: addVar(name, asmVar{ name: name + "_base", kind: asmKind(arch.ptrSize), typ: "slice base", off: offset, size: arch.ptrSize, }) addVar(name, asmVar{ name: name + "_len", kind: asmKind(arch.intSize), typ: "slice len", off: offset + arch.ptrSize, size: arch.intSize, }) addVar(name, asmVar{ name: name + "_cap", kind: asmKind(arch.intSize), typ: "slice cap", off: offset + arch.ptrSize + arch.intSize, size: arch.intSize, }) case asmString: addVar(name, asmVar{ name: name + "_base", kind: asmKind(arch.ptrSize), typ: "string base", off: offset, size: arch.ptrSize, }) addVar(name, asmVar{ name: name + "_len", kind: asmKind(arch.intSize), typ: "string len", off: offset + arch.ptrSize, size: arch.intSize, }) } offset += size } } } m := make(map[string]*asmFunc) for _, arch = range arches { fn = &asmFunc{ arch: arch, vars: make(map[string]*asmVar), varByOffset: make(map[int]*asmVar), } offset = 0 addParams(decl.Type.Params.List) if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 { offset += -offset & (arch.ptrSize - 1) addParams(decl.Type.Results.List) } fn.size = offset m[arch.name] = fn } if failed { return nil } return m } // asmCheckVar checks a single variable reference. func asmCheckVar(warnf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) { m := asmOpcode.FindStringSubmatch(line) if m == nil { warnf("cannot find assembly opcode") } // Determine operand sizes from instruction. // Typically the suffix suffices, but there are exceptions. var src, dst, kind asmKind op := m[1] switch fn.arch.name + "." + op { case "386.FMOVLP": src, dst = 8, 4 case "arm.MOVD": src = 8 case "arm.MOVW": src = 4 case "arm.MOVH", "arm.MOVHU": src = 2 case "arm.MOVB", "arm.MOVBU": src = 1 default: if fn.arch.name == "386" || fn.arch.name == "amd64" { if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) { // FMOVDP, FXCHD, etc src = 8 break } if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) { // FMOVFP, FXCHF, etc src = 4 break } if strings.HasSuffix(op, "SD") { // MOVSD, SQRTSD, etc src = 8 break } if strings.HasSuffix(op, "SS") { // MOVSS, SQRTSS, etc src = 4 break } if strings.HasPrefix(op, "SET") { // SETEQ, etc src = 1 break } switch op[len(op)-1] { case 'B': src = 1 case 'W': src = 2 case 'L': src = 4 case 'D', 'Q': src = 8 } } } if dst == 0 { dst = src } // Determine whether the match we're holding // is the first or second argument. if strings.Index(line, expr) > strings.Index(line, ",") { kind = dst } else { kind = src } vk := v.kind vt := v.typ switch vk { case asmInterface, asmEmptyInterface, asmString, asmSlice: // allow reference to first word (pointer) vk = v.inner[0].kind vt = v.inner[0].typ } if off != v.off { var inner bytes.Buffer for i, vi := range v.inner { if len(v.inner) > 1 { fmt.Fprintf(&inner, ",") } fmt.Fprintf(&inner, " ") if i == len(v.inner)-1 { fmt.Fprintf(&inner, "or ") } fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) } warnf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String()) return } if kind != 0 && kind != vk { var inner bytes.Buffer if len(v.inner) > 0 { fmt.Fprintf(&inner, " containing") for i, vi := range v.inner { if i > 0 && len(v.inner) > 2 { fmt.Fprintf(&inner, ",") } fmt.Fprintf(&inner, " ") if i > 0 && i == len(v.inner)-1 { fmt.Fprintf(&inner, "and ") } fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) } } warnf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String()) } } ./cmd/vet/vet_test.go0000644000014500017510000000334512246613010014225 0ustar michaelstaff// 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 main_test import ( "bytes" "os" "os/exec" "path/filepath" "runtime" "testing" ) const ( dataDir = "testdata" binary = "testvet" ) // 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) { // Windows systems can't be guaranteed to have Perl and so can't run errchk. if runtime.GOOS == "windows" { t.Skip("skipping test; no Perl on Windows") } // go build cmd := exec.Command("go", "build", "-o", binary) run(cmd, t) // defer removal of vet defer os.Remove(binary) // errchk ./testvet gos, err := filepath.Glob(filepath.Join(dataDir, "*.go")) if err != nil { t.Fatal(err) } asms, err := filepath.Glob(filepath.Join(dataDir, "*.s")) if err != nil { 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. } cmd = exec.Command(errchk, append(flags, files...)...) if !run(cmd, t) { t.Fatal("vet command failed") } } func run(c *exec.Cmd, t *testing.T) bool { output, err := c.CombinedOutput() os.Stderr.Write(output) if err != nil { t.Fatal(err) } // Errchk delights by not returning non-zero status if it finds errors, so we look at the output. // It prints "BUG" if there is a failure. if !c.ProcessState.Success() { return false } return !bytes.Contains(output, []byte("BUG")) } ./cmd/vet/buildtag.go0000644000014500017510000000442512246613010014163 0ustar michaelstaff// 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 main import ( "bytes" "fmt" "os" "strings" "unicode" ) var ( nl = []byte("\n") slashSlash = []byte("//") plusBuild = []byte("+build") ) // checkBuildTag checks that build tags are in the correct location and well-formed. func checkBuildTag(name string, data []byte) { if !vet("buildtags") { return } lines := bytes.SplitAfter(data, nl) // Determine cutpoint where +build comments are no longer valid. // They are valid in leading // comments in the file followed by // a blank line. var cutoff int for i, line := range lines { line = bytes.TrimSpace(line) if len(line) == 0 { cutoff = i continue } if bytes.HasPrefix(line, slashSlash) { continue } break } for i, line := range lines { line = bytes.TrimSpace(line) if !bytes.HasPrefix(line, slashSlash) { continue } text := bytes.TrimSpace(line[2:]) if bytes.HasPrefix(text, plusBuild) { fields := bytes.Fields(text) if !bytes.Equal(fields[0], plusBuild) { // Comment is something like +buildasdf not +build. fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) continue } if i >= cutoff { fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1) setExit(1) continue } // Check arguments. Args: for _, arg := range fields[1:] { for _, elem := range strings.Split(string(arg), ",") { if strings.HasPrefix(elem, "!!") { fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg) setExit(1) break Args } if strings.HasPrefix(elem, "!") { elem = elem[1:] } for _, c := range elem { if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg) setExit(1) break Args } } } } continue } // Comment with +build but not at beginning. if bytes.Contains(line, plusBuild) && i < cutoff { fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) continue } } } ./cmd/vet/atomic.go0000644000014500017510000000264712246613010013650 0ustar michaelstaff// 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 main import ( "go/ast" "go/token" ) // checkAtomicAssignment walks the assignment statement checking for common // mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1) func (f *File) checkAtomicAssignment(n *ast.AssignStmt) { if !vet("atomic") { return } if len(n.Lhs) != len(n.Rhs) { return } for i, right := range n.Rhs { call, ok := right.(*ast.CallExpr) if !ok { continue } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { continue } pkg, ok := sel.X.(*ast.Ident) if !ok || pkg.Name != "atomic" { continue } switch sel.Sel.Name { case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": f.checkAtomicAddAssignment(n.Lhs[i], call) } } } // checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value // to the same variable being used in the operation func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { arg := call.Args[0] broken := false if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { broken = f.gofmt(left) == f.gofmt(uarg.X) } else if star, ok := left.(*ast.StarExpr); ok { broken = f.gofmt(star.X) == f.gofmt(arg) } if broken { f.Warn(left.Pos(), "direct assignment to atomic value") } } ./cmd/vet/method.go0000644000014500017510000001277412246613010013656 0ustar michaelstaff// Copyright 2010 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 contains the code to check canonical methods. package main import ( "fmt" "go/ast" "go/printer" "strings" ) type MethodSig struct { args []string results []string } // canonicalMethods lists the input and output types for Go methods // that are checked using dynamic interface checks. Because the // checks are dynamic, such methods would not cause a compile error // if they have the wrong signature: instead the dynamic check would // fail, sometimes mysteriously. If a method is found with a name listed // here but not the input/output types listed here, vet complains. // // A few of the canonical methods have very common names. // For example, a type might implement a Scan method that // has nothing to do with fmt.Scanner, but we still want to check // the methods that are intended to implement fmt.Scanner. // To do that, the arguments that have a = prefix are treated as // signals that the canonical meaning is intended: if a Scan // method doesn't have a fmt.ScanState as its first argument, // we let it go. But if it does have a fmt.ScanState, then the // rest has to match. var canonicalMethods = map[string]MethodSig{ // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict "Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter "GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder "GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder "MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler "MarshalXML": {[]string{"*xml.Encoder", "xml.StartElement"}, []string{"error"}}, // xml.Marshaler "Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader) "ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader "ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom "ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader "Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner "Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler "UnmarshalXML": {[]string{"*xml.Decoder", "xml.StartElement"}, []string{"error"}}, // xml.Unmarshaler "UnreadByte": {[]string{}, []string{"error"}}, "UnreadRune": {[]string{}, []string{"error"}}, "WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer) "WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo } func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { if !vet("methods") { return } // Expected input/output. expect, ok := canonicalMethods[id.Name] if !ok { return } // Actual input/output args := typeFlatten(t.Params.List) var results []ast.Expr if t.Results != nil { results = typeFlatten(t.Results.List) } // Do the =s (if any) all match? if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") { return } // Everything must match. if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") { expectFmt := id.Name + "(" + argjoin(expect.args) + ")" if len(expect.results) == 1 { expectFmt += " " + argjoin(expect.results) } else if len(expect.results) > 1 { expectFmt += " (" + argjoin(expect.results) + ")" } f.b.Reset() if err := printer.Fprint(&f.b, f.fset, t); err != nil { fmt.Fprintf(&f.b, "<%s>", err) } actual := f.b.String() actual = strings.TrimPrefix(actual, "func") actual = id.Name + actual f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt) } } func argjoin(x []string) string { y := make([]string, len(x)) for i, s := range x { if s[0] == '=' { s = s[1:] } y[i] = s } return strings.Join(y, ", ") } // Turn parameter list into slice of types // (in the ast, types are Exprs). // Have to handle f(int, bool) and f(x, y, z int) // so not a simple 1-to-1 conversion. func typeFlatten(l []*ast.Field) []ast.Expr { var t []ast.Expr for _, f := range l { if len(f.Names) == 0 { t = append(t, f.Type) continue } for _ = range f.Names { t = append(t, f.Type) } } return t } // Does each type in expect with the given prefix match the corresponding type in actual? func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool { for i, x := range expect { if !strings.HasPrefix(x, prefix) { continue } if i >= len(actual) { return false } if !f.matchParamType(x, actual[i]) { return false } } if prefix == "" && len(actual) > len(expect) { return false } return true } // Does this one type match? func (f *File) matchParamType(expect string, actual ast.Expr) bool { if strings.HasPrefix(expect, "=") { expect = expect[1:] } // Strip package name if we're in that package. if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' { expect = expect[n+1:] } // Overkill but easy. f.b.Reset() printer.Fprint(&f.b, f.fset, actual) return f.b.String() == expect } ./cmd/vet/deadcode.go0000644000014500017510000001460312246613010014117 0ustar michaelstaff// 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. // Check for syntactically unreachable code. package main import ( "go/ast" "go/token" ) type deadState struct { f *File hasBreak map[ast.Stmt]bool hasGoto map[string]bool labels map[string]ast.Stmt breakTarget ast.Stmt reachable bool } // checkUnreachable checks a function body for dead code. func (f *File) checkUnreachable(body *ast.BlockStmt) { if !vet("unreachable") || body == nil { return } d := &deadState{ f: f, hasBreak: make(map[ast.Stmt]bool), hasGoto: make(map[string]bool), labels: make(map[string]ast.Stmt), } d.findLabels(body) d.reachable = true d.findDead(body) } // findLabels gathers information about the labels defined and used by stmt // and about which statements break, whether a label is involved or not. func (d *deadState) findLabels(stmt ast.Stmt) { switch x := stmt.(type) { default: d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x) case *ast.AssignStmt, *ast.BadStmt, *ast.DeclStmt, *ast.DeferStmt, *ast.EmptyStmt, *ast.ExprStmt, *ast.GoStmt, *ast.IncDecStmt, *ast.ReturnStmt, *ast.SendStmt: // no statements inside case *ast.BlockStmt: for _, stmt := range x.List { d.findLabels(stmt) } case *ast.BranchStmt: switch x.Tok { case token.GOTO: d.hasGoto[x.Label.Name] = true case token.BREAK: stmt := d.breakTarget if x.Label != nil { stmt = d.labels[x.Label.Name] } if stmt != nil { d.hasBreak[stmt] = true } } case *ast.IfStmt: d.findLabels(x.Body) if x.Else != nil { d.findLabels(x.Else) } case *ast.LabeledStmt: d.labels[x.Label.Name] = x.Stmt d.findLabels(x.Stmt) // These cases are all the same, but the x.Body only works // when the specific type of x is known, so the cases cannot // be merged. case *ast.ForStmt: outer := d.breakTarget d.breakTarget = x d.findLabels(x.Body) d.breakTarget = outer case *ast.RangeStmt: outer := d.breakTarget d.breakTarget = x d.findLabels(x.Body) d.breakTarget = outer case *ast.SelectStmt: outer := d.breakTarget d.breakTarget = x d.findLabels(x.Body) d.breakTarget = outer case *ast.SwitchStmt: outer := d.breakTarget d.breakTarget = x d.findLabels(x.Body) d.breakTarget = outer case *ast.TypeSwitchStmt: outer := d.breakTarget d.breakTarget = x d.findLabels(x.Body) d.breakTarget = outer case *ast.CommClause: for _, stmt := range x.Body { d.findLabels(stmt) } case *ast.CaseClause: for _, stmt := range x.Body { d.findLabels(stmt) } } } // findDead walks the statement looking for dead code. // If d.reachable is false on entry, stmt itself is dead. // When findDead returns, d.reachable tells whether the // statement following stmt is reachable. func (d *deadState) findDead(stmt ast.Stmt) { // Is this a labeled goto target? // If so, assume it is reachable due to the goto. // This is slightly conservative, in that we don't // check that the goto is reachable, so // L: goto L // will not provoke a warning. // But it's good enough. if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] { d.reachable = true } if !d.reachable { switch stmt.(type) { case *ast.EmptyStmt: // do not warn about unreachable empty statements default: d.f.Warnf(stmt.Pos(), "unreachable code") d.reachable = true // silence error about next statement } } switch x := stmt.(type) { default: d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x) case *ast.AssignStmt, *ast.BadStmt, *ast.DeclStmt, *ast.DeferStmt, *ast.EmptyStmt, *ast.GoStmt, *ast.IncDecStmt, *ast.SendStmt: // no control flow case *ast.BlockStmt: for _, stmt := range x.List { d.findDead(stmt) } case *ast.BranchStmt: switch x.Tok { case token.BREAK, token.GOTO, token.FALLTHROUGH: d.reachable = false case token.CONTINUE: // NOTE: We accept "continue" statements as terminating. // They are not necessary in the spec definition of terminating, // because a continue statement cannot be the final statement // before a return. But for the more general problem of syntactically // identifying dead code, continue redirects control flow just // like the other terminating statements. d.reachable = false } case *ast.ExprStmt: // Call to panic? call, ok := x.X.(*ast.CallExpr) if ok { name, ok := call.Fun.(*ast.Ident) if ok && name.Name == "panic" && name.Obj == nil { d.reachable = false } } case *ast.ForStmt: d.findDead(x.Body) d.reachable = x.Cond != nil || d.hasBreak[x] case *ast.IfStmt: d.findDead(x.Body) if x.Else != nil { r := d.reachable d.reachable = true d.findDead(x.Else) d.reachable = d.reachable || r } else { // might not have executed if statement d.reachable = true } case *ast.LabeledStmt: d.findDead(x.Stmt) case *ast.RangeStmt: d.findDead(x.Body) d.reachable = true case *ast.ReturnStmt: d.reachable = false case *ast.SelectStmt: // NOTE: Unlike switch and type switch below, we don't care // whether a select has a default, because a select without a // default blocks until one of the cases can run. That's different // from a switch without a default, which behaves like it has // a default with an empty body. anyReachable := false for _, comm := range x.Body.List { d.reachable = true for _, stmt := range comm.(*ast.CommClause).Body { d.findDead(stmt) } anyReachable = anyReachable || d.reachable } d.reachable = anyReachable || d.hasBreak[x] case *ast.SwitchStmt: anyReachable := false hasDefault := false for _, cas := range x.Body.List { cc := cas.(*ast.CaseClause) if cc.List == nil { hasDefault = true } d.reachable = true for _, stmt := range cc.Body { d.findDead(stmt) } anyReachable = anyReachable || d.reachable } d.reachable = anyReachable || d.hasBreak[x] || !hasDefault case *ast.TypeSwitchStmt: anyReachable := false hasDefault := false for _, cas := range x.Body.List { cc := cas.(*ast.CaseClause) if cc.List == nil { hasDefault = true } d.reachable = true for _, stmt := range cc.Body { d.findDead(stmt) } anyReachable = anyReachable || d.reachable } d.reachable = anyReachable || d.hasBreak[x] || !hasDefault } } ./cmd/vet/testdata/0000755000014500017510000000000012246613010013645 5ustar michaelstaff./cmd/vet/testdata/asm1.s0000644000014500017510000003521712246613010014702 0ustar michaelstaff// 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 amd64 // +build vet_test TEXT ·arg1(SB),0,$0-2 MOVB x+0(FP), AX MOVB y+1(FP), BX MOVW x+0(FP), AX // ERROR "\[amd64\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value" MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value" MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value" MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value" MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value" MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" TESTB x+0(FP), AX TESTB y+1(FP), BX TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value" TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value" TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value" TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value" TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value" TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value" TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" RET TEXT ·arg2(SB),0,$0-4 MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value" MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" MOVW x+0(FP), AX MOVW y+2(FP), BX MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value" MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value" MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value" MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value" MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value" TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value" TESTW x+0(FP), AX TESTW y+2(FP), BX TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value" TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value" TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value" TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value" TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" RET TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value" MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value" MOVL x+0(FP), AX MOVL y+4(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value" MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value" MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value" TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value" TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value" TESTL x+0(FP), AX TESTL y+4(FP), AX TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value" TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value" TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" RET TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value" MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value" MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value" MOVQ x+0(FP), AX MOVQ y+8(FP), AX MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value" TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value" TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value" TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value" TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value" TESTQ x+0(FP), AX TESTQ y+8(FP), AX TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" RET TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value" MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value" MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value" MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value" MOVQ x+0(FP), AX MOVQ y+8(FP), AX MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value" TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value" TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value" TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value" TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value" TESTQ x+0(FP), AX TESTQ y+8(FP), AX TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" RET TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value" MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value" MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value" MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value" MOVQ x+0(FP), AX MOVQ y+8(FP), AX MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value" TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value" TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value" TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value" TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value" TESTQ x+0(FP), AX TESTQ y+8(FP), AX TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" MOVL c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value" MOVL m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value" MOVL f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value" RET TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value" MOVQ x+0(FP), AX MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value" MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value" MOVQ x_base+0(FP), AX MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value" MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value" MOVQ x_len+8(FP), AX MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)" MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)" RET TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value" MOVQ x+0(FP), AX MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value" MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value" MOVQ x_base+0(FP), AX MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value" MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value" MOVQ x_len+8(FP), AX MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" MOVW x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value" MOVL x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value" MOVQ x_cap+16(FP), AX MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)" MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)" MOVQ y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)" RET TEXT ·argiface(SB),0,$0-32 MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value" MOVQ x+0(FP), AX MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value" MOVL x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value" MOVQ x_type+0(FP), AX MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" MOVW x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value" MOVL x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value" MOVQ x_data+8(FP), AX MOVW y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value" MOVL y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value" MOVQ y+16(FP), AX MOVW y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value" MOVL y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value" MOVQ y_itable+16(FP), AX MOVQ y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)" MOVW y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" MOVL y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" MOVQ y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" MOVW y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value" MOVL y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value" MOVQ y_data+24(FP), AX RET TEXT ·returnint(SB),0,$0-8 MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value" MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value" MOVL AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value" MOVQ AX, ret+0(FP) MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" RET TEXT ·returnbyte(SB),0,$0-9 MOVQ x+0(FP), AX MOVB AX, ret+8(FP) MOVW AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value" MOVL AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value" MOVQ AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value" MOVB AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)" RET TEXT ·returnnamed(SB),0,$0-41 MOVB x+0(FP), AX MOVQ AX, r1+8(FP) MOVW AX, r2+16(FP) MOVQ AX, r3+24(FP) MOVQ AX, r3_base+24(FP) MOVQ AX, r3_len+32(FP) MOVB AX, r4+40(FP) MOVL AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value" RET ./cmd/vet/testdata/rangeloop.go0000644000014500017510000000253212246613010016164 0ustar michaelstaff// Copyright 2012 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 contains tests for the rangeloop checker. package testdata func RangeLoopTests() { var s []int for i, v := range s { go func() { println(i) // ERROR "range variable i enclosed by function" println(v) // ERROR "range variable v enclosed by function" }() } for i, v := range s { defer func() { println(i) // ERROR "range variable i enclosed by function" println(v) // ERROR "range variable v enclosed by function" }() } for i := range s { go func() { println(i) // ERROR "range variable i enclosed by function" }() } for _, v := range s { go func() { println(v) // ERROR "range variable v enclosed by function" }() } for i, v := range s { go func() { println(i, v) }() println("unfortunately, we don't catch the error above because of this statement") } for i, v := range s { go func(i, v int) { println(i, v) }(i, v) } for i, v := range s { i, v := i, v go func() { println(i, v) }() } // If the key of the range statement is not an identifier // the code should not panic (it used to). var x [2]int var f int for x[0], f = range s { go func() { _ = f // ERROR "range variable f enclosed by function" }() } } ./cmd/vet/testdata/asm.go0000644000014500017510000000145412246613010014760 0ustar michaelstaff// Copyright 2010 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 ignore // This file contains declarations to test the assembly in test_asm.s. package testdata func arg1(x int8, y uint8) func arg2(x int16, y uint16) func arg4(x int32, y uint32) func arg8(x int64, y uint64) func argint(x int, y uint) func argptr(x *byte, y *byte, c chan int, m map[int]int, f func()) func argstring(x, y string) func argslice(x, y []string) func argiface(x interface{}, y interface { m() }) func returnint() int func returnbyte(x int) byte func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte) func noprof(x int) func dupok(x int) func nosplit(x int) func rodata(x int) func noptr(x int) func wrapper(x int) ./cmd/vet/testdata/assign.go0000644000014500017510000000072612246613010015465 0ustar michaelstaff// 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. // This file contains tests for the useless-assignment checker. package testdata type ST struct { x int } func (s *ST) SetX(x int) { // Accidental self-assignment; it should be "s.x = x" x = x // ERROR "self-assignment of x to x" // Another mistake s.x = s.x // ERROR "self-assignment of s.x to s.x" } ./cmd/vet/testdata/nilfunc.go0000644000014500017510000000142212246613010015631 0ustar michaelstaff// 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 testdata func F() {} type T struct { F func() } func (T) M() {} var Fv = F func Comparison() { var t T var fn func() if fn == nil || Fv == nil || t.F == nil { // no error; these func vars or fields may be nil } if F == nil { // ERROR "comparison of function F == nil is always false" panic("can't happen") } if t.M == nil { // ERROR "comparison of function M == nil is always false" panic("can't happen") } if F != nil { // ERROR "comparison of function F != nil is always true" if t.M != nil { // ERROR "comparison of function M != nil is always true" return } } panic("can't happen") } ./cmd/vet/testdata/composite.go0000644000014500017510000000220712246613010016177 0ustar michaelstaff// Copyright 2012 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 contains tests for the untagged struct literal checker. // This file contains the test for untagged struct literals. package testdata import ( "flag" "go/scanner" ) var Okay1 = []string{ "Name", "Usage", "DefValue", } var Okay2 = map[string]bool{ "Name": true, "Usage": true, "DefValue": true, } var Okay3 = struct { X string Y string Z string }{ "Name", "Usage", "DefValue", } type MyStruct struct { X string Y string Z string } var Okay4 = MyStruct{ "Name", "Usage", "DefValue", } // Testing is awkward because we need to reference things from a separate package // to trigger the warnings. var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "unkeyed fields" "Name", "Usage", nil, // Value "DefValue", } // Used to test the check for slices and arrays: If that test is disabled and // vet is run with --compositewhitelist=false, this line triggers an error. // Clumsy but sufficient. var scannerErrorListTest = scanner.ErrorList{nil, nil} ./cmd/vet/testdata/structtag.go0000644000014500017510000000060712246613010016217 0ustar michaelstaff// Copyright 2010 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 contains tests for the structtag checker. // This file contains the test for canonical struct tags. package testdata type StructTagTest struct { X int "hello" // ERROR "not compatible with reflect.StructTag.Get" } ./cmd/vet/testdata/asm2.s0000644000014500017510000003555412246613010014707 0ustar michaelstaff// 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 386 // +build vet_test TEXT ·arg1(SB),0,$0-2 MOVB x+0(FP), AX MOVB y+1(FP), BX MOVW x+0(FP), AX // ERROR "\[386\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value" MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value" MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value" MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value" MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value" MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" TESTB x+0(FP), AX TESTB y+1(FP), BX TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value" TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value" TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value" TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value" TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value" TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value" TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" RET TEXT ·arg2(SB),0,$0-4 MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value" MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" MOVW x+0(FP), AX MOVW y+2(FP), BX MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value" MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value" MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value" MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value" MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value" TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value" TESTW x+0(FP), AX TESTW y+2(FP), BX TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value" TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value" TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value" TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value" TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" RET TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value" MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value" MOVL x+0(FP), AX MOVL y+4(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value" MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value" MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value" TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value" TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value" TESTL x+0(FP), AX TESTL y+4(FP), AX TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value" TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value" TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" RET TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value" MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value" MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" MOVL x_lo+0(FP), AX MOVL x_hi+4(FP), AX MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" MOVL y_lo+8(FP), AX MOVL y_hi+12(FP), AX MOVQ x+0(FP), AX MOVQ y+8(FP), AX MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value" TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value" TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value" TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" TESTQ x+0(FP), AX TESTQ y+8(FP), AX TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" RET TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 4-byte value" MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint is 4-byte value" MOVL x+0(FP), AX MOVL y+4(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int is 4-byte value" MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint is 4-byte value" MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 4-byte value" TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint is 4-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 4-byte value" TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint is 4-byte value" TESTL x+0(FP), AX TESTL y+4(FP), AX TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int is 4-byte value" TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint is 4-byte value" TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" RET TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 4-byte value" MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); \*byte is 4-byte value" MOVL x+0(FP), AX MOVL y+4(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value" MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value" MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 4-byte value" TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); \*byte is 4-byte value" TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 4-byte value" TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); \*byte is 4-byte value" TESTL x+0(FP), AX TESTL y+4(FP), AX TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value" TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value" TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" MOVW c+8(FP), AX // ERROR "invalid MOVW of c\+8\(FP\); chan int is 4-byte value" MOVW m+12(FP), AX // ERROR "invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value" MOVW f+16(FP), AX // ERROR "invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value" RET TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 4-byte value" MOVL x+0(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); string base is 4-byte value" MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 4-byte value" MOVL x_base+0(FP), AX MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value" MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); string len is 4-byte value" MOVL x_len+4(FP), AX MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value" MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)" MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)" RET TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 4-byte value" MOVL x+0(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); slice base is 4-byte value" MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value" MOVL x_base+0(FP), AX MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value" MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value" MOVL x_len+4(FP), AX MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value" MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" MOVW x_cap+8(FP), AX // ERROR "invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value" MOVL x_cap+8(FP), AX MOVQ x_cap+8(FP), AX // ERROR "invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value" MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)" MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)" MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)" RET TEXT ·argiface(SB),0,$0-16 MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 4-byte value" MOVL x+0(FP), AX MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); interface type is 4-byte value" MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value" MOVL x_type+0(FP), AX MOVQ x_type+0(FP), AX // ERROR "invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value" MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" MOVW x_data+4(FP), AX // ERROR "invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value" MOVL x_data+4(FP), AX MOVQ x_data+4(FP), AX // ERROR "invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value" MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); interface itable is 4-byte value" MOVL y+8(FP), AX MOVQ y+8(FP), AX // ERROR "invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value" MOVW y_itable+8(FP), AX // ERROR "invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value" MOVL y_itable+8(FP), AX MOVQ y_itable+8(FP), AX // ERROR "invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value" MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)" MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" MOVL y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" MOVW y_data+12(FP), AX // ERROR "invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value" MOVL y_data+12(FP), AX MOVQ y_data+12(FP), AX // ERROR "invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value" RET TEXT ·returnint(SB),0,$0-4 MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value" MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 4-byte value" MOVL AX, ret+0(FP) MOVQ AX, ret+0(FP) // ERROR "invalid MOVQ of ret\+0\(FP\); int is 4-byte value" MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" RET TEXT ·returnbyte(SB),0,$0-5 MOVL x+0(FP), AX MOVB AX, ret+4(FP) MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value" MOVL AX, ret+4(FP) // ERROR "invalid MOVL of ret\+4\(FP\); byte is 1-byte value" MOVQ AX, ret+4(FP) // ERROR "invalid MOVQ of ret\+4\(FP\); byte is 1-byte value" MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)" RET TEXT ·returnnamed(SB),0,$0-21 MOVB x+0(FP), AX MOVL AX, r1+4(FP) MOVW AX, r2+8(FP) MOVL AX, r3+12(FP) MOVL AX, r3_base+12(FP) MOVL AX, r3_len+16(FP) MOVB AX, r4+20(FP) MOVQ AX, r1+4(FP) // ERROR "invalid MOVQ of r1\+4\(FP\); int is 4-byte value" RET ./cmd/vet/testdata/asm4.s0000644000014500017510000000073112246613010014676 0ustar michaelstaff// 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 amd64 // +build vet_test // Test cases for symbolic NOSPLIT etc. on TEXT symbols. TEXT ·noprof(SB),NOPROF,$0-8 RET TEXT ·dupok(SB),DUPOK,$0-8 RET TEXT ·nosplit(SB),NOSPLIT,$0 RET TEXT ·rodata(SB),RODATA,$0-8 RET TEXT ·noptr(SB),NOPTR|NOSPLIT,$0 RET TEXT ·wrapper(SB),WRAPPER,$0-8 RET ./cmd/vet/testdata/buildtag.go0000644000014500017510000000060612246613010015771 0ustar michaelstaff// 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. // This file contains tests for the buildtag checker. // +builder // ERROR "possible malformed \+build comment" // +build !ignore package testdata // +build toolate // ERROR "build comment appears too late in file" var _ = 3 ./cmd/vet/testdata/atomic.go0000644000014500017510000000253012246613010015450 0ustar michaelstaff// 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. // This file contains tests for the atomic checker. package testdata import ( "sync/atomic" ) type Counter uint64 func AtomicTests() { x := uint64(1) x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" y := &x *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value" var su struct{ Counter uint64 } su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value" z1 := atomic.AddUint64(&su.Counter, 1) _ = z1 // Avoid err "z declared and not used" var sp struct{ Counter *uint64 } *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value" z2 := atomic.AddUint64(sp.Counter, 1) _ = z2 // Avoid err "z declared and not used" au := []uint64{10, 20} au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value" au[1] = atomic.AddUint64(&au[0], 1) ap := []*uint64{&au[0], &au[1]} *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value" *ap[1] = atomic.AddUint64(ap[0], 1) } ./cmd/vet/testdata/method.go0000644000014500017510000000102512246613010015452 0ustar michaelstaff// Copyright 2010 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 contains tests for the canonical method checker. // This file contains the code to check canonical methods. package testdata import ( "fmt" ) type MethodTest int func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan" } type MethodTestInterface interface { ReadByte() byte // ERROR "should have signature ReadByte" } ./cmd/vet/testdata/deadcode.go0000644000014500017510000005552212246613010015735 0ustar michaelstaff// 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 ignore // This file contains tests for the dead code checker. package testdata type T int var x interface{} var c chan int func external() int // ok func _() int { } func _() int { print(1) } func _() int { print(1) return 2 println() // ERROR "unreachable code" } func _() int { L: print(1) goto L println() // ERROR "unreachable code" } func _() int { print(1) panic(2) println() // ERROR "unreachable code" } // but only builtin panic func _() int { var panic = func(int) {} print(1) panic(2) println() // ok } func _() int { { print(1) return 2 println() // ERROR "unreachable code" } println() // ok } func _() int { { print(1) return 2 } println() // ERROR "unreachable code" } func _() int { L: { print(1) goto L println() // ERROR "unreachable code" } println() // ok } func _() int { L: { print(1) goto L } println() // ERROR "unreachable code" } func _() int { print(1) { panic(2) } } func _() int { print(1) { panic(2) println() // ERROR "unreachable code" } } func _() int { print(1) { panic(2) } println() // ERROR "unreachable code" } func _() int { print(1) return 2 { // ERROR "unreachable code" } } func _() int { L: print(1) goto L { // ERROR "unreachable code" } } func _() int { print(1) panic(2) { // ERROR "unreachable code" } } func _() int { { print(1) return 2 { // ERROR "unreachable code" } } } func _() int { L: { print(1) goto L { // ERROR "unreachable code" } } } func _() int { print(1) { panic(2) { // ERROR "unreachable code" } } } func _() int { { print(1) return 2 } { // ERROR "unreachable code" } } func _() int { L: { print(1) goto L } { // ERROR "unreachable code" } } func _() int { print(1) { panic(2) } { // ERROR "unreachable code" } } func _() int { print(1) if x == nil { panic(2) } else { panic(3) } println() // ERROR "unreachable code" } func _() int { L: print(1) if x == nil { panic(2) } else { goto L } println() // ERROR "unreachable code" } func _() int { L: print(1) if x == nil { panic(2) } else if x == 1 { return 0 } else if x != 2 { panic(3) } else { goto L } println() // ERROR "unreachable code" } // if-else chain missing final else is not okay, even if the // conditions cover every possible case. func _() int { print(1) if x == nil { panic(2) } else if x != nil { panic(3) } println() // ok } func _() int { print(1) if x == nil { panic(2) } println() // ok } func _() int { L: print(1) if x == nil { panic(2) } else if x == 1 { return 0 } else if x != 1 { panic(3) } println() // ok } func _() int { print(1) for { } println() // ERROR "unreachable code" } func _() int { for { for { break } } println() // ERROR "unreachable code" } func _() int { for { for { break println() // ERROR "unreachable code" } } } func _() int { for { for { continue println() // ERROR "unreachable code" } } } func _() int { for { L: for { break L } } println() // ERROR "unreachable code" } func _() int { print(1) for { break } println() // ok } func _() int { for { for { } break // ERROR "unreachable code" } println() // ok } func _() int { L: for { for { break L } } println() // ok } func _() int { print(1) for x == nil { } println() // ok } func _() int { for x == nil { for { break } } println() // ok } func _() int { for x == nil { L: for { break L } } println() // ok } func _() int { print(1) for true { } println() // ok } func _() int { for true { for { break } } println() // ok } func _() int { for true { L: for { break L } } println() // ok } func _() int { print(1) select {} println() // ERROR "unreachable code" } func _() int { print(1) select { case <-c: print(2) panic("abc") println() // ERROR "unreachable code" } } func _() int { print(1) select { case <-c: print(2) panic("abc") } println() // ERROR "unreachable code" } func _() int { print(1) select { case <-c: print(2) for { } println() // ERROR "unreachable code" } } func _() int { print(1) select { case <-c: print(2) for { } } println() // ERROR "unreachable code" } func _() int { L: print(1) select { case <-c: print(2) panic("abc") println() // ERROR "unreachable code" case c <- 1: print(2) goto L println() // ERROR "unreachable code" } } func _() int { L: print(1) select { case <-c: print(2) panic("abc") case c <- 1: print(2) goto L } println() // ERROR "unreachable code" } func _() int { print(1) select { case <-c: print(2) panic("abc") println() // ERROR "unreachable code" default: select {} println() // ERROR "unreachable code" } } func _() int { print(1) select { case <-c: print(2) panic("abc") default: select {} } println() // ERROR "unreachable code" } func _() int { print(1) select { case <-c: print(2) } println() // ok } func _() int { L: print(1) select { case <-c: print(2) panic("abc") goto L // ERROR "unreachable code" case c <- 1: print(2) } println() // ok } func _() int { print(1) select { case <-c: print(2) panic("abc") default: print(2) } println() // ok } func _() int { print(1) select { default: break } println() // ok } func _() int { print(1) select { case <-c: print(2) panic("abc") break // ERROR "unreachable code" } println() // ok } func _() int { print(1) L: select { case <-c: print(2) for { break L } } println() // ok } func _() int { print(1) L: select { case <-c: print(2) panic("abc") case c <- 1: print(2) break L } println() // ok } func _() int { print(1) select { case <-c: print(1) panic("abc") default: select {} break // ERROR "unreachable code" } println() // ok } func _() int { print(1) switch x { case 1: print(2) panic(3) println() // ERROR "unreachable code" default: return 4 println() // ERROR "unreachable code" } } func _() int { print(1) switch x { case 1: print(2) panic(3) default: return 4 } println() // ERROR "unreachable code" } func _() int { print(1) switch x { default: return 4 println() // ERROR "unreachable code" case 1: print(2) panic(3) println() // ERROR "unreachable code" } } func _() int { print(1) switch x { default: return 4 case 1: print(2) panic(3) } println() // ERROR "unreachable code" } func _() int { print(1) switch x { case 1: print(2) fallthrough default: return 4 println() // ERROR "unreachable code" } } func _() int { print(1) switch x { case 1: print(2) fallthrough default: return 4 } println() // ERROR "unreachable code" } func _() int { print(1) switch { } println() // ok } func _() int { print(1) switch x { case 1: print(2) panic(3) case 2: return 4 } println() // ok } func _() int { print(1) switch x { case 2: return 4 case 1: print(2) panic(3) } println() // ok } func _() int { print(1) switch x { case 1: print(2) fallthrough case 2: return 4 } println() // ok } func _() int { print(1) switch x { case 1: print(2) panic(3) } println() // ok } func _() int { print(1) L: switch x { case 1: print(2) panic(3) break L // ERROR "unreachable code" default: return 4 } println() // ok } func _() int { print(1) switch x { default: return 4 break // ERROR "unreachable code" case 1: print(2) panic(3) } println() // ok } func _() int { print(1) L: switch x { case 1: print(2) for { break L } default: return 4 } println() // ok } func _() int { print(1) switch x.(type) { case int: print(2) panic(3) println() // ERROR "unreachable code" default: return 4 println() // ERROR "unreachable code" } } func _() int { print(1) switch x.(type) { case int: print(2) panic(3) default: return 4 } println() // ERROR "unreachable code" } func _() int { print(1) switch x.(type) { default: return 4 println() // ERROR "unreachable code" case int: print(2) panic(3) println() // ERROR "unreachable code" } } func _() int { print(1) switch x.(type) { default: return 4 case int: print(2) panic(3) } println() // ERROR "unreachable code" } func _() int { print(1) switch x.(type) { case int: print(2) fallthrough default: return 4 println() // ERROR "unreachable code" } } func _() int { print(1) switch x.(type) { case int: print(2) fallthrough default: return 4 } println() // ERROR "unreachable code" } func _() int { print(1) switch { } println() // ok } func _() int { print(1) switch x.(type) { case int: print(2) panic(3) case float64: return 4 } println() // ok } func _() int { print(1) switch x.(type) { case float64: return 4 case int: print(2) panic(3) } println() // ok } func _() int { print(1) switch x.(type) { case int: print(2) fallthrough case float64: return 4 } println() // ok } func _() int { print(1) switch x.(type) { case int: print(2) panic(3) } println() // ok } func _() int { print(1) L: switch x.(type) { case int: print(2) panic(3) break L // ERROR "unreachable code" default: return 4 } println() // ok } func _() int { print(1) switch x.(type) { default: return 4 break // ERROR "unreachable code" case int: print(2) panic(3) } println() // ok } func _() int { print(1) L: switch x.(type) { case int: print(2) for { break L } default: return 4 } println() // ok } // again, but without the leading print(1). // testing that everything works when the terminating statement is first. func _() int { println() // ok } func _() int { return 2 println() // ERROR "unreachable code" } func _() int { L: goto L println() // ERROR "unreachable code" } func _() int { panic(2) println() // ERROR "unreachable code" } // but only builtin panic func _() int { var panic = func(int) {} panic(2) println() // ok } func _() int { { return 2 println() // ERROR "unreachable code" } } func _() int { { return 2 } println() // ERROR "unreachable code" } func _() int { L: { goto L println() // ERROR "unreachable code" } } func _() int { L: { goto L } println() // ERROR "unreachable code" } func _() int { { panic(2) println() // ERROR "unreachable code" } } func _() int { { panic(2) } println() // ERROR "unreachable code" } func _() int { return 2 { // ERROR "unreachable code" } println() // ok } func _() int { L: goto L { // ERROR "unreachable code" } println() // ok } func _() int { panic(2) { // ERROR "unreachable code" } println() // ok } func _() int { { return 2 { // ERROR "unreachable code" } } println() // ok } func _() int { L: { goto L { // ERROR "unreachable code" } } println() // ok } func _() int { { panic(2) { // ERROR "unreachable code" } } println() // ok } func _() int { { return 2 } { // ERROR "unreachable code" } println() // ok } func _() int { L: { goto L } { // ERROR "unreachable code" } println() // ok } func _() int { { panic(2) } { // ERROR "unreachable code" } println() // ok } // again, with func literals var _ = func() int { } var _ = func() int { print(1) } var _ = func() int { print(1) return 2 println() // ERROR "unreachable code" } var _ = func() int { L: print(1) goto L println() // ERROR "unreachable code" } var _ = func() int { print(1) panic(2) println() // ERROR "unreachable code" } // but only builtin panic var _ = func() int { var panic = func(int) {} print(1) panic(2) println() // ok } var _ = func() int { { print(1) return 2 println() // ERROR "unreachable code" } println() // ok } var _ = func() int { { print(1) return 2 } println() // ERROR "unreachable code" } var _ = func() int { L: { print(1) goto L println() // ERROR "unreachable code" } println() // ok } var _ = func() int { L: { print(1) goto L } println() // ERROR "unreachable code" } var _ = func() int { print(1) { panic(2) } } var _ = func() int { print(1) { panic(2) println() // ERROR "unreachable code" } } var _ = func() int { print(1) { panic(2) } println() // ERROR "unreachable code" } var _ = func() int { print(1) return 2 { // ERROR "unreachable code" } } var _ = func() int { L: print(1) goto L { // ERROR "unreachable code" } } var _ = func() int { print(1) panic(2) { // ERROR "unreachable code" } } var _ = func() int { { print(1) return 2 { // ERROR "unreachable code" } } } var _ = func() int { L: { print(1) goto L { // ERROR "unreachable code" } } } var _ = func() int { print(1) { panic(2) { // ERROR "unreachable code" } } } var _ = func() int { { print(1) return 2 } { // ERROR "unreachable code" } } var _ = func() int { L: { print(1) goto L } { // ERROR "unreachable code" } } var _ = func() int { print(1) { panic(2) } { // ERROR "unreachable code" } } var _ = func() int { print(1) if x == nil { panic(2) } else { panic(3) } println() // ERROR "unreachable code" } var _ = func() int { L: print(1) if x == nil { panic(2) } else { goto L } println() // ERROR "unreachable code" } var _ = func() int { L: print(1) if x == nil { panic(2) } else if x == 1 { return 0 } else if x != 2 { panic(3) } else { goto L } println() // ERROR "unreachable code" } // if-else chain missing final else is not okay, even if the // conditions cover every possible case. var _ = func() int { print(1) if x == nil { panic(2) } else if x != nil { panic(3) } println() // ok } var _ = func() int { print(1) if x == nil { panic(2) } println() // ok } var _ = func() int { L: print(1) if x == nil { panic(2) } else if x == 1 { return 0 } else if x != 1 { panic(3) } println() // ok } var _ = func() int { print(1) for { } println() // ERROR "unreachable code" } var _ = func() int { for { for { break } } println() // ERROR "unreachable code" } var _ = func() int { for { for { break println() // ERROR "unreachable code" } } } var _ = func() int { for { for { continue println() // ERROR "unreachable code" } } } var _ = func() int { for { L: for { break L } } println() // ERROR "unreachable code" } var _ = func() int { print(1) for { break } println() // ok } var _ = func() int { for { for { } break // ERROR "unreachable code" } println() // ok } var _ = func() int { L: for { for { break L } } println() // ok } var _ = func() int { print(1) for x == nil { } println() // ok } var _ = func() int { for x == nil { for { break } } println() // ok } var _ = func() int { for x == nil { L: for { break L } } println() // ok } var _ = func() int { print(1) for true { } println() // ok } var _ = func() int { for true { for { break } } println() // ok } var _ = func() int { for true { L: for { break L } } println() // ok } var _ = func() int { print(1) select {} println() // ERROR "unreachable code" } var _ = func() int { print(1) select { case <-c: print(2) panic("abc") println() // ERROR "unreachable code" } } var _ = func() int { print(1) select { case <-c: print(2) panic("abc") } println() // ERROR "unreachable code" } var _ = func() int { print(1) select { case <-c: print(2) for { } println() // ERROR "unreachable code" } } var _ = func() int { print(1) select { case <-c: print(2) for { } } println() // ERROR "unreachable code" } var _ = func() int { L: print(1) select { case <-c: print(2) panic("abc") println() // ERROR "unreachable code" case c <- 1: print(2) goto L println() // ERROR "unreachable code" } } var _ = func() int { L: print(1) select { case <-c: print(2) panic("abc") case c <- 1: print(2) goto L } println() // ERROR "unreachable code" } var _ = func() int { print(1) select { case <-c: print(2) panic("abc") println() // ERROR "unreachable code" default: select {} println() // ERROR "unreachable code" } } var _ = func() int { print(1) select { case <-c: print(2) panic("abc") default: select {} } println() // ERROR "unreachable code" } var _ = func() int { print(1) select { case <-c: print(2) } println() // ok } var _ = func() int { L: print(1) select { case <-c: print(2) panic("abc") goto L // ERROR "unreachable code" case c <- 1: print(2) } println() // ok } var _ = func() int { print(1) select { case <-c: print(2) panic("abc") default: print(2) } println() // ok } var _ = func() int { print(1) select { default: break } println() // ok } var _ = func() int { print(1) select { case <-c: print(2) panic("abc") break // ERROR "unreachable code" } println() // ok } var _ = func() int { print(1) L: select { case <-c: print(2) for { break L } } println() // ok } var _ = func() int { print(1) L: select { case <-c: print(2) panic("abc") case c <- 1: print(2) break L } println() // ok } var _ = func() int { print(1) select { case <-c: print(1) panic("abc") default: select {} break // ERROR "unreachable code" } println() // ok } var _ = func() int { print(1) switch x { case 1: print(2) panic(3) println() // ERROR "unreachable code" default: return 4 println() // ERROR "unreachable code" } } var _ = func() int { print(1) switch x { case 1: print(2) panic(3) default: return 4 } println() // ERROR "unreachable code" } var _ = func() int { print(1) switch x { default: return 4 println() // ERROR "unreachable code" case 1: print(2) panic(3) println() // ERROR "unreachable code" } } var _ = func() int { print(1) switch x { default: return 4 case 1: print(2) panic(3) } println() // ERROR "unreachable code" } var _ = func() int { print(1) switch x { case 1: print(2) fallthrough default: return 4 println() // ERROR "unreachable code" } } var _ = func() int { print(1) switch x { case 1: print(2) fallthrough default: return 4 } println() // ERROR "unreachable code" } var _ = func() int { print(1) switch { } println() // ok } var _ = func() int { print(1) switch x { case 1: print(2) panic(3) case 2: return 4 } println() // ok } var _ = func() int { print(1) switch x { case 2: return 4 case 1: print(2) panic(3) } println() // ok } var _ = func() int { print(1) switch x { case 1: print(2) fallthrough case 2: return 4 } println() // ok } var _ = func() int { print(1) switch x { case 1: print(2) panic(3) } println() // ok } var _ = func() int { print(1) L: switch x { case 1: print(2) panic(3) break L // ERROR "unreachable code" default: return 4 } println() // ok } var _ = func() int { print(1) switch x { default: return 4 break // ERROR "unreachable code" case 1: print(2) panic(3) } println() // ok } var _ = func() int { print(1) L: switch x { case 1: print(2) for { break L } default: return 4 } println() // ok } var _ = func() int { print(1) switch x.(type) { case int: print(2) panic(3) println() // ERROR "unreachable code" default: return 4 println() // ERROR "unreachable code" } } var _ = func() int { print(1) switch x.(type) { case int: print(2) panic(3) default: return 4 } println() // ERROR "unreachable code" } var _ = func() int { print(1) switch x.(type) { default: return 4 println() // ERROR "unreachable code" case int: print(2) panic(3) println() // ERROR "unreachable code" } } var _ = func() int { print(1) switch x.(type) { default: return 4 case int: print(2) panic(3) } println() // ERROR "unreachable code" } var _ = func() int { print(1) switch x.(type) { case int: print(2) fallthrough default: return 4 println() // ERROR "unreachable code" } } var _ = func() int { print(1) switch x.(type) { case int: print(2) fallthrough default: return 4 } println() // ERROR "unreachable code" } var _ = func() int { print(1) switch { } println() // ok } var _ = func() int { print(1) switch x.(type) { case int: print(2) panic(3) case float64: return 4 } println() // ok } var _ = func() int { print(1) switch x.(type) { case float64: return 4 case int: print(2) panic(3) } println() // ok } var _ = func() int { print(1) switch x.(type) { case int: print(2) fallthrough case float64: return 4 } println() // ok } var _ = func() int { print(1) switch x.(type) { case int: print(2) panic(3) } println() // ok } var _ = func() int { print(1) L: switch x.(type) { case int: print(2) panic(3) break L // ERROR "unreachable code" default: return 4 } println() // ok } var _ = func() int { print(1) switch x.(type) { default: return 4 break // ERROR "unreachable code" case int: print(2) panic(3) } println() // ok } var _ = func() int { print(1) L: switch x.(type) { case int: print(2) for { break L } default: return 4 } println() // ok } // again, but without the leading print(1). // testing that everything works when the terminating statement is first. var _ = func() int { println() // ok } var _ = func() int { return 2 println() // ERROR "unreachable code" } var _ = func() int { L: goto L println() // ERROR "unreachable code" } var _ = func() int { panic(2) println() // ERROR "unreachable code" } // but only builtin panic var _ = func() int { var panic = func(int) {} panic(2) println() // ok } var _ = func() int { { return 2 println() // ERROR "unreachable code" } } var _ = func() int { { return 2 } println() // ERROR "unreachable code" } var _ = func() int { L: { goto L println() // ERROR "unreachable code" } } var _ = func() int { L: { goto L } println() // ERROR "unreachable code" } var _ = func() int { { panic(2) println() // ERROR "unreachable code" } } var _ = func() int { { panic(2) } println() // ERROR "unreachable code" } var _ = func() int { return 2 { // ERROR "unreachable code" } println() // ok } var _ = func() int { L: goto L { // ERROR "unreachable code" } println() // ok } var _ = func() int { panic(2) { // ERROR "unreachable code" } println() // ok } var _ = func() int { { return 2 { // ERROR "unreachable code" } } println() // ok } var _ = func() int { L: { goto L { // ERROR "unreachable code" } } println() // ok } var _ = func() int { { panic(2) { // ERROR "unreachable code" } } println() // ok } var _ = func() int { { return 2 } { // ERROR "unreachable code" } println() // ok } var _ = func() int { L: { goto L } { // ERROR "unreachable code" } println() // ok } var _ = func() int { { panic(2) } { // ERROR "unreachable code" } println() // ok } ./cmd/vet/testdata/shadow.go0000644000014500017510000000304412246613010015462 0ustar michaelstaff// 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. // This file contains tests for the shadowed variable checker. // Some of these errors are caught by the compiler (shadowed return parameters for example) // but are nonetheless useful tests. package testdata import "os" func ShadowRead(f *os.File, buf []byte) (err error) { var x int if f != nil { err := 3 // OK - different type. _ = err } if f != nil { _, err := f.Read(buf) // ERROR "declaration of err shadows declaration at testdata/shadow.go:13" if err != nil { return err } i := 3 // OK _ = i } if f != nil { var _, err = f.Read(buf) // ERROR "declaration of err shadows declaration at testdata/shadow.go:13" if err != nil { return err } } for i := 0; i < 10; i++ { i := i // OK: obviously intentional idiomatic redeclaration go func() { println(i) }() } var shadowTemp interface{} switch shadowTemp := shadowTemp.(type) { // OK: obviously intentional idiomatic redeclaration case int: println("OK") _ = shadowTemp } if shadowTemp := shadowTemp; true { // OK: obviously intentional idiomatic redeclaration var f *os.File // OK because f is not mentioned later in the function. // The declaration of x is a shadow because x is mentioned below. var x int // ERROR "declaration of x shadows declaration at testdata/shadow.go:14" _, _, _ = x, f, shadowTemp } // Use a couple of variables to trigger shadowing errors. _, _ = err, x return } ./cmd/vet/testdata/print.go0000644000014500017510000002767512246613010015351 0ustar michaelstaff// Copyright 2010 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 contains tests for the printf checker. package testdata import ( "fmt" "os" "unsafe" // just for test case printing unsafe.Pointer ) func UnsafePointerPrintfTest() { var up unsafe.Pointer fmt.Printf("%p, %x %X", up, up, up) } // Error methods that do not satisfy the Error interface and should be checked. type errorTest1 int func (errorTest1) Error(...interface{}) string { return "hi" } type errorTest2 int // Analogous to testing's *T type. func (errorTest2) Error(...interface{}) { } type errorTest3 int func (errorTest3) Error() { // No return value. } type errorTest4 int func (errorTest4) Error() int { // Different return type. return 3 } type errorTest5 int func (errorTest5) error() { // niladic; don't complain if no args (was bug) } // This function never executes, but it serves as a simple test for the program. // Test with make test. func PrintfTests() { var b bool var i int var r rune var s string var x float64 var p *int var imap map[int]int var fslice []float64 var c complex64 // Some good format/argtypes fmt.Printf("") fmt.Printf("%b %b %b", 3, i, x) fmt.Printf("%c %c %c %c", 3, i, 'x', r) fmt.Printf("%d %d %d", 3, i, imap) fmt.Printf("%e %e %e %e", 3e9, x, fslice, c) fmt.Printf("%E %E %E %E", 3e9, x, fslice, c) fmt.Printf("%f %f %f %f", 3e9, x, fslice, c) fmt.Printf("%F %F %F %F", 3e9, x, fslice, c) fmt.Printf("%g %g %g %g", 3e9, x, fslice, c) fmt.Printf("%G %G %G %G", 3e9, x, fslice, c) fmt.Printf("%b %b %b %b", 3e9, x, fslice, c) fmt.Printf("%o %o", 3, i) fmt.Printf("%p %p", p, nil) fmt.Printf("%q %q %q %q", 3, i, 'x', r) fmt.Printf("%s %s %s", "hi", s, []byte{65}) fmt.Printf("%t %t", true, b) fmt.Printf("%T %T", 3, i) fmt.Printf("%U %U", 3, i) fmt.Printf("%v %v", 3, i) fmt.Printf("%x %x %x %x", 3, i, "hi", s) fmt.Printf("%X %X %X %X", 3, i, "hi", s) fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3) fmt.Printf("%s", &stringerv) fmt.Printf("%v", &stringerv) fmt.Printf("%T", &stringerv) fmt.Printf("%v", notstringerv) fmt.Printf("%T", notstringerv) fmt.Printf("%q", stringerarrayv) fmt.Printf("%v", stringerarrayv) fmt.Printf("%s", stringerarrayv) fmt.Printf("%v", notstringerarrayv) fmt.Printf("%T", notstringerarrayv) fmt.Printf("%d", new(Formatter)) fmt.Printf("%*%", 2) // Ridiculous but allowed. fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say. fmt.Printf("%g", 1+2i) // Some bad format/argTypes fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type" fmt.Printf("%t", c) // ERROR "arg c for printf verb %t of wrong type" fmt.Printf("%t", 1+2i) // ERROR "arg 1 \+ 2i for printf verb %t of wrong type" fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type" fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type" fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type" fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type" fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type" fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type" fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type" fmt.Printf("%g", imap) // ERROR "arg imap for printf verb %g of wrong type" fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type" fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type" fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type" fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type" fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type" fmt.Printf("%s", byte(65)) // ERROR "arg byte\(65\) for printf verb %s of wrong type" fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type" fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type" fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type" fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type" fmt.Printf("%s", stringerv) // ERROR "arg stringerv for printf verb %s of wrong type" fmt.Printf("%t", stringerv) // ERROR "arg stringerv for printf verb %t of wrong type" fmt.Printf("%q", notstringerv) // ERROR "arg notstringerv for printf verb %q of wrong type" fmt.Printf("%t", notstringerv) // ERROR "arg notstringerv for printf verb %t of wrong type" fmt.Printf("%t", stringerarrayv) // ERROR "arg stringerarrayv for printf verb %t of wrong type" fmt.Printf("%t", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %t of wrong type" fmt.Printf("%q", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %q of wrong type" fmt.Printf("%d", Formatter(true)) // ERROR "arg Formatter\(true\) for printf verb %d of wrong type" fmt.Printf("%s", nonemptyinterface) // correct (the dynamic type of nonemptyinterface may be a stringer) fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" fmt.Println() // not an error fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call" fmt.Sprintf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Sprintf call" fmt.Printf("%s%%%d", "hi", 3) // correct fmt.Printf("%08s", "woo") // correct fmt.Printf("% 8s", "woo") // correct fmt.Printf("%.*d", 3, 3) // correct fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call" fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int" fmt.Printf("%.*d", i, 3) // correct fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int" fmt.Printf("%*%", 0.22) // ERROR "arg 0.22 for \* in printf format not of type int" fmt.Printf("%q %q", multi()...) // ok fmt.Printf("%#q", `blah`) // ok printf("now is the time", "buddy") // ERROR "no formatting directive" Printf("now is the time", "buddy") // ERROR "no formatting directive" Printf("hi") // ok const format = "%s %s\n" Printf(format, "hi", "there") Printf(format, "hi") // ERROR "missing argument for Printf verb %s: need 2, have 1" f := new(stringer) f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call" f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" Printf("d%", 2) // ERROR "missing verb at end of format string in Printf call" Printf("%d", percentDV) Printf("%d", &percentDV) Printf("%d", notPercentDV) // ERROR "arg notPercentDV for printf verb %d of wrong type" Printf("%d", ¬PercentDV) // ERROR "arg ¬PercentDV for printf verb %d of wrong type" Printf("%p", ¬PercentDV) // Works regardless: we print it as a pointer. Printf("%s", percentSV) Printf("%s", &percentSV) // Good argument reorderings. Printf("%[1]d", 3) Printf("%[1]*d", 3, 1) Printf("%[2]*[1]d", 1, 3) Printf("%[2]*.[1]*[3]d", 2, 3, 4) fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly. // Bad argument reorderings. Printf("%[xd", 3) // ERROR "illegal syntax for printf argument index" Printf("%[x]d", 3) // ERROR "illegal syntax for printf argument index" Printf("%[3]*s", "hi", 2) // ERROR "missing argument for Printf indirect \*: need 3, have 2" fmt.Sprintf("%[3]d", 2) // ERROR "missing argument for Sprintf verb %d: need 3, have 1" Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int" // Something that satisfies the error interface. var e error fmt.Println(e.Error()) // ok // Something that looks like an error interface but isn't, such as the (*T).Error method // in the testing package. var et1 errorTest1 fmt.Println(et1.Error()) // ERROR "no args in Error call" fmt.Println(et1.Error("hi")) // ok fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" var et2 errorTest2 et2.Error() // ERROR "no args in Error call" et2.Error("hi") // ok, not an error method. et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" var et3 errorTest3 et3.Error() // ok, not an error method. var et4 errorTest4 et4.Error() // ok, not an error method. var et5 errorTest5 et5.error() // ok, not an error method. // Bug: used to recur forever. Printf("%p %x", recursiveStructV, recursiveStructV.next) Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next) Printf("%p %x", recursiveSliceV, recursiveSliceV) Printf("%p %x", recursiveMapV, recursiveMapV) } // Printf is used by the test so we must declare it. func Printf(format string, args ...interface{}) { panic("don't call - testing only") } // printf is used by the test so we must declare it. func printf(format string, args ...interface{}) { panic("don't call - testing only") } // multi is used by the test. func multi() []interface{} { panic("don't call - testing only") } type stringer float64 var stringerv stringer func (*stringer) String() string { return "string" } func (*stringer) Warn(int, ...interface{}) string { return "warn" } func (*stringer) Warnf(int, string, ...interface{}) string { return "warnf" } type notstringer struct { f float64 } var notstringerv notstringer type stringerarray [4]float64 func (stringerarray) String() string { return "string" } var stringerarrayv stringerarray type notstringerarray [4]float64 var notstringerarrayv notstringerarray var nonemptyinterface = interface { f() }(nil) // A data type we can print with "%d". type percentDStruct struct { a int b []byte c *float64 } var percentDV percentDStruct // A data type we cannot print correctly with "%d". type notPercentDStruct struct { a int b []byte c bool } var notPercentDV notPercentDStruct // A data type we can print with "%s". type percentSStruct struct { a string b []byte c stringerarray } var percentSV percentSStruct type recursiveStringer int func (s recursiveStringer) String() string { fmt.Sprintf("%d", s) fmt.Sprintf("%#v", s) fmt.Sprintf("%v", s) // ERROR "arg s for printf causes recursive call to String method" fmt.Sprintf("%v", &s) // ERROR "arg &s for printf causes recursive call to String method" return fmt.Sprintln(s) // ERROR "arg s for print causes recursive call to String method" } type recursivePtrStringer int func (p *recursivePtrStringer) String() string { fmt.Sprintf("%v", *p) return fmt.Sprintln(p) // ERROR "arg p for print causes recursive call to String method" } type Formatter bool func (*Formatter) Format(fmt.State, rune) { } type RecursiveSlice []RecursiveSlice var recursiveSliceV = &RecursiveSlice{} type RecursiveMap map[int]RecursiveMap var recursiveMapV = make(RecursiveMap) type RecursiveStruct struct { next *RecursiveStruct } var recursiveStructV = &RecursiveStruct{} type RecursiveStruct1 struct { next *Recursive2Struct } type RecursiveStruct2 struct { next *Recursive1Struct } var recursiveStruct1V = &RecursiveStruct1{} ./cmd/vet/testdata/asm3.s0000644000014500017510000002127312246613010014701 0ustar michaelstaff// 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 arm // +build vet_test TEXT ·arg1(SB),0,$0-2 MOVB x+0(FP), AX MOVB y+1(FP), BX MOVH x+0(FP), AX // ERROR "\[arm\] invalid MOVH of x\+0\(FP\); int8 is 1-byte value" MOVH y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value" MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" RET TEXT ·arg2(SB),0,$0-4 MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value" MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" MOVH x+0(FP), AX MOVH y+2(FP), BX MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value" MOVW y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value" MOVH x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" MOVH y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" RET TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value" MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value" MOVW x+0(FP), AX MOVW y+4(FP), AX MOVW x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVW y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" RET TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value" MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value" MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" MOVW x_lo+0(FP), AX MOVW x_hi+4(FP), AX MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" MOVW y_lo+8(FP), AX MOVW y_hi+12(FP), AX MOVQ x+0(FP), AX MOVQ y+8(FP), AX MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" RET TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value" MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value" MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value" MOVW x+0(FP), AX MOVW y+4(FP), AX MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" RET TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20" MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value" MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value" MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value" MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value" MOVW x+0(FP), AX MOVW y+4(FP), AX MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" MOVH c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value" MOVH m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value" MOVH f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value" RET TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16" MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value" MOVW x+0(FP), AX MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value" MOVW x_base+0(FP), AX MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value" MOVW x_len+4(FP), AX MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)" MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)" RET TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24" MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value" MOVW x+0(FP), AX MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value" MOVW x_base+0(FP), AX MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value" MOVW x_len+4(FP), AX MOVH x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" MOVH x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value" MOVW x_cap+8(FP), AX MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)" MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)" MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)" RET TEXT ·argiface(SB),0,$0-16 MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value" MOVW x+0(FP), AX MOVH x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value" MOVW x_type+0(FP), AX MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" MOVH x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" MOVH x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value" MOVW x_data+4(FP), AX MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value" MOVW y+8(FP), AX MOVH y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value" MOVW y_itable+8(FP), AX MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)" MOVH y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" MOVH y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value" MOVW y_data+12(FP), AX RET TEXT ·returnint(SB),0,$0-4 MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value" MOVH AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value" MOVW AX, ret+0(FP) MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" RET TEXT ·returnbyte(SB),0,$0-5 MOVW x+0(FP), AX MOVB AX, ret+4(FP) MOVH AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value" MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value" MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)" RET TEXT ·returnnamed(SB),0,$0-21 MOVB x+0(FP), AX MOVW AX, r1+4(FP) MOVH AX, r2+8(FP) MOVW AX, r3+12(FP) MOVW AX, r3_base+12(FP) MOVW AX, r3_len+16(FP) MOVB AX, r4+20(FP) MOVB AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value" RET ./cmd/vet/testdata/buildtag_bad.go0000644000014500017510000000120012246613010016566 0ustar michaelstaff// This file contains misplaced or malformed build constraints. // The Go tool will skip it, because the constraints are invalid. // It serves only to test the tag checker during make test. // Mention +build // ERROR "possible malformed \+build comment" // +build !!bang // ERROR "invalid double negative in build constraint" // +build @#$ // ERROR "invalid non-alphanumeric build constraint" // +build toolate // ERROR "build comment appears too late in file" package bad // This is package 'bad' rather than 'main' so the erroneous build // tag doesn't end up looking like a package doc for the vet command // when examined by godoc. ./cmd/vet/shadow.go0000644000014500017510000001461612246613010013660 0ustar michaelstaff// 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. /* This file contains the code to check for shadowed variables. A shadowed variable is a variable declared in an inner scope with the same name and type as a variable in an outer scope, and where the outer variable is mentioned after the inner one is declared. (This definition can be refined; the module generates too many false positives and is not yet enabled by default.) For example: func BadRead(f *os.File, buf []byte) error { var err error for { n, err := f.Read(buf) // shadows the function variable 'err' if err != nil { break // causes return of wrong value } foo(buf) } return err } */ package main import ( "go/ast" "go/token" "code.google.com/p/go.tools/go/types" ) // Span stores the minimum range of byte positions in the file in which a // given variable (types.Object) is mentioned. It is lexically defined: it spans // from the beginning of its first mention to the end of its last mention. // A variable is considered shadowed (if *strictShadowing is off) only if the // shadowing variable is declared within the span of the shadowed variable. // In other words, if a variable is shadowed but not used after the shadowed // variable is declared, it is inconsequential and not worth complaining about. // This simple check dramatically reduces the nuisance rate for the shadowing // check, at least until something cleverer comes along. // // One wrinkle: A "naked return" is a silent use of a variable that the Span // will not capture, but the compilers catch naked returns of shadowed // variables so we don't need to. // // Cases this gets wrong (TODO): // - If a for loop's continuation statement mentions a variable redeclared in // the block, we should complain about it but don't. // - A variable declared inside a function literal can falsely be identified // as shadowing a variable in the outer function. // type Span struct { min token.Pos max token.Pos } // contains reports whether the position is inside the span. func (s Span) contains(pos token.Pos) bool { return s.min <= pos && pos < s.max } // growSpan expands the span for the object to contain the instance represented // by the identifier. func (pkg *Package) growSpan(ident *ast.Ident, obj types.Object) { if *strictShadowing { return // No need } pos := ident.Pos() end := ident.End() span, ok := pkg.spans[obj] if ok { if span.min > pos { span.min = pos } if span.max < end { span.max = end } } else { span = Span{pos, end} } pkg.spans[obj] = span } // checkShadowAssignment checks for shadowing in a short variable declaration. func (f *File) checkShadowAssignment(a *ast.AssignStmt) { if !vet("shadow") { return } if a.Tok != token.DEFINE { return } if f.idiomaticShortRedecl(a) { return } for _, expr := range a.Lhs { ident, ok := expr.(*ast.Ident) if !ok { f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier") return } f.checkShadowing(ident) } } // idiomaticShortRedecl reports whether this short declaration can be ignored for // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. func (f *File) idiomaticShortRedecl(a *ast.AssignStmt) bool { // Don't complain about deliberate redeclarations of the form // i := i // Such constructs are idiomatic in range loops to create a new variable // for each iteration. Another example is // switch n := n.(type) if len(a.Rhs) != len(a.Lhs) { return false } // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.) for i, expr := range a.Lhs { lhs, ok := expr.(*ast.Ident) if !ok { f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier") return true // Don't do any more processing. } switch rhs := a.Rhs[i].(type) { case *ast.Ident: if lhs.Name != rhs.Name { return false } case *ast.TypeAssertExpr: if id, ok := rhs.X.(*ast.Ident); ok { if lhs.Name != id.Name { return false } } } } return true } // idiomaticRedecl reports whether this declaration spec can be ignored for // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. func (f *File) idiomaticRedecl(d *ast.ValueSpec) bool { // Don't complain about deliberate redeclarations of the form // var i, j = i, j if len(d.Names) != len(d.Values) { return false } for i, lhs := range d.Names { if rhs, ok := d.Values[i].(*ast.Ident); ok { if lhs.Name != rhs.Name { return false } } } return true } // checkShadowDecl checks for shadowing in a general variable declaration. func (f *File) checkShadowDecl(d *ast.GenDecl) { if !vet("shadow") { return } if d.Tok != token.VAR { return } for _, spec := range d.Specs { valueSpec, ok := spec.(*ast.ValueSpec) if !ok { f.Badf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec") return } // Don't complain about deliberate redeclarations of the form // var i = i if f.idiomaticRedecl(valueSpec) { return } for _, ident := range valueSpec.Names { f.checkShadowing(ident) } } } // checkShadowing checks whether the identifier shadows an identifier in an outer scope. func (f *File) checkShadowing(ident *ast.Ident) { obj := f.pkg.idents[ident] if obj == nil { return } // obj.Parent.Parent is the surrounding scope. If we can find another declaration // starting from there, we have a shadowed variable. shadowed := obj.Parent().Parent().LookupParent(obj.Name()) if shadowed == nil { return } // Don't complain if it's shadowing a universe-declared variable; that's fine. if shadowed.Parent() == types.Universe { return } if *strictShadowing { // The shadowed variable must appear before this one to be an instance of shadowing. if shadowed.Pos() > ident.Pos() { return } } else { // Don't complain if the span of validity of the shadowed variable doesn't include // the shadowing variable. span, ok := f.pkg.spans[shadowed] if !ok { f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name) return } if !span.contains(ident.Pos()) { return } } // Don't complain if the types differ: that implies the programmer really wants two variables. if types.IsIdentical(obj.Type(), shadowed.Type()) { f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos())) } } ./cmd/vet/print.go0000644000014500017510000003535212246613010013527 0ustar michaelstaff// Copyright 2010 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 contains the printf-checker. package main import ( "bytes" "flag" "fmt" "go/ast" "go/token" "strconv" "strings" "unicode/utf8" "code.google.com/p/go.tools/go/exact" ) var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") // printfList records the formatted-print functions. The value is the location // of the format parameter. Names are lower-cased so the lookup is // case insensitive. var printfList = map[string]int{ "errorf": 0, "fatalf": 0, "fprintf": 1, "panicf": 0, "printf": 0, "sprintf": 0, } // printList records the unformatted-print functions. The value is the location // of the first parameter to be printed. Names are lower-cased so the lookup is // case insensitive. var printList = map[string]int{ "error": 0, "fatal": 0, "fprint": 1, "fprintln": 1, "panic": 0, "panicln": 0, "print": 0, "println": 0, "sprint": 0, "sprintln": 0, } // checkCall triggers the print-specific checks if the call invokes a print function. func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { if !vet("printf") { return } name := strings.ToLower(Name) if skip, ok := printfList[name]; ok { f.checkPrintf(call, Name, skip) return } if skip, ok := printList[name]; ok { f.checkPrint(call, Name, skip) return } } // formatState holds the parsed representation of a printf directive such as "%3.*[4]d". // It is constructed by parsePrintfVerb. type formatState struct { verb rune // the format verb: 'd' for "%d" format string // the full format string name string // Printf, Sprintf etc. flags []byte // the list of # + etc. argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call nbytes int // number of bytes of the format string consumed. indexed bool // whether an indexing expression appears: %[1]d. firstArg int // Index of first argument after the format in the Printf call. // Used only during parse. file *File call *ast.CallExpr argNum int // Which argument we're expecting to format now. indexPending bool // Whether we have an indexed argument that has not resolved. } // checkPrintf checks a call to a formatted print routine such as Printf. // call.Args[formatIndex] is (well, should be) the format argument. func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { if formatIndex >= len(call.Args) { f.Warn(call.Pos(), "too few arguments in call to", name) return } lit := f.pkg.values[call.Args[formatIndex]] if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-constant format in call to", name) } return } if lit.Kind() != exact.String { f.Badf(call.Pos(), "constant %v not a string in call to %s", lit, name) return } format := exact.StringVal(lit) firstArg := formatIndex + 1 // Arguments are immediately after format string. if !strings.Contains(format, "%") { if len(call.Args) > firstArg { f.Badf(call.Pos(), "no formatting directive in %s call", name) } return } // Hard part: check formats against args. argNum := firstArg indexed := false for i, w := 0, 0; i < len(format); i += w { w = 1 if format[i] == '%' { state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum) if state == nil { return } w = state.nbytes if state.indexed { indexed = true } if !f.okPrintfArg(call, state) { // One error per format is enough. return } if len(state.argNums) > 0 { // Continue with the next sequential argument. argNum = state.argNums[len(state.argNums)-1] + 1 } } } // Dotdotdot is hard. if call.Ellipsis.IsValid() && argNum >= len(call.Args)-1 { return } // If the arguments were direct indexed, we assume the programmer knows what's up. // Otherwise, there should be no leftover arguments. if !indexed && argNum != len(call.Args) { expect := argNum - firstArg numArgs := len(call.Args) - firstArg f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) } } // parseFlags accepts any printf flags. func (s *formatState) parseFlags() { for s.nbytes < len(s.format) { switch c := s.format[s.nbytes]; c { case '#', '0', '+', '-', ' ': s.flags = append(s.flags, c) s.nbytes++ default: return } } } // scanNum advances through a decimal number if present. func (s *formatState) scanNum() { for ; s.nbytes < len(s.format); s.nbytes++ { c := s.format[s.nbytes] if c < '0' || '9' < c { return } } } // parseIndex scans an index expression. It returns false if there is a syntax error. func (s *formatState) parseIndex() bool { if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { return true } // Argument index present. s.indexed = true s.nbytes++ // skip '[' start := s.nbytes s.scanNum() if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index") return false } arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) if err != nil { s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index: %s", err) return false } s.nbytes++ // skip ']' arg := int(arg32) arg += s.firstArg - 1 // We want to zero-index the actual arguments. s.argNum = arg s.indexPending = true return true } // parseNum scans a width or precision (or *). It returns false if there's a bad index expression. func (s *formatState) parseNum() bool { if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { if s.indexPending { // Absorb it. s.indexPending = false } s.nbytes++ s.argNums = append(s.argNums, s.argNum) s.argNum++ } else { s.scanNum() } return true } // parsePrecision scans for a precision. It returns false if there's a bad index expression. func (s *formatState) parsePrecision() bool { // If there's a period, there may be a precision. if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { s.flags = append(s.flags, '.') // Treat precision as a flag. s.nbytes++ if !s.parseIndex() { return false } if !s.parseNum() { return false } } return true } // parsePrintfVerb looks the formatting directive that begins the format string // and returns a formatState that encodes what the directive wants, without looking // at the actual arguments present in the call. The result is nil if there is an error. func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { state := &formatState{ format: format, name: name, flags: make([]byte, 0, 5), argNum: argNum, argNums: make([]int, 0, 1), nbytes: 1, // There's guaranteed to be a percent sign. indexed: false, firstArg: firstArg, file: f, call: call, } // There may be flags. state.parseFlags() indexPending := false // There may be an index. if !state.parseIndex() { return nil } // There may be a width. if !state.parseNum() { return nil } // There may be a precision. if !state.parsePrecision() { return nil } // Now a verb, possibly prefixed by an index (which we may already have). if !indexPending && !state.parseIndex() { return nil } if state.nbytes == len(state.format) { f.Badf(call.Pos(), "missing verb at end of format string in %s call", name) return nil } verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) state.verb = verb state.nbytes += w if verb != '%' { state.argNums = append(state.argNums, state.argNum) } return state } // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. type printfArgType int const ( argBool printfArgType = 1 << iota argInt argRune argString argFloat argComplex argPointer anyType printfArgType = ^0 ) type printVerb struct { verb rune // User may provide verb through Formatter; could be a rune. flags string // known flags are all ASCII typ printfArgType } // Common flag sets for printf verbs. const ( noFlag = "" numFlag = " -+.0" sharpNumFlag = " -+.0#" allFlags = " -+.0#" ) // printVerbs identifies which flags are known to printf for each verb. // TODO: A type that implements Formatter may do what it wants, and vet // will complain incorrectly. var printVerbs = []printVerb{ // '-' is a width modifier, always valid. // '.' is a precision for float, max width for strings. // '+' is required sign for numbers, Go format for %v. // '#' is alternate format for several verbs. // ' ' is spacer for numbers {'%', noFlag, 0}, {'b', numFlag, argInt | argFloat | argComplex}, {'c', "-", argRune | argInt}, {'d', numFlag, argInt}, {'e', numFlag, argFloat | argComplex}, {'E', numFlag, argFloat | argComplex}, {'f', numFlag, argFloat | argComplex}, {'F', numFlag, argFloat | argComplex}, {'g', numFlag, argFloat | argComplex}, {'G', numFlag, argFloat | argComplex}, {'o', sharpNumFlag, argInt}, {'p', "-#", argPointer}, {'q', " -+.0#", argRune | argInt | argString}, {'s', " -+.0", argString}, {'t', "-", argBool}, {'T', "-", anyType}, {'U', "-#", argRune | argInt}, {'v', allFlags, anyType}, {'x', sharpNumFlag, argRune | argInt | argString}, {'X', sharpNumFlag, argRune | argInt | argString}, } // okPrintfArg compares the formatState to the arguments actually present, // reporting any discrepancies it can discern. If the final argument is ellipsissed, // there's little it can do for that. func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { var v printVerb found := false // Linear scan is fast enough for a small list. for _, v = range printVerbs { if v.verb == state.verb { found = true break } } if !found { f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb) return false } for _, flag := range state.flags { if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag) return false } } // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all // but the final arg must be an integer. trueArgs := 1 if state.verb == '%' { trueArgs = 0 } nargs := len(state.argNums) for i := 0; i < nargs-trueArgs; i++ { argNum := state.argNums[i] if !f.argCanBeChecked(call, i, true, state) { return } arg := call.Args[argNum] if !f.matchArgType(argInt, nil, arg) { f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg)) return false } } if state.verb == '%' { return true } argNum := state.argNums[len(state.argNums)-1] if !f.argCanBeChecked(call, len(state.argNums)-1, false, state) { return false } arg := call.Args[argNum] if !f.matchArgType(v.typ, nil, arg) { typeString := "" if typ := f.pkg.types[arg]; typ != nil { typeString = typ.String() } f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) return false } if v.typ&argString != 0 && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) { f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg)) return false } return true } // recursiveStringer reports whether the provided argument is r or &r for the // fmt.Stringer receiver identifier r. func (f *File) recursiveStringer(e ast.Expr) bool { if f.lastStringerReceiver == nil { return false } var obj *ast.Object switch e := e.(type) { case *ast.Ident: obj = e.Obj case *ast.UnaryExpr: if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND { obj = id.Obj } } // We compare the underlying Object, which checks that the identifier // is the one we declared as the receiver for the String method in // which this printf appears. return obj == f.lastStringerReceiver } // argCanBeChecked reports whether the specified argument is statically present; // it may be beyond the list of arguments or in a terminal slice... argument, which // means we can't see it. func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, state *formatState) bool { argNum := state.argNums[formatArg] if argNum < 0 { // Shouldn't happen, so catch it with prejudice. panic("negative arg num") } if argNum < len(call.Args)-1 { return true // Always OK. } if call.Ellipsis.IsValid() { return false // We just can't tell; there could be many more arguments. } if argNum < len(call.Args) { return true } // There are bad indexes in the format or there are fewer arguments than the format needs. verb := fmt.Sprintf("verb %%%c", state.verb) if isStar { verb = "indirect *" } // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. f.Badf(call.Pos(), "missing argument for %s %s: need %d, have %d", state.name, verb, arg, len(call.Args)-state.firstArg) return false } // checkPrint checks a call to an unformatted print routine such as Println. // call.Args[firstArg] is the first argument to be printed. func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { isLn := strings.HasSuffix(name, "ln") isF := strings.HasPrefix(name, "F") args := call.Args // check for Println(os.Stderr, ...) if firstArg == 0 && !isF && len(args) > 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) } } } } if len(args) <= firstArg { // If we have a call to a method called Error that satisfies the Error interface, // then it's ok. Otherwise it's something like (*T).Error from the testing package // and we need to check it. if name == "Error" && f.isErrorMethodCall(call) { return } // If it's an Error call now, it's probably for printing errors. if !isLn { // Check the signature to be sure: there are niladic functions called "error". if firstArg != 0 || f.numArgsInSignature(call) != firstArg { f.Badf(call.Pos(), "no args in %s call", name) } } return } arg := args[firstArg] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) } } if isLn { // The last item, if a string, should not have a newline. arg = args[len(call.Args)-1] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.HasSuffix(lit.Value, `\n"`) { f.Badf(call.Pos(), "%s call ends with newline", name) } } } for _, arg := range args { if f.recursiveStringer(arg) { f.Badf(call.Pos(), "arg %s for print causes recursive call to String method", f.gofmt(arg)) } } } ./cmd/oracle/0000755000014500017510000000000012246613010012503 5ustar michaelstaff./cmd/oracle/oracle.el0000644000014500017510000001735612246613010014306 0ustar michaelstaff;;; ;;; Integration of the Go 'oracle' analysis tool into Emacs. ;;; ;;; To install the Go oracle, run: ;;; % export GOROOT=... GOPATH=... ;;; % go get code.google.com/p/go.tools/cmd/oracle ;;; % mv $GOPATH/bin/oracle $GOROOT/bin/ ;;; ;;; Load this file into Emacs and set go-oracle-scope to your ;;; configuration. Then, find a file of Go source code, enable ;;; go-oracle-mode, select an expression of interest, and press `C-c C-o d' ;;; (for "describe") or run one of the other go-oracle-xxx commands. ;;; ;;; TODO(adonovan): simplify installation and configuration by making ;;; oracle a subcommand of 'go tool'. (require 'compile) (require 'go-mode) (require 'cl) (defgroup go-oracle nil "Options specific to the Go oracle." :group 'go) (defcustom go-oracle-command (concat (car (go-root-and-paths)) "/bin/oracle") "The Go oracle command; the default is $GOROOT/bin/oracle." :type 'string :group 'go-oracle) (defcustom go-oracle-scope "" "The scope of the analysis. See `go-oracle-set-scope'." :type 'string :group 'go-oracle) (defvar go-oracle--scope-history nil "History of values supplied to `go-oracle-set-scope'.") (defvar go-oracle-mode-map (let ((m (make-sparse-keymap))) (define-key m (kbd "C-c C-o d") #'go-oracle-describe) (define-key m (kbd "C-c C-o f") #'go-oracle-freevars) (define-key m (kbd "C-c C-o g") #'go-oracle-callgraph) (define-key m (kbd "C-c C-o i") #'go-oracle-implements) (define-key m (kbd "C-c C-o p") #'go-oracle-peers) (define-key m (kbd "C-c C-o r") #'go-oracle-referrers) (define-key m (kbd "C-c C-o s") #'go-oracle-callstack) (define-key m (kbd "C-c C-o <") #'go-oracle-callers) (define-key m (kbd "C-c C-o >") #'go-oracle-callees) m)) ;; TODO(dominikh): Rethink set-scope some. Setting it to a file is ;; painful because it doesn't use find-file, and variables/~ aren't ;; expanded. Setting it to an import path is somewhat painful because ;; it doesn't make use of go-mode's import path completion. One option ;; would be having two different functions, but then we can't ;; automatically call it when no scope has been set. Also it wouldn't ;; easily allow specifying more than one file/package. (defun go-oracle-set-scope () "Set the scope for the Go oracle, prompting the user to edit the previous scope. The scope specifies a set of arguments, separated by spaces. It may be: 1) a set of packages whose main() functions will be analyzed. 2) a list of *.go filenames; they will treated like as a single package (see #3). 3) a single package whose main() function and/or Test* functions will be analyzed. In the common case, this is similar to the argument(s) you would specify to 'go build'." (interactive) (let ((scope (read-from-minibuffer "Go oracle scope: " go-oracle-scope nil nil 'go-oracle--scope-history))) (if (string-equal "" scope) (error "You must specify a non-empty scope for the Go oracle")) (setq go-oracle-scope scope))) (defun go-oracle--run (mode) "Run the Go oracle in the specified MODE, passing it the selected region of the current buffer. Process the output to replace each file name with a small hyperlink. Display the result." (if (not buffer-file-name) (error "Cannot use oracle on a buffer without a file name")) ;; It's not sufficient to save a modified buffer since if ;; gofmt-before-save is on the before-save-hook, saving will ;; disturb the selected region. (if (buffer-modified-p) (error "Please save the buffer before invoking go-oracle")) (if (string-equal "" go-oracle-scope) (go-oracle-set-scope)) (let* ((filename (file-truename buffer-file-name)) (posflag (if (use-region-p) (format "-pos=%s:#%d,#%d" filename (1- (go--position-bytes (region-beginning))) (1- (go--position-bytes (region-end)))) (format "-pos=%s:#%d" filename (1- (position-bytes (point)))))) ;; This would be simpler if we could just run 'go tool oracle'. (env-vars (go-root-and-paths)) (goroot-env (concat "GOROOT=" (car env-vars))) (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":")))) (with-current-buffer (get-buffer-create "*go-oracle*") (setq buffer-read-only nil) (erase-buffer) (insert "Go Oracle\n") (let ((args (append (list go-oracle-command nil t nil posflag mode) (split-string go-oracle-scope " " t)))) ;; Log the command to *Messages*, for debugging. (message "Command: %s:" args) (message nil) ; clears/shrinks minibuffer (message "Running oracle...") ;; Use dynamic binding to modify/restore the environment (let ((process-environment (list* goroot-env gopath-env process-environment))) (apply #'call-process args))) (insert "\n") (compilation-mode) (setq compilation-error-screen-columns nil) ;; Hide the file/line info to save space. ;; Replace each with a little widget. ;; compilation-mode + this loop = slooow. ;; TODO(adonovan): have oracle give us an S-expression ;; and we'll do the markup directly. (let ((buffer-read-only nil) (p 1)) (while (not (null p)) (let ((np (compilation-next-single-property-change p 'compilation-message))) ;; TODO(adonovan): this can be verbose in the *Messages* buffer. ;; (message "Post-processing link (%d%%)" (/ (* p 100) (point-max))) (if np (when (equal (line-number-at-pos p) (line-number-at-pos np)) ;; np is (typically) the space following ":"; consume it too. (put-text-property p np 'display "▶") (goto-char np) (insert " "))) (setq p np))) (message nil)) (let ((w (display-buffer (current-buffer)))) (balance-windows) (shrink-window-if-larger-than-buffer w) (set-window-point w (point-min)))))) (defun go-oracle-callees () "Show possible callees of the function call at the current point." (interactive) (go-oracle--run "callees")) (defun go-oracle-callers () "Show the set of callers of the function containing the current point." (interactive) (go-oracle--run "callers")) (defun go-oracle-callgraph () "Show the callgraph of the current program." (interactive) (go-oracle--run "callgraph")) (defun go-oracle-callstack () "Show an arbitrary path from a root of the call graph to the function containing the current point." (interactive) (go-oracle--run "callstack")) (defun go-oracle-describe () "Describe the expression at the current point." (interactive) (go-oracle--run "describe")) (defun go-oracle-implements () "Describe the 'implements' relation for types in the package containing the current point." (interactive) (go-oracle--run "implements")) (defun go-oracle-freevars () "Enumerate the free variables of the current selection." (interactive) (go-oracle--run "freevars")) (defun go-oracle-peers () "Enumerate the set of possible corresponding sends/receives for this channel receive/send operation." (interactive) (go-oracle--run "peers")) (defun go-oracle-referrers () "Enumerate all references to the object denoted by the selected identifier." (interactive) (go-oracle--run "referrers")) ;; TODO(dominikh): better docstring (define-minor-mode go-oracle-mode "Oracle minor mode for go-mode Keys specific to go-oracle-mode: \\{go-oracle-mode-map}" nil " oracle" go-oracle-mode-map) (provide 'go-oracle) ./cmd/oracle/emacs-test.bash0000755000014500017510000000271612246613010015420 0ustar michaelstaff#!/bin/bash # # Simple test of Go oracle/Emacs integration. # Requires that GOROOT and GOPATH are set. # Side effect: builds and installs oracle in $GOROOT. set -eu [ -z "$GOROOT" ] && { echo "Error: GOROOT is unset." >&2; exit 1; } [ -z "$GOPATH" ] && { echo "Error: GOPATH is unset." >&2; exit 1; } log=/tmp/$(basename $0)-$$.log thisdir=$(dirname $0) function die() { echo "Error: $@." cat $log exit 1 } >&2 trap "rm -f $log" EXIT # Build and install oracle. go get code.google.com/p/go.tools/cmd/oracle || die "'go get' failed" mv -f $GOPATH/bin/oracle $GOROOT/bin/ $GOROOT/bin/oracle >$log 2>&1 || true # (prints usage and exits 1) grep -q "Run.*help" $log || die "$GOROOT/bin/oracle not installed" # Run Emacs, set the scope to the oracle tool itself, # load ./main.go, and describe the "fmt" import. emacs --batch --no-splash --no-window-system --no-init \ --load $GOROOT/misc/emacs/go-mode.el \ --load $thisdir/oracle.el \ --eval ' (progn (setq go-oracle-scope "code.google.com/p/go.tools/cmd/oracle") (find-file "'$thisdir'/main.go") (search-forward "\"fmt\"") (backward-char) (go-oracle-describe) (princ (with-current-buffer "*go-oracle*" (buffer-substring-no-properties (point-min) (point-max)))) (kill-emacs 0)) ' main.go >$log 2>&1 || die "emacs command failed" # Check that Println is mentioned. grep -q "fmt/print.go.*func Println" $log || die "didn't find expected lines in log; got:" echo "PASS" ./cmd/oracle/main.go0000644000014500017510000001171012246613010013756 0ustar michaelstaff// 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. // oracle: a tool for answering questions about Go source code. // http://golang.org/s/oracle-design // http://golang.org/s/oracle-user-manual // // Run with -help flag or help subcommand for usage information. // package main import ( "bufio" "encoding/json" "encoding/xml" "flag" "fmt" "go/build" "io" "log" "os" "runtime" "runtime/pprof" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/oracle" ) var posFlag = flag.String("pos", "", "Filename and byte offset or extent of a syntax element about which to query, "+ "e.g. foo.go:#123,#456, bar.go:#123.") var ptalogFlag = flag.String("ptalog", "", "Location of the points-to analysis log file, or empty to disable logging.") var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.") // TODO(adonovan): eliminate or flip this flag after PTA presolver is implemented. var reflectFlag = flag.Bool("reflect", true, "Analyze reflection soundly (slow).") const useHelp = "Run 'oracle -help' for more information.\n" const helpMessage = `Go source code oracle. Usage: oracle [ ...] ... The -format flag controls the output format: plain an editor-friendly format in which every line of output is of the form "pos: text", where pos is "-" if unknown. json structured data in JSON syntax. xml structured data in XML syntax. The -pos flag is required in all modes except 'callgraph'. The mode argument determines the query to perform: callees show possible targets of selected function call callers show possible callers of selected function callgraph show complete callgraph of program callstack show path from callgraph root to selected function describe describe selected syntax: definition, methods, etc freevars show free variables of selection implements show 'implements' relation for selected package peers show send/receive corresponding to selected channel op referrers show all refs to entity denoted by selected identifier The user manual is available here: http://golang.org/s/oracle-user-manual Examples: Describe the syntax at offset 530 in this file (an import spec): % oracle -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#530 describe \ code.google.com/p/go.tools/cmd/oracle Print the callgraph of the trivial web-server in JSON format: % oracle -format=json src/pkg/net/http/triv.go callgraph ` + importer.InitialPackagesUsage var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") func init() { // 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 printHelp() { fmt.Println(helpMessage) fmt.Println("Flags:") flag.PrintDefaults() } func main() { // Don't print full help unless -help was requested. // Just gently remind users that it's there. flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) } flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { // (err has already been printed) if err == flag.ErrHelp { printHelp() } os.Exit(2) } args := flag.Args() if len(args) == 0 || args[0] == "" { fmt.Fprint(os.Stderr, "Error: a mode argument is required.\n"+useHelp) os.Exit(2) } mode := args[0] args = args[1:] if mode == "help" { printHelp() os.Exit(2) } if len(args) == 0 { fmt.Fprint(os.Stderr, "Error: no package arguments.\n"+useHelp) os.Exit(2) } // 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() { buf.Flush() f.Close() }() } } // Profiling support. if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // -format flag switch *formatFlag { case "json", "plain", "xml": // ok default: fmt.Fprintf(os.Stderr, "Error: illegal -format value: %q.\n"+useHelp, *formatFlag) os.Exit(2) } // Ask the oracle. res, err := oracle.Query(args, mode, *posFlag, ptalog, &build.Default, *reflectFlag) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s.\n", err) os.Exit(1) } // Print the result. switch *formatFlag { case "json": b, err := json.MarshalIndent(res.Serial(), "", "\t") if err != nil { fmt.Fprintf(os.Stderr, "JSON error: %s.\n", err) os.Exit(1) } os.Stdout.Write(b) case "xml": b, err := xml.MarshalIndent(res.Serial(), "", "\t") if err != nil { fmt.Fprintf(os.Stderr, "XML error: %s.\n", err) os.Exit(1) } os.Stdout.Write(b) case "plain": res.WriteTo(os.Stdout) } } ./cmd/oracle/oracle.vim0000644000014500017510000000646012246613010014473 0ustar michaelstaff" -*- text -*- " oracle.vim -- Vim integration for the Go oracle. " " Load with (e.g.) :source oracle.vim " Call with (e.g.) :GoOracleDescribe " while cursor or selection is over syntax of interest. " Run :copen to show the quick-fix file. " " This is an absolutely rudimentary integration of the Go Oracle into " Vim's quickfix mechanism and it needs a number of usability " improvements before it can be practically useful to Vim users. " Voluntary contributions welcomed! " " TODO(adonovan): " - reject buffers with no filename. " - hide all filenames in quickfix buffer. " Get the path to the Go oracle executable. func! s:go_oracle_bin() let [ext, sep] = (has('win32') || has('win64') ? ['.exe', ';'] : ['', ':']) let go_oracle = globpath(join(split($GOPATH, sep), ','), '/bin/oracle' . ext) if go_oracle == '' let go_oracle = globpath($GOROOT, '/bin/oracle' . ext) endif return go_oracle endfunction let s:go_oracle = s:go_oracle_bin() func! s:qflist(output) let qflist = [] " Parse GNU-style 'file:line.col-line.col: message' format. let mx = '^\(\a:[\\/][^:]\+\|[^:]\+\):\(\d\+\):\(\d\+\):\(.*\)$' for line in split(a:output, "\n") let ml = matchlist(line, mx) " Ignore non-match lines or warnings if ml == [] || ml[4] =~ '^ warning:' continue endif let item = { \ 'filename': ml[1], \ 'text': ml[4], \ 'lnum': ml[2], \ 'col': ml[3], \} let bnr = bufnr(fnameescape(ml[1])) if bnr != -1 let item['bufnr'] = bnr endif call add(qflist, item) endfor call setqflist(qflist) cwindow endfun func! s:getpos(l, c) if &encoding != 'utf-8' let buf = a:l == 1 ? '' : (join(getline(1, a:l-1), "\n") . "\n") let buf .= a:c == 1 ? '' : getline('.')[:a:c-2] return len(iconv(buf, &encoding, 'utf-8')) endif return line2byte(a:l) + (a:c-2) endfun func! s:RunOracle(mode, selected) range abort let fname = expand('%:p') let sname = get(g:, 'go_oracle_scope_file', fname) if a:selected != -1 let pos1 = s:getpos(line("'<"), col("'<")) let pos2 = s:getpos(line("'>"), col("'>")) let cmd = printf('%s -pos=%s:#%d,#%d %s %s', \ s:go_oracle, \ shellescape(fname), pos1, pos2, a:mode, shellescape(sname)) else let pos = s:getpos(line('.'), col('.')) let cmd = printf('%s -pos=%s:#%d %s %s', \ s:go_oracle, \ shellescape(fname), pos, a:mode, shellescape(sname)) endif call s:qflist(system(cmd)) endfun " Describe the expression at the current point. command! -range=% GoOracleDescribe \ call s:RunOracle('describe', ) " Show possible callees of the function call at the current point. command! -range=% GoOracleCallees \ call s:RunOracle('callees', ) " Show the set of callers of the function containing the current point. command! -range=% GoOracleCallers \ call s:RunOracle('callers', ) " Show the callgraph of the current program. command! -range=% GoOracleCallgraph \ call s:RunOracle('callgraph', ) " Describe the 'implements' relation for types in the " package containing the current point. command! -range=% GoOracleImplements \ call s:RunOracle('implements', ) " Enumerate the set of possible corresponding sends/receives for " this channel receive/send operation. command! -range=% GoOracleChannelPeers \ call s:RunOracle('peers', ) ./go/0000755000014500017510000000000012246613010011100 5ustar michaelstaff./go/exact/0000755000014500017510000000000012246613010012204 5ustar michaelstaff./go/exact/exact_test.go0000644000014500017510000001041112246613010014673 0ustar michaelstaff// 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 exact import ( "go/token" "strings" "testing" ) // TODO(gri) expand this test framework var tests = []string{ // unary operations `+ 0 = 0`, `- 1 = -1`, `^ 0 = -1`, `! true = false`, `! false = true`, // etc. // binary operations `"" + "" = ""`, `"foo" + "" = "foo"`, `"" + "bar" = "bar"`, `"foo" + "bar" = "foobar"`, `0 + 0 = 0`, `0 + 0.1 = 0.1`, `0 + 0.1i = 0.1i`, `0.1 + 0.9 = 1`, `1e100 + 1e100 = 2e100`, `0 - 0 = 0`, `0 - 0.1 = -0.1`, `0 - 0.1i = -0.1i`, `1e100 - 1e100 = 0`, `0 * 0 = 0`, `1 * 0.1 = 0.1`, `1 * 0.1i = 0.1i`, `1i * 1i = -1`, `0 / 0 = "division_by_zero"`, `10 / 2 = 5`, `5 / 3 = 5/3`, `5i / 3i = 5/3`, `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for / `10 % 3 = 1`, `0 & 0 = 0`, `12345 & 0 = 0`, `0xff & 0xf = 0xf`, `0 | 0 = 0`, `12345 | 0 = 12345`, `0xb | 0xa0 = 0xab`, `0 ^ 0 = 0`, `1 ^ -1 = -2`, `0 &^ 0 = 0`, `0xf &^ 1 = 0xe`, `1 &^ 0xf = 0`, // etc. // shifts `0 << 0 = 0`, `1 << 10 = 1024`, `0 >> 0 = 0`, `1024 >> 10 == 1`, // etc. // comparisons `false == false = true`, `false == true = false`, `true == false = false`, `true == true = true`, `false != false = false`, `false != true = true`, `true != false = true`, `true != true = false`, `"foo" == "bar" = false`, `"foo" != "bar" = true`, `"foo" < "bar" = false`, `"foo" <= "bar" = false`, `"foo" > "bar" = true`, `"foo" >= "bar" = true`, `0 == 0 = true`, `0 != 0 = false`, `0 < 10 = true`, `10 <= 10 = true`, `0 > 10 = false`, `10 >= 10 = true`, `1/123456789 == 1/123456789 == true`, `1/123456789 != 1/123456789 == false`, `1/123456789 < 1/123456788 == true`, `1/123456788 <= 1/123456789 == false`, `0.11 > 0.11 = false`, `0.11 >= 0.11 = true`, // etc. } func TestOps(t *testing.T) { for _, test := range tests { a := strings.Split(test, " ") i := 0 // operator index var x, x0 Value switch len(a) { case 4: // unary operation case 5: // binary operation x, x0 = val(a[0]), val(a[0]) i = 1 default: t.Errorf("invalid test case: %s", test) continue } op, ok := optab[a[i]] if !ok { panic("missing optab entry for " + a[i]) } y, y0 := val(a[i+1]), val(a[i+1]) got := doOp(x, op, y) want := val(a[i+3]) if !Compare(got, token.EQL, want) { t.Errorf("%s: got %s; want %s", test, got, want) } if x0 != nil && !Compare(x, token.EQL, x0) { t.Errorf("%s: x changed to %s", test, x) } if !Compare(y, token.EQL, y0) { t.Errorf("%s: y changed to %s", test, y) } } } // ---------------------------------------------------------------------------- // Support functions func val(lit string) Value { if len(lit) == 0 { return MakeUnknown() } switch lit { case "?": return MakeUnknown() case "true": return MakeBool(true) case "false": return MakeBool(false) } tok := token.INT switch first, last := lit[0], lit[len(lit)-1]; { case first == '"' || first == '`': tok = token.STRING lit = strings.Replace(lit, "_", " ", -1) case first == '\'': tok = token.CHAR case last == 'i': tok = token.IMAG default: if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") { tok = token.FLOAT } } return MakeFromLiteral(lit, tok) } var optab = map[string]token.Token{ "!": token.NOT, "+": token.ADD, "-": token.SUB, "*": token.MUL, "/": token.QUO, "%": token.REM, "<<": token.SHL, ">>": token.SHR, "&": token.AND, "|": token.OR, "^": token.XOR, "&^": token.AND_NOT, "==": token.EQL, "!=": token.NEQ, "<": token.LSS, "<=": token.LEQ, ">": token.GTR, ">=": token.GEQ, } func panicHandler(v *Value) { switch p := recover().(type) { case nil: // nothing to do case string: *v = MakeString(p) case error: *v = MakeString(p.Error()) default: panic(p) } } func doOp(x Value, op token.Token, y Value) (z Value) { defer panicHandler(&z) if x == nil { return UnaryOp(op, y, -1) } switch op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return MakeBool(Compare(x, op, y)) case token.SHL, token.SHR: s, _ := Int64Val(y) return Shift(x, op, uint(s)) default: return BinaryOp(x, op, y) } } ./go/exact/exact.go0000644000014500017510000004164112246613010013645 0ustar michaelstaff// 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 exact implements mathematically exact values // and operations for all Go basic types. // package exact import ( "fmt" "go/token" "math/big" "strconv" ) // Kind specifies the kind of value represented by a Value. type Kind int // Implementation note: Kinds must be enumerated in // order of increasing "complexity" (used by match). const ( // unknown values Unknown Kind = iota // non-numeric values Bool String // numeric values Int Float Complex ) // A Value represents a mathematically precise value of a given Kind. type Value interface { // Kind returns the value kind; it is always the smallest // kind in which the value can be represented exactly. Kind() Kind // String returns a human-readable form of the value. String() string // Prevent external implementations. implementsValue() } // ---------------------------------------------------------------------------- // Implementations type ( unknownVal struct{} boolVal bool stringVal string int64Val int64 intVal struct{ val *big.Int } floatVal struct{ val *big.Rat } complexVal struct{ re, im *big.Rat } ) func (unknownVal) Kind() Kind { return Unknown } func (boolVal) Kind() Kind { return Bool } func (stringVal) Kind() Kind { return String } func (int64Val) Kind() Kind { return Int } func (intVal) Kind() Kind { return Int } func (floatVal) Kind() Kind { return Float } func (complexVal) Kind() Kind { return Complex } func (unknownVal) String() string { return "unknown" } func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) } func (x stringVal) String() string { return strconv.Quote(string(x)) } func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) } func (x intVal) String() string { return x.val.String() } func (x floatVal) String() string { return x.val.String() } func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) } func (unknownVal) implementsValue() {} func (boolVal) implementsValue() {} func (stringVal) implementsValue() {} func (int64Val) implementsValue() {} func (intVal) implementsValue() {} func (floatVal) implementsValue() {} func (complexVal) implementsValue() {} // int64 bounds var ( minInt64 = big.NewInt(-1 << 63) maxInt64 = big.NewInt(1<<63 - 1) ) func normInt(x *big.Int) Value { if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 { return int64Val(x.Int64()) } return intVal{x} } func normFloat(x *big.Rat) Value { if x.IsInt() { return normInt(x.Num()) } return floatVal{x} } func normComplex(re, im *big.Rat) Value { if im.Sign() == 0 { return normFloat(re) } return complexVal{re, im} } // ---------------------------------------------------------------------------- // Factories // MakeUnknown returns the Unknown value. func MakeUnknown() Value { return unknownVal{} } // MakeBool returns the Bool value for x. func MakeBool(b bool) Value { return boolVal(b) } // MakeString returns the String value for x. func MakeString(s string) Value { return stringVal(s) } // MakeInt64 returns the Int value for x. func MakeInt64(x int64) Value { return int64Val(x) } // MakeUint64 returns the Int value for x. func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) } // MakeFloat64 returns the numeric value for x. // If x is not finite, the result is unknown. func MakeFloat64(x float64) Value { f := new(big.Rat).SetFloat64(x) if f != nil { return normFloat(f) } return unknownVal{} } // MakeFromLiteral returns the corresponding literal value. // If the literal has illegal format, the result is nil. func MakeFromLiteral(lit string, tok token.Token) Value { switch tok { case token.INT: if x, err := strconv.ParseInt(lit, 0, 64); err == nil { return int64Val(x) } if x, ok := new(big.Int).SetString(lit, 0); ok { return intVal{x} } case token.FLOAT: if x, ok := new(big.Rat).SetString(lit); ok { return normFloat(x) } case token.IMAG: if n := len(lit); n > 0 && lit[n-1] == 'i' { if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok { return normComplex(big.NewRat(0, 1), im) } } case token.CHAR: if n := len(lit); n >= 2 { if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil { return int64Val(code) } } case token.STRING: if s, err := strconv.Unquote(lit); err == nil { return stringVal(s) } } // TODO(gri) should we instead a) return unknown, or b) an error? return nil } // ---------------------------------------------------------------------------- // Accessors // BoolVal returns the Go boolean value of x, which must be a Bool. // The result is false for unknown values. func BoolVal(x Value) bool { switch x := x.(type) { case boolVal: return bool(x) case unknownVal: return false } panic(fmt.Sprintf("invalid BoolVal(%v)", x)) } // StringVal returns the Go string value of x, which must be a String. // The result is "" for unknown values. func StringVal(x Value) string { switch x := x.(type) { case stringVal: return string(x) case unknownVal: return "" } panic(fmt.Sprintf("invalidStringVal(%v)", x)) } // Int64Val returns the Go int64 value of x and whether the result is exact; // x must be an Int. If the result is not exact, its value is undefined. // The result is (0, false) for unknown values. func Int64Val(x Value) (int64, bool) { switch x := x.(type) { case int64Val: return int64(x), true case intVal: return x.val.Int64(), x.val.BitLen() <= 63 case unknownVal: return 0, false } panic(fmt.Sprintf("invalid Int64Val(%v)", x)) } // Uint64Val returns the Go uint64 value of x and whether the result is exact; // x must be an Int. If the result is not exact, its value is undefined. // The result is (0, false) for unknown values. func Uint64Val(x Value) (uint64, bool) { switch x := x.(type) { case int64Val: return uint64(x), x >= 0 case intVal: return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64 case unknownVal: return 0, false } panic(fmt.Sprintf("invalid Uint64Val(%v)", x)) } // Float64Val returns the nearest Go float64 value of x and whether the result is exact; // x must be numeric but not Complex. The result is (0, false) for unknown values. func Float64Val(x Value) (float64, bool) { switch x := x.(type) { case int64Val: f := float64(int64(x)) return f, int64Val(f) == x case intVal: return new(big.Rat).SetFrac(x.val, int1).Float64() case floatVal: return x.val.Float64() case unknownVal: return 0, false } panic(fmt.Sprintf("invalid Float64Val(%v)", x)) } // BitLen() returns the number of bits required to represent // the absolute value x in binary representation; x must be an Int. // The result is 0 for unknown values. func BitLen(x Value) int { switch x := x.(type) { case int64Val: return new(big.Int).SetInt64(int64(x)).BitLen() case intVal: return x.val.BitLen() case unknownVal: return 0 } panic(fmt.Sprintf("invalid BitLen(%v)", x)) } // Sign returns -1, 0, or 1 depending on whether // x < 0, x == 0, or x > 0. For complex values z, // the sign is 0 if z == 0, otherwise it is != 0. // The result is 1 for unknown values. func Sign(x Value) int { switch x := x.(type) { case int64Val: switch { case x < 0: return -1 case x > 0: return 1 } return 0 case intVal: return x.val.Sign() case floatVal: return x.val.Sign() case complexVal: return x.re.Sign() | x.im.Sign() case unknownVal: return 1 // avoid spurious division by zero errors } panic(fmt.Sprintf("invalid Sign(%v)", x)) } // ---------------------------------------------------------------------------- // Support for assembling/disassembling complex numbers // MakeImag returns the numeric value x*i (possibly 0); // x must be numeric but not Complex. // The result is unknown for unknown values. func MakeImag(x Value) Value { var im *big.Rat switch x := x.(type) { case int64Val: im = big.NewRat(int64(x), 1) case intVal: im = new(big.Rat).SetFrac(x.val, int1) case floatVal: im = x.val case unknownVal: return x default: panic(fmt.Sprintf("invalid MakeImag(%v)", x)) } return normComplex(rat0, im) } // Real returns the real part of x, which must be a numeric value. // The result is unknown for unknown values. func Real(x Value) Value { if z, ok := x.(complexVal); ok { return normFloat(z.re) } // TODO(gri) should we check explicit for unknownVal and disallow all others? return x } // Imag returns the imaginary part of x, which must be a numeric value. // The result is 0 for unknown values. func Imag(x Value) Value { if z, ok := x.(complexVal); ok { return normFloat(z.im) } // TODO(gri) should we check explicit for unknownVal and disallow all others? return int64Val(0) } // ---------------------------------------------------------------------------- // Operations // is32bit reports whether x can be represented using 32 bits. func is32bit(x int64) bool { const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 } // is63bit reports whether x can be represented using 63 bits. func is63bit(x int64) bool { const s = 63 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 } // UnaryOp returns the result of the unary expression op y. // The operation must be defined for the operand. // If size >= 0 it specifies the ^ (xor) result size in bytes. // func UnaryOp(op token.Token, y Value, size int) Value { switch op { case token.ADD: switch y.(type) { case unknownVal, int64Val, intVal, floatVal, complexVal: return y } case token.SUB: switch y := y.(type) { case int64Val: if z := -y; z != y { return z // no overflow } return normInt(new(big.Int).Neg(big.NewInt(int64(y)))) case intVal: return normInt(new(big.Int).Neg(y.val)) case floatVal: return normFloat(new(big.Rat).Neg(y.val)) case complexVal: return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im)) } case token.XOR: var z big.Int switch y := y.(type) { case int64Val: z.Not(big.NewInt(int64(y))) case intVal: z.Not(y.val) default: goto Error } // For unsigned types, the result will be negative and // thus "too large": We must limit the result size to // the type's size. if size >= 0 { s := uint(size) * 8 z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)< ord(y) { y, x = match(y, x) return x, y } // ord(x) <= ord(y) switch x := x.(type) { case unknownVal, boolVal, stringVal, complexVal: return x, y case int64Val: switch y := y.(type) { case int64Val: return x, y case intVal: return intVal{big.NewInt(int64(x))}, y case floatVal: return floatVal{big.NewRat(int64(x), 1)}, y case complexVal: return complexVal{big.NewRat(int64(x), 1), rat0}, y } case intVal: switch y := y.(type) { case intVal: return x, y case floatVal: return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y case complexVal: return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y } case floatVal: switch y := y.(type) { case floatVal: return x, y case complexVal: return complexVal{x.val, rat0}, y } } panic("unreachable") } // BinaryOp returns the result of the binary expression x op y. // The operation must be defined for the operands. // To force integer division of Int operands, use op == token.QUO_ASSIGN // instead of token.QUO; the result is guaranteed to be Int in this case. // Division by zero leads to a run-time panic. // func BinaryOp(x Value, op token.Token, y Value) Value { x, y = match(x, y) switch x := x.(type) { case unknownVal: return x case boolVal: y := y.(boolVal) switch op { case token.LAND: return x && y case token.LOR: return x || y } case int64Val: a := int64(x) b := int64(y.(int64Val)) var c int64 switch op { case token.ADD: if !is63bit(a) || !is63bit(b) { return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b))) } c = a + b case token.SUB: if !is63bit(a) || !is63bit(b) { return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b))) } c = a - b case token.MUL: if !is32bit(a) || !is32bit(b) { return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b))) } c = a * b case token.QUO: return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b))) case token.QUO_ASSIGN: // force integer division c = a / b case token.REM: c = a % b case token.AND: c = a & b case token.OR: c = a | b case token.XOR: c = a ^ b case token.AND_NOT: c = a &^ b default: goto Error } return int64Val(c) case intVal: a := x.val b := y.(intVal).val var c big.Int switch op { case token.ADD: c.Add(a, b) case token.SUB: c.Sub(a, b) case token.MUL: c.Mul(a, b) case token.QUO: return normFloat(new(big.Rat).SetFrac(a, b)) case token.QUO_ASSIGN: // force integer division c.Quo(a, b) case token.REM: c.Rem(a, b) case token.AND: c.And(a, b) case token.OR: c.Or(a, b) case token.XOR: c.Xor(a, b) case token.AND_NOT: c.AndNot(a, b) default: goto Error } return normInt(&c) case floatVal: a := x.val b := y.(floatVal).val var c big.Rat switch op { case token.ADD: c.Add(a, b) case token.SUB: c.Sub(a, b) case token.MUL: c.Mul(a, b) case token.QUO: c.Quo(a, b) default: goto Error } return normFloat(&c) case complexVal: y := y.(complexVal) a, b := x.re, x.im c, d := y.re, y.im var re, im big.Rat switch op { case token.ADD: // (a+c) + i(b+d) re.Add(a, c) im.Add(b, d) case token.SUB: // (a-c) + i(b-d) re.Sub(a, c) im.Sub(b, d) case token.MUL: // (ac-bd) + i(bc+ad) var ac, bd, bc, ad big.Rat ac.Mul(a, c) bd.Mul(b, d) bc.Mul(b, c) ad.Mul(a, d) re.Sub(&ac, &bd) im.Add(&bc, &ad) case token.QUO: // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd var ac, bd, bc, ad, s, cc, dd big.Rat ac.Mul(a, c) bd.Mul(b, d) bc.Mul(b, c) ad.Mul(a, d) cc.Mul(c, c) dd.Mul(d, d) s.Add(&cc, &dd) re.Add(&ac, &bd) re.Quo(&re, &s) im.Sub(&bc, &ad) im.Quo(&im, &s) default: goto Error } return normComplex(&re, &im) case stringVal: if op == token.ADD { return x + y.(stringVal) } } Error: panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y)) } // Shift returns the result of the shift expression x op s // with op == token.SHL or token.SHR (<< or >>). x must be // an Int. // func Shift(x Value, op token.Token, s uint) Value { switch x := x.(type) { case unknownVal: return x case int64Val: if s == 0 { return x } switch op { case token.SHL: z := big.NewInt(int64(x)) return normInt(z.Lsh(z, s)) case token.SHR: return x >> s } case intVal: if s == 0 { return x } var z big.Int switch op { case token.SHL: return normInt(z.Lsh(x.val, s)) case token.SHR: return normInt(z.Rsh(x.val, s)) } } panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s)) } func cmpZero(x int, op token.Token) bool { switch op { case token.EQL: return x == 0 case token.NEQ: return x != 0 case token.LSS: return x < 0 case token.LEQ: return x <= 0 case token.GTR: return x > 0 case token.GEQ: return x >= 0 } panic("unreachable") } // Compare returns the result of the comparison x op y. // The comparison must be defined for the operands. // func Compare(x Value, op token.Token, y Value) bool { x, y = match(x, y) switch x := x.(type) { case unknownVal: return true case boolVal: y := y.(boolVal) switch op { case token.EQL: return x == y case token.NEQ: return x != y } case int64Val: y := y.(int64Val) switch op { case token.EQL: return x == y case token.NEQ: return x != y case token.LSS: return x < y case token.LEQ: return x <= y case token.GTR: return x > y case token.GEQ: return x >= y } case intVal: return cmpZero(x.val.Cmp(y.(intVal).val), op) case floatVal: return cmpZero(x.val.Cmp(y.(floatVal).val), op) case complexVal: y := y.(complexVal) re := x.re.Cmp(y.re) im := x.im.Cmp(y.im) switch op { case token.EQL: return re == 0 && im == 0 case token.NEQ: return re != 0 || im != 0 } case stringVal: y := y.(stringVal) switch op { case token.EQL: return x == y case token.NEQ: return x != y case token.LSS: return x < y case token.LEQ: return x <= y case token.GTR: return x > y case token.GEQ: return x >= y } } panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y)) } ./go/types/0000755000014500017510000000000012246613010012244 5ustar michaelstaff./go/types/package.go0000644000014500017510000000352212246613010014170 0ustar michaelstaff// 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 types import "fmt" // A Package describes a Go package. type Package struct { path string name string scope *Scope complete bool imports []*Package fake bool // scope lookup errors are silently dropped if package is fake (internal use only) } // NewPackage returns a new Package for the given package path, // name, and scope. The package is not complete and contains no // explicit imports. func NewPackage(path, name string, scope *Scope) *Package { return &Package{path: path, name: name, scope: scope} } // Path returns the package path. func (pkg *Package) Path() string { return pkg.path } // Name returns the package name. func (pkg *Package) Name() string { return pkg.name } // Scope returns the (complete or incomplete) package scope // holding the objects declared at package level (TypeNames, // Consts, Vars, and Funcs). func (pkg *Package) Scope() *Scope { return pkg.scope } // A package is complete if its scope contains (at least) all // exported objects; otherwise it is incomplete. func (pkg *Package) Complete() bool { return pkg.complete } // MarkComplete marks a package as complete. func (pkg *Package) MarkComplete() { pkg.complete = true } // Imports returns the list of packages explicitly imported by // pkg; the list is in source order. Package unsafe is excluded. func (pkg *Package) Imports() []*Package { return pkg.imports } // SetImports sets the list of explicitly imported packages to list. // It is the caller's responsibility to make sure list elements are unique. func (pkg *Package) SetImports(list []*Package) { pkg.imports = list } func (pkg *Package) String() string { return fmt.Sprintf("package %s (%s)", pkg.name, pkg.path) } ./go/types/predicates.go0000644000014500017510000001767712246613010014740 0ustar michaelstaff// Copyright 2012 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 implements commonly used type predicates. package types import "sort" func isNamed(typ Type) bool { if _, ok := typ.(*Basic); ok { return ok } _, ok := typ.(*Named) return ok } func isBoolean(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsBoolean != 0 } func isInteger(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsInteger != 0 } func isUnsigned(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsUnsigned != 0 } func isFloat(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsFloat != 0 } func isComplex(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsComplex != 0 } func isNumeric(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsNumeric != 0 } func isString(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsString != 0 } func isTyped(typ Type) bool { t, ok := typ.Underlying().(*Basic) return !ok || t.info&IsUntyped == 0 } func isUntyped(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsUntyped != 0 } func isOrdered(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsOrdered != 0 } func isConstType(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.info&IsConstType != 0 } func isInterface(typ Type) bool { _, ok := typ.Underlying().(*Interface) return ok } func isComparable(typ Type) bool { switch t := typ.Underlying().(type) { case *Basic: // assume invalid types to be comparable // to avoid follow-up errors return t.kind != UntypedNil case *Pointer, *Interface, *Chan: return true case *Struct: for _, f := range t.fields { if !isComparable(f.typ) { return false } } return true case *Array: return isComparable(t.elem) } return false } // hasNil reports whether a type includes the nil value. func hasNil(typ Type) bool { switch t := typ.Underlying().(type) { case *Basic: return t.kind == UnsafePointer case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: return true } return false } // IsIdentical reports whether x and y are identical. func IsIdentical(x, y Type) bool { return isIdenticalInternal(x, y, nil) } // An ifacePair is a node in a stack of interface type pairs compared for identity. type ifacePair struct { x, y *Interface prev *ifacePair } func (p *ifacePair) identical(q *ifacePair) bool { return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x } func isIdenticalInternal(x, y Type, p *ifacePair) bool { if x == y { return true } switch x := x.(type) { case *Basic: // Basic types are singletons except for the rune and byte // aliases, thus we cannot solely rely on the x == y check // above. if y, ok := y.(*Basic); ok { return x.kind == y.kind } case *Array: // Two array types are identical if they have identical element types // and the same array length. if y, ok := y.(*Array); ok { return x.len == y.len && isIdenticalInternal(x.elem, y.elem, p) } case *Slice: // Two slice types are identical if they have identical element types. if y, ok := y.(*Slice); ok { return isIdenticalInternal(x.elem, y.elem, p) } case *Struct: // Two struct types are identical if they have the same sequence of fields, // and if corresponding fields have the same names, and identical types, // and identical tags. Two anonymous fields are considered to have the same // name. Lower-case field names from different packages are always different. if y, ok := y.(*Struct); ok { if x.NumFields() == y.NumFields() { for i, f := range x.fields { g := y.fields[i] if f.anonymous != g.anonymous || x.Tag(i) != y.Tag(i) || !f.sameId(g.pkg, g.name) || !isIdenticalInternal(f.typ, g.typ, p) { return false } } return true } } case *Pointer: // Two pointer types are identical if they have identical base types. if y, ok := y.(*Pointer); ok { return isIdenticalInternal(x.base, y.base, p) } case *Tuple: // Two tuples types are identical if they have the same number of elements // and corresponding elements have identical types. if y, ok := y.(*Tuple); ok { if x.Len() == y.Len() { if x != nil { for i, v := range x.vars { w := y.vars[i] if !isIdenticalInternal(v.typ, w.typ, p) { return false } } } return true } } case *Signature: // Two function types are identical if they have the same number of parameters // and result values, corresponding parameter and result types are identical, // and either both functions are variadic or neither is. Parameter and result // names are not required to match. if y, ok := y.(*Signature); ok { return x.isVariadic == y.isVariadic && isIdenticalInternal(x.params, y.params, p) && isIdenticalInternal(x.results, y.results, p) } case *Interface: // Two interface types are identical if they have the same set of methods with // the same names and identical function types. Lower-case method names from // different packages are always different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { a := x.allMethods b := y.allMethods if len(a) == len(b) { // Interface types are the only types where cycles can occur // that are not "terminated" via named types; and such cycles // can only be created via method parameter types that are // anonymous interfaces (directly or indirectly) embedding // the current interface. Example: // // type T interface { // m() interface{T} // } // // If two such (differently named) interfaces are compared, // endless recursion occurs if the cycle is not detected. // // If x and y were compared before, they must be equal // (if they were not, the recursion would have stopped); // search the ifacePair stack for the same pair. // // This is a quadratic algorithm, but in practice these stacks // are extremely short (bounded by the nesting depth of interface // type declarations that recur via parameter types, an extremely // rare occurrence). An alternative implementation might use a // "visited" map, but that is probably less efficient overall. q := &ifacePair{x, y, p} for p != nil { if p.identical(q) { return true // same pair was compared before } p = p.prev } if debug { assert(sort.IsSorted(byUniqueMethodName(a))) assert(sort.IsSorted(byUniqueMethodName(b))) } for i, f := range a { g := b[i] if f.Id() != g.Id() || !isIdenticalInternal(f.typ, g.typ, q) { return false } } return true } } case *Map: // Two map types are identical if they have identical key and value types. if y, ok := y.(*Map); ok { return isIdenticalInternal(x.key, y.key, p) && isIdenticalInternal(x.elem, y.elem, p) } case *Chan: // Two channel types are identical if they have identical value types // and the same direction. if y, ok := y.(*Chan); ok { return x.dir == y.dir && isIdenticalInternal(x.elem, y.elem, p) } case *Named: // Two named types are identical if their type names originate // in the same type declaration. if y, ok := y.(*Named); ok { return x.obj == y.obj } default: unreachable() } return false } // 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. // func defaultType(typ Type) Type { if t, ok := typ.(*Basic); ok { k := t.kind switch k { case UntypedBool: k = Bool case UntypedInt: k = Int case UntypedRune: k = Rune case UntypedFloat: k = Float64 case UntypedComplex: k = Complex128 case UntypedString: k = String } typ = Typ[k] } return typ } ./go/types/exprstring_test.go0000644000014500017510000000360412246613010016042 0ustar michaelstaff// 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 types_test import ( "go/parser" "testing" . "code.google.com/p/go.tools/go/types" ) var testExprs = []testEntry{ // basic type literals dup("x"), dup("true"), dup("42"), dup("3.1415"), dup("2.71828i"), dup(`'a'`), dup(`"foo"`), dup("`bar`"), // func and composite literals {"func(){}", "(func() literal)"}, {"func(x int) complex128 {}", "(func(x int) complex128 literal)"}, {"[]int{1, 2, 3}", "([]int literal)"}, // non-type expressions dup("(x)"), dup("x.f"), dup("a[i]"), dup("s[:]"), dup("s[i:]"), dup("s[:j]"), dup("s[i:j]"), dup("s[::]"), dup("s[i::]"), dup("s[:j:]"), dup("s[::k]"), dup("s[i:j:]"), dup("s[i::k]"), dup("s[:j:k]"), dup("s[i:j:k]"), dup("x.(T)"), dup("x.([10]int)"), dup("x.([...]int)"), dup("x.(struct{})"), dup("x.(struct{x int; y, z float32; E})"), dup("x.(func())"), dup("x.(func(x int))"), dup("x.(func() int)"), dup("x.(func(x, y int, z float32) (r int))"), dup("x.(func(a, b, c int))"), dup("x.(func(x ...T))"), dup("x.(interface{})"), dup("x.(interface{m(); n(x int); E})"), dup("x.(interface{m(); n(x int) T; E; F})"), dup("x.(map[K]V)"), dup("x.(chan E)"), dup("x.(<-chan E)"), dup("x.(chan<- chan int)"), dup("x.(chan<- <-chan int)"), dup("x.(<-chan chan int)"), dup("x.(chan (<-chan int))"), dup("f()"), dup("f(x)"), dup("int(x)"), dup("f(x, x + y)"), dup("f(s...)"), dup("f(a, s...)"), dup("*x"), dup("&x"), dup("x + y"), dup("x + y << (2 * s)"), } func TestExprString(t *testing.T) { for _, test := range testExprs { x, err := parser.ParseExpr(test.src) if err != nil { t.Errorf("%s: %s", test.src, err) continue } if got := ExprString(x); got != test.str { t.Errorf("%s: got %s, want %s", test.src, got, test.str) } } } ./go/types/methodset.go0000644000014500017510000001660412246613010014576 0ustar michaelstaff// 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. // This file implements method sets. package types import ( "bytes" "fmt" "sort" "sync" ) // A MethodSet is an ordered set of concrete or abstract (interface) methods; // a method is a MethodVal selection. // The zero value for a MethodSet is a ready-to-use empty method set. type MethodSet struct { list []*Selection } func (s *MethodSet) String() string { if s.Len() == 0 { return "MethodSet {}" } var buf bytes.Buffer fmt.Fprintln(&buf, "MethodSet {") for _, f := range s.list { fmt.Fprintf(&buf, "\t%s\n", f) } fmt.Fprintln(&buf, "}") return buf.String() } // Len returns the number of methods in s. func (s *MethodSet) Len() int { return len(s.list) } // At returns the i'th method in s for 0 <= i < s.Len(). func (s *MethodSet) At(i int) *Selection { return s.list[i] } // Lookup returns the method with matching package and name, or nil if not found. func (s *MethodSet) Lookup(pkg *Package, name string) *Selection { if s.Len() == 0 { return nil } key := Id(pkg, name) i := sort.Search(len(s.list), func(i int) bool { m := s.list[i] return m.obj.Id() >= key }) if i < len(s.list) { m := s.list[i] if m.obj.Id() == key { return m } } return nil } // Shared empty method set. var emptyMethodSet MethodSet // A cachedMethodSet provides access to a method set // for a given type by computing it once on demand, // and then caching it for future use. Threadsafe. type cachedMethodSet struct { mset *MethodSet mu sync.RWMutex // protects mset } // Of returns the (possibly cached) method set for typ. // Threadsafe. func (c *cachedMethodSet) of(typ Type) *MethodSet { c.mu.RLock() mset := c.mset c.mu.RUnlock() if mset == nil { mset = NewMethodSet(typ) c.mu.Lock() c.mset = mset c.mu.Unlock() } return mset } // NewMethodSet computes the method set for the given type T. // It always returns a non-nil method set, even if it is empty. func NewMethodSet(T Type) *MethodSet { // WARNING: The code in this function is extremely subtle - do not modify casually! // This function and lookupFieldOrMethod should be kept in sync. // method set up to the current depth, allocated lazily var base methodSet typ, isPtr := deref(T) named, _ := typ.(*Named) // *typ where typ is an interface has no methods. if isPtr { utyp := typ if named != nil { utyp = named.underlying } if _, ok := utyp.(*Interface); ok { return &emptyMethodSet } } // Start with typ as single entry at shallowest depth. // If typ is not a named type, insert a nil type instead. current := []embeddedType{{named, nil, isPtr, false}} // named types that we have seen already, allocated lazily var seen map[*Named]bool // collect methods at current depth for len(current) > 0 { var next []embeddedType // embedded types found at current depth // field and method sets at current depth, allocated lazily var fset fieldSet var mset methodSet for _, e := range current { // The very first time only, e.typ may be nil. // In this case, we don't have a named type and // we simply continue with the underlying type. if e.typ != nil { if seen[e.typ] { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth // were consolidated before). The type at that depth shadows // this same type at the current depth, so we can ignore // this one. continue } if seen == nil { seen = make(map[*Named]bool) } seen[e.typ] = true mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples) // continue with underlying type typ = e.typ.underlying } switch t := typ.(type) { case *Struct: for i, f := range t.fields { fset = fset.add(f, e.multiples) // Embedded fields are always of the form T or *T where // T is a named type. If typ appeared multiple times at // this depth, f.Type appears multiple times at the next // depth. if f.anonymous { // Ignore embedded basic types - only user-defined // named types can have methods or struct fields. typ, isPtr := deref(f.typ) if t, _ := typ.(*Named); t != nil { next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) } } } case *Interface: mset = mset.add(t.allMethods, e.index, true, e.multiples) } } // Add methods and collisions at this depth to base if no entries with matching // names exist already. for k, m := range mset { if _, found := base[k]; !found { // Fields collide with methods of the same name at this depth. if _, found := fset[k]; found { m = nil // collision } if base == nil { base = make(methodSet) } base[k] = m } } // Multiple fields with matching names collide at this depth and shadow all // entries further down; add them as collisions to base if no entries with // matching names exist already. for k, f := range fset { if f == nil { if _, found := base[k]; !found { if base == nil { base = make(methodSet) } base[k] = nil // collision } } } current = consolidateMultiples(next) } if len(base) == 0 { return &emptyMethodSet } // collect methods var list []*Selection for _, m := range base { if m != nil { m.recv = T list = append(list, m) } } sort.Sort(byUniqueName(list)) return &MethodSet{list} } // A fieldSet is a set of fields and name collisions. // A collision indicates that multiple fields with the // same unique id appeared. type fieldSet map[string]*Var // a nil entry indicates a name collision // Add adds field f to the field set s. // If multiples is set, f appears multiple times // and is treated as a collision. func (s fieldSet) add(f *Var, multiples bool) fieldSet { if s == nil { s = make(fieldSet) } key := f.Id() // if f is not in the set, add it if !multiples { if _, found := s[key]; !found { s[key] = f return s } } s[key] = nil // collision return s } // A methodSet is a set of methods and name collisions. // A collision indicates that multiple methods with the // same unique id appeared. type methodSet map[string]*Selection // a nil entry indicates a name collision // Add adds all functions in list to the method set s. // If multiples is set, every function in list appears multiple times // and is treated as a collision. func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet { if len(list) == 0 { return s } if s == nil { s = make(methodSet) } for i, f := range list { key := f.Id() // if f is not in the set, add it if !multiples { if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect} continue } } s[key] = nil // collision } return s } // ptrRecv reports whether the receiver is of the form *T. // The receiver must exist. func ptrRecv(f *Func) bool { _, isPtr := deref(f.typ.(*Signature).recv.typ) return isPtr } // byUniqueName function lists can be sorted by their unique names. type byUniqueName []*Selection func (a byUniqueName) Len() int { return len(a) } func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() } func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } ./go/types/self_test.go0000644000014500017510000000427112246613010014567 0ustar michaelstaff// 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 types_test import ( "flag" "fmt" "go/ast" "go/parser" "go/token" "path/filepath" "testing" "time" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) var benchmark = flag.Bool("b", false, "run benchmarks") func TestSelf(t *testing.T) { fset := token.NewFileSet() files, err := pkgFiles(fset, ".") if err != nil { t.Fatal(err) } _, err = Check("go/types", fset, files) if err != nil { // Importing go.tools/go/exact doensn't work in the // build dashboard environment. Don't report an error // for now so that the build remains green. // TODO(gri) fix this t.Log(err) // replace w/ t.Fatal eventually return } } func TestBenchmark(t *testing.T) { if !*benchmark { return } // We're not using testing's benchmarking mechanism directly // because we want custom output. for _, p := range []string{"types", "exact", "gcimporter"} { path := filepath.Join("..", p) runbench(t, path, false) runbench(t, path, true) fmt.Println() } } func runbench(t *testing.T, path string, ignoreFuncBodies bool) { fset := token.NewFileSet() files, err := pkgFiles(fset, path) if err != nil { t.Fatal(err) } b := testing.Benchmark(func(b *testing.B) { for i := 0; i < b.N; i++ { conf := Config{IgnoreFuncBodies: ignoreFuncBodies} conf.Check(path, fset, files, nil) } }) // determine line count lines := 0 fset.Iterate(func(f *token.File) bool { lines += f.LineCount() return true }) d := time.Duration(b.NsPerOp()) fmt.Printf( "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n", filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies, ) } func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) { filenames, err := pkgFilenames(path) // from stdlib_test.go if err != nil { return nil, err } var files []*ast.File for _, filename := range filenames { file, err := parser.ParseFile(fset, filename, nil, 0) if err != nil { return nil, err } files = append(files, file) } return files, nil } ./go/types/assignments.go0000644000014500017510000001721012246613010015127 0ustar michaelstaff// 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. // This file implements initialization and assignment checks. package types import ( "go/ast" "go/token" "code.google.com/p/go.tools/go/exact" ) // assignment reports whether x can be assigned to a variable of type T, // if necessary by attempting to convert untyped values to the appropriate // type. If x.mode == invalid upon return, then assignment has already // issued an error message and the caller doesn't have to report another. // Use T == nil to indicate assignment to an untyped blank identifier. // // TODO(gri) Should find a better way to handle in-band errors. // func (check *checker) assignment(x *operand, T Type) bool { switch x.mode { case invalid: return true // error reported before case constant, variable, mapindex, value, commaok: // ok default: unreachable() } // x must be a single value // (tuple types are never named - no need for underlying type) if t, _ := x.typ.(*Tuple); t != nil { assert(t.Len() > 1) check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x) x.mode = invalid return false } if isUntyped(x.typ) { target := T // spec: "If an untyped constant is assigned to a variable of interface // type or the blank identifier, the constant is first converted to type // bool, rune, int, float64, complex128 or string respectively, depending // on whether the value is a boolean, rune, integer, floating-point, complex, // or string constant." if T == nil || isInterface(T) { if T == nil && x.typ == Typ[UntypedNil] { check.errorf(x.pos(), "use of untyped nil") x.mode = invalid return false } target = defaultType(x.typ) } check.convertUntyped(x, target) if x.mode == invalid { return false } } // spec: "If a left-hand side is the blank identifier, any typed or // non-constant value except for the predeclared identifier nil may // be assigned to it." return T == nil || x.isAssignableTo(check.conf, T) } func (check *checker) initConst(lhs *Const, x *operand) { if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] { if lhs.typ == nil { lhs.typ = Typ[Invalid] } return } // rhs must be a constant if x.mode != constant { check.errorf(x.pos(), "%s is not constant", x) if lhs.typ == nil { lhs.typ = Typ[Invalid] } return } assert(isConstType(x.typ)) // If the lhs doesn't have a type yet, use the type of x. if lhs.typ == nil { lhs.typ = x.typ } if !check.assignment(x, lhs.typ) { if x.mode != invalid { check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x) } lhs.val = exact.MakeUnknown() return } lhs.val = x.val } func (check *checker) initVar(lhs *Var, x *operand) Type { if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] { if lhs.typ == nil { lhs.typ = Typ[Invalid] } return nil } // If the lhs doesn't have a type yet, use the type of x. if lhs.typ == nil { typ := x.typ if isUntyped(typ) { // convert untyped types to default types if typ == Typ[UntypedNil] { check.errorf(x.pos(), "use of untyped nil") lhs.typ = Typ[Invalid] return nil } typ = defaultType(typ) } lhs.typ = typ } if !check.assignment(x, lhs.typ) { if x.mode != invalid { check.errorf(x.pos(), "cannot initialize variable %s (type %s) with %s", lhs.Name(), lhs.typ, x) } return nil } return lhs.typ } func (check *checker) assignVar(lhs ast.Expr, x *operand) Type { if x.mode == invalid || x.typ == Typ[Invalid] { return nil } // Determine if the lhs is a (possibly parenthesized) identifier. ident, _ := unparen(lhs).(*ast.Ident) // Don't evaluate lhs if it is the blank identifier. if ident != nil && ident.Name == "_" { check.recordObject(ident, nil) if !check.assignment(x, nil) { assert(x.mode == invalid) x.typ = nil } return x.typ } // If the lhs is an identifier denoting a variable v, this assignment // is not a 'use' of v. Remember current value of v.used and restore // after evaluating the lhs via check.expr. var v *Var var v_used bool if ident != nil { if obj := check.topScope.LookupParent(ident.Name); obj != nil { v, _ = obj.(*Var) if v != nil { v_used = v.used } } } var z operand check.expr(&z, lhs) if v != nil { v.used = v_used // restore v.used } if z.mode == invalid || z.typ == Typ[Invalid] { return nil } // spec: "Each left-hand side operand must be addressable, a map index // expression, or the blank identifier. Operands may be parenthesized." switch z.mode { case invalid: return nil case variable, mapindex: // ok default: check.errorf(z.pos(), "cannot assign to %s", &z) return nil } if !check.assignment(x, z.typ) { if x.mode != invalid { check.errorf(x.pos(), "cannot assign %s to %s", x, &z) } return nil } return z.typ } // If returnPos is valid, initVars is called to type-check the assignment of // return expressions, and returnPos is the position of the return statement. func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) { l := len(lhs) get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid()) if l != r { // invalidate lhs for _, obj := range lhs { if obj.typ == nil { obj.typ = Typ[Invalid] } } if returnPos.IsValid() { check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r) return } check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) return } var x operand if commaOk { var a [2]Type for i := range a { get(&x, i) a[i] = check.initVar(lhs[i], &x) } check.recordCommaOkTypes(rhs[0], a) return } for i, lhs := range lhs { get(&x, i) check.initVar(lhs, &x) } } func (check *checker) assignVars(lhs, rhs []ast.Expr) { l := len(lhs) get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2) if l != r { check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) return } var x operand if commaOk { var a [2]Type for i := range a { get(&x, i) a[i] = check.assignVar(lhs[i], &x) } check.recordCommaOkTypes(rhs[0], a) return } for i, lhs := range lhs { get(&x, i) check.assignVar(lhs, &x) } } func (check *checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) { scope := check.topScope // collect lhs variables var newVars []*Var var lhsVars = make([]*Var, len(lhs)) for i, lhs := range lhs { var obj *Var if ident, _ := lhs.(*ast.Ident); ident != nil { // Use the correct obj if the ident is redeclared. The // variable's scope starts after the declaration; so we // must use Scope.Lookup here and call Scope.Insert later. if alt := scope.Lookup(ident.Name); alt != nil { // redeclared object must be a variable if alt, _ := alt.(*Var); alt != nil { obj = alt } else { check.errorf(lhs.Pos(), "cannot assign to %s", lhs) } } else { // declare new variable obj = NewVar(ident.Pos(), check.pkg, ident.Name, nil) newVars = append(newVars, obj) } if obj != nil { check.recordObject(ident, obj) } } else { check.errorf(lhs.Pos(), "cannot declare %s", lhs) } if obj == nil { obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable } lhsVars[i] = obj } check.initVars(lhsVars, rhs, token.NoPos) // declare new variables if len(newVars) > 0 { for _, obj := range newVars { check.declare(scope, nil, obj) // recordObject already called } } else { check.errorf(pos, "no new variables on left side of :=") } } ./go/types/go11.go0000644000014500017510000000050212246613010013337 0ustar michaelstaff// 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 } ./go/types/eval_test.go0000644000014500017510000000635112246613010014566 0ustar michaelstaff// 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. // This file contains tests for Eval. package types_test import ( "go/ast" "go/parser" "go/token" "strings" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) { gotTyp, gotVal, err := Eval(str, pkg, scope) if err != nil { t.Errorf("Eval(%q) failed: %s", str, err) return } if gotTyp == nil { t.Errorf("Eval(%q) got nil type but no error", str) return } // compare types if typ != nil { // we have a type, check identity if !IsIdentical(gotTyp, typ) { t.Errorf("Eval(%q) got type %s, want %s", str, gotTyp, typ) return } } else { // we have a string, compare type string gotStr := gotTyp.String() if gotStr != typStr { t.Errorf("Eval(%q) got type %s, want %s", str, gotStr, typStr) return } } // compare values gotStr := "" if gotVal != nil { gotStr = gotVal.String() } if gotStr != valStr { t.Errorf("Eval(%q) got value %s, want %s", str, gotStr, valStr) } } func TestEvalBasic(t *testing.T) { for _, typ := range Typ[Bool : String+1] { testEval(t, nil, nil, typ.Name(), typ, "", "") } } func TestEvalComposite(t *testing.T) { for _, test := range independentTestTypes { testEval(t, nil, nil, test.src, nil, test.str, "") } } func TestEvalArith(t *testing.T) { var tests = []string{ `true`, `false == false`, `12345678 + 87654321 == 99999999`, `10 * 20 == 200`, `(1<<1000)*2 >> 100 == 2<<900`, `"foo" + "bar" == "foobar"`, `"abc" <= "bcd"`, `len([10]struct{}{}) == 2*5`, } for _, test := range tests { testEval(t, nil, nil, test, Typ[UntypedBool], "", "true") } } func TestEvalContext(t *testing.T) { src := ` package p import "fmt" import m "math" const c = 3.0 type T []int func f(a int, s string) float64 { fmt.Println("calling f") _ = m.Pi // use package math const d int = c + 1 var x int x = a + len(s) return float64(x) } ` fset := token.NewFileSet() file, err := parser.ParseFile(fset, "p", src, 0) if err != nil { t.Fatal(err) } pkg, err := Check("p", fset, []*ast.File{file}) if err != nil { t.Fatal(err) } pkgScope := pkg.Scope() if n := pkgScope.NumChildren(); n != 1 { t.Fatalf("got %d file scopes, want 1", n) } fileScope := pkgScope.Child(0) if n := fileScope.NumChildren(); n != 1 { t.Fatalf("got %d functions scopes, want 1", n) } funcScope := fileScope.Child(0) var tests = []string{ `true => true, untyped boolean`, `fmt.Println => , func(a·3 ...interface{}) (n·1 int, err·2 error)`, `c => 3, untyped float`, `T => , p.T`, `a => , int`, `s => , string`, `d => 4, int`, `x => , int`, `d/c => 1, int`, `c/2 => 3/2, untyped float`, `m.Pi < m.E => false, untyped boolean`, } for _, test := range tests { str, typ := split(test, ", ") str, val := split(str, "=>") testEval(t, pkg, funcScope, str, nil, typ, val) } } // split splits string s at the first occurrence of s. func split(s, sep string) (string, string) { i := strings.Index(s, sep) return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):]) } ./go/types/typemap/0000755000014500017510000000000012246613010013723 5ustar michaelstaff./go/types/typemap/typemap_test.go0000644000014500017510000001102112246613010016763 0ustar michaelstaffpackage typemap_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 ( "go/ast" "testing" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types/typemap" ) 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(ast.RECV, tInt) // <-chan int tChanInt2 = types.NewChan(ast.RECV, tInt) // <-chan int, again ) func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { if !types.IsIdentical(x, y) { t.Errorf("%s: not equal: %s, %s", comment, x, y) } if x == y { t.Errorf("%s: identical: %p, %p", comment, x, y) } } func TestAxioms(t *testing.T) { checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}") checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}") } func TestTypeMap(t *testing.T) { var tmap *typemap.M // All methods but Set are safe on on (*T)(nil). tmap.Len() tmap.At(tPStr1) tmap.Delete(tPStr1) tmap.KeysString() tmap.String() tmap = new(typemap.M) // Length of empty map. if l := tmap.Len(); l != 0 { t.Errorf("Len() on empty typemap: got %d, want 0", l) } // At of missing key. if v := tmap.At(tPStr1); v != nil { t.Errorf("At() on empty typemap: got %v, want nil", v) } // Deletion of missing key. if tmap.Delete(tPStr1) { t.Errorf("Delete() on empty typemap: 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.IsIdentical 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, "") } } ./go/types/typemap/typemap.go0000644000014500017510000001724412246613010015741 0ustar michaelstaff// Package typemap defines type M, a hash-table-based mapping from // types (go/types.Type) to arbitrary values, and a hash function on // types. // // 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. // // Not thread-safe. // package typemap import ( "bytes" "fmt" "unsafe" "code.google.com/p/go.tools/go/types" ) // typemap.M is a mapping from types.Type to interface{} values. // // Just as with map[K]V, a nil *typemap.M is a valid empty map. // type M struct { hasher Hasher // shared by many typemap.Ms 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 typemap.M. // // 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 typemap.M instances. 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 // M.Lookup require updates to the hasher, so a full Mutex lock (not a // read-lock) is require around all typemap.M operations if a shared // hasher is accessed from multiple threads. // // If SetHasher is not called, the type-map will create a private // hasher at the first call to Insert. // func (m *M) 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 *M) 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.IsIdentical(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 *M) 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.IsIdentical(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 *M) 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.IsIdentical(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 *M) 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 *M) 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 *M) 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 *M) 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 *M) String() string { return m.toString(true) } // KeysString returns a string representation of the map's key set. // Order is unspecified. // func (m *M) 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 // IsIdentical(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 IsIdentical 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.IsVariadic() { 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(uintptr(unsafe.Pointer(t.Obj()))) 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 } ./go/types/scope.go0000644000014500017510000000773712246613010013722 0ustar michaelstaff// 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. // This file implements Scopes. package types import ( "bytes" "fmt" "io" "sort" "strings" ) // TODO(gri) Provide scopes with a name or other mechanism so that // objects can use that information for better printing. // A Scope maintains a set of objects and links to its containing // (parent) and contained (children) scopes. Objects may be inserted // and looked up by name. The zero value for Scope is a ready-to-use // empty scope. type Scope struct { parent *Scope children []*Scope elems map[string]Object // lazily allocated } // NewScope returns a new, empty scope contained in the given parent // scope, if any. func NewScope(parent *Scope) *Scope { s := &Scope{parent: parent} // don't add children to Universe scope! if parent != nil && parent != Universe { parent.children = append(parent.children, s) } return s } // Parent returns the scope's containing (parent) scope. func (s *Scope) Parent() *Scope { return s.parent } // Len() returns the number of scope elements. func (s *Scope) Len() int { return len(s.elems) } // Names returns the scope's element names in sorted order. func (s *Scope) Names() []string { names := make([]string, len(s.elems)) i := 0 for name := range s.elems { names[i] = name i++ } sort.Strings(names) return names } // NumChildren() returns the number of scopes nested in s. func (s *Scope) NumChildren() int { return len(s.children) } // Child returns the i'th child scope for 0 <= i < NumChildren(). func (s *Scope) Child(i int) *Scope { return s.children[i] } // Lookup returns the object in scope s with the given name if such an // object exists; otherwise the result is nil. func (s *Scope) Lookup(name string) Object { return s.elems[name] } // LookupParent follows the parent chain of scopes starting with s until // it finds a scope where Lookup(name) returns a non-nil object, and then // returns that object. If no such scope exists, the result is nil. func (s *Scope) LookupParent(name string) Object { for ; s != nil; s = s.parent { if obj := s.elems[name]; obj != nil { return obj } } return nil } // TODO(gri): Should Insert not be exported? // Insert attempts to insert an object obj into scope s. // If s already contains an alternative object alt with // the same name, Insert leaves s unchanged and returns alt. // Otherwise it inserts obj, sets the object's scope to // s, and returns nil. Objects with blank "_" names are // not inserted, but have their parent field set to s. func (s *Scope) Insert(obj Object) Object { name := obj.Name() // spec: "The blank identifier, represented by the underscore // character _, may be used in a declaration like any other // identifier but the declaration does not introduce a new // binding." if name == "_" { obj.setParent(s) return nil } if alt := s.elems[name]; alt != nil { return alt } if s.elems == nil { s.elems = make(map[string]Object) } s.elems[name] = obj obj.setParent(s) return nil } // WriteTo writes a string representation of the scope to w, // with the scope elements sorted by name. // The level of indentation is controlled by n >= 0, with // n == 0 for no indentation. // If recurse is set, it also writes nested (children) scopes. func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { const ind = ". " indn := strings.Repeat(ind, n) if len(s.elems) == 0 { fmt.Fprintf(w, "%sscope %p {}\n", indn, s) return } fmt.Fprintf(w, "%sscope %p {\n", indn, s) indn1 := indn + ind for _, name := range s.Names() { fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name]) } if recurse { for _, s := range s.children { fmt.Fprintln(w) s.WriteTo(w, n+1, recurse) } } fmt.Fprintf(w, "%s}", indn) } // String returns a string representation of the scope, for debugging. func (s *Scope) String() string { var buf bytes.Buffer s.WriteTo(&buf, 0, false) return buf.String() } ./go/types/stmt.go0000644000014500017510000004121312246613010013563 0ustar michaelstaff// Copyright 2012 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 implements typechecking of statements. package types import ( "go/ast" "go/token" ) // stmtContext is a bitset describing the environment // (outer statements) containing a statement. type stmtContext uint const ( fallthroughOk stmtContext = 1 << iota inBreakable inContinuable ) func (check *checker) initStmt(s ast.Stmt) { if s != nil { check.stmt(0, s) } } func (check *checker) stmtList(ctxt stmtContext, list []ast.Stmt) { ok := ctxt&fallthroughOk != 0 inner := ctxt &^ fallthroughOk for i, s := range list { inner := inner if ok && i+1 == len(list) { inner |= fallthroughOk } check.stmt(inner, s) } } func (check *checker) multipleDefaults(list []ast.Stmt) { var first ast.Stmt for _, s := range list { var d ast.Stmt switch c := s.(type) { case *ast.CaseClause: if len(c.List) == 0 { d = s } case *ast.CommClause: if c.Comm == nil { d = s } default: check.invalidAST(s.Pos(), "case/communication clause expected") } if d != nil { if first != nil { check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos()) } else { first = d } } } } func (check *checker) openScope(s ast.Stmt) { scope := NewScope(check.topScope) check.recordScope(s, scope) check.topScope = scope } func (check *checker) closeScope() { check.topScope = check.topScope.Parent() } func assignOp(op token.Token) token.Token { // token_test.go verifies the token ordering this function relies on if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN { return op + (token.ADD - token.ADD_ASSIGN) } return token.ILLEGAL } func (check *checker) suspendedCall(keyword string, call *ast.CallExpr) { var x operand var msg string switch check.rawExpr(&x, call, nil) { case conversion: msg = "requires function call, not conversion" case expression: msg = "discards result of" case statement: return default: unreachable() } check.errorf(x.pos(), "%s %s %s", keyword, msg, &x) } func (check *checker) caseValues(x operand /* copy argument (not *operand!) */, values []ast.Expr) { // No duplicate checking for now. See issue 4524. for _, e := range values { var y operand check.expr(&y, e) if y.mode == invalid { return } // TODO(gri) The convertUntyped call pair below appears in other places. Factor! // Order matters: By comparing y against x, error positions are at the case values. check.convertUntyped(&y, x.typ) if y.mode == invalid { return } check.convertUntyped(&x, y.typ) if x.mode == invalid { return } check.comparison(&y, &x, token.EQL) } } func (check *checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) { L: for _, e := range types { T = check.typOrNil(e) if T == Typ[Invalid] { continue } // complain about duplicate types // TODO(gri) use a type hash to avoid quadratic algorithm for t, pos := range seen { if T == nil && t == nil || T != nil && t != nil && IsIdentical(T, t) { // talk about "case" rather than "type" because of nil case check.errorf(e.Pos(), "duplicate case in type switch") check.errorf(pos, "previous case %s", T) continue L } } seen[T] = e.Pos() if T != nil { check.typeAssertion(e.Pos(), x, xtyp, T) } } return } // stmt typechecks statement s. func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { // statements cannot use iota in general // (constant declarations set it explicitly) assert(check.iota == nil) // statements must end with the same top scope as they started with if debug { defer func(scope *Scope) { // don't check if code is panicking if p := recover(); p != nil { panic(p) } assert(scope == check.topScope) }(check.topScope) } inner := ctxt &^ fallthroughOk switch s := s.(type) { case *ast.BadStmt, *ast.EmptyStmt: // ignore case *ast.DeclStmt: check.declStmt(s.Decl) case *ast.LabeledStmt: check.hasLabel = true check.stmt(ctxt, s.Stmt) case *ast.ExprStmt: // spec: "With the exception of specific built-in functions, // function and method calls and receive operations can appear // in statement context. Such statements may be parenthesized." var x operand kind := check.rawExpr(&x, s.X, nil) var msg string switch x.mode { default: if kind == statement { return } msg = "is not used" case builtin: msg = "must be called" case typexpr: msg = "is not an expression" } check.errorf(x.pos(), "%s %s", &x, msg) case *ast.SendStmt: var ch, x operand check.expr(&ch, s.Chan) check.expr(&x, s.Value) if ch.mode == invalid || x.mode == invalid { return } if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir&ast.SEND == 0 || !check.assignment(&x, tch.elem) { if x.mode != invalid { check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch) } } case *ast.IncDecStmt: var op token.Token switch s.Tok { case token.INC: op = token.ADD case token.DEC: op = token.SUB default: check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok) return } var x operand Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position check.binary(&x, s.X, Y, op) if x.mode == invalid { return } check.assignVar(s.X, &x) case *ast.AssignStmt: switch s.Tok { case token.ASSIGN, token.DEFINE: if len(s.Lhs) == 0 { check.invalidAST(s.Pos(), "missing lhs in assignment") return } if s.Tok == token.DEFINE { check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs) } else { // regular assignment check.assignVars(s.Lhs, s.Rhs) } default: // assignment operations if len(s.Lhs) != 1 || len(s.Rhs) != 1 { check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok) return } op := assignOp(s.Tok) if op == token.ILLEGAL { check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok) return } var x operand check.binary(&x, s.Lhs[0], s.Rhs[0], op) if x.mode == invalid { return } check.assignVar(s.Lhs[0], &x) } case *ast.GoStmt: check.suspendedCall("go", s.Call) case *ast.DeferStmt: check.suspendedCall("defer", s.Call) case *ast.ReturnStmt: sig := check.funcSig if n := sig.results.Len(); n > 0 { // determine if the function has named results named := false lhs := make([]*Var, n) for i, res := range sig.results.vars { if res.name != "" { // a blank (_) result parameter is a named result named = true } lhs[i] = res } if len(s.Results) > 0 || !named { check.initVars(lhs, s.Results, s.Return) return } } else if len(s.Results) > 0 { check.errorf(s.Pos(), "no result values expected") } case *ast.BranchStmt: if s.Label != nil { check.hasLabel = true return // checked in 2nd pass (check.labels) } switch s.Tok { case token.BREAK: if ctxt&inBreakable == 0 { check.errorf(s.Pos(), "break not in for, switch, or select statement") } case token.CONTINUE: if ctxt&inContinuable == 0 { check.errorf(s.Pos(), "continue not in for statement") } case token.FALLTHROUGH: if ctxt&fallthroughOk == 0 { check.errorf(s.Pos(), "fallthrough statement out of place") } default: check.invalidAST(s.Pos(), "branch statement: %s", s.Tok) } case *ast.BlockStmt: check.openScope(s) defer check.closeScope() check.stmtList(inner, s.List) case *ast.IfStmt: check.openScope(s) defer check.closeScope() check.initStmt(s.Init) var x operand check.expr(&x, s.Cond) if x.mode != invalid && !isBoolean(x.typ) { check.errorf(s.Cond.Pos(), "non-boolean condition in if statement") } check.stmt(inner, s.Body) if s.Else != nil { check.stmt(inner, s.Else) } case *ast.SwitchStmt: inner |= inBreakable check.openScope(s) defer check.closeScope() check.initStmt(s.Init) var x operand tag := s.Tag if tag == nil { // use fake true tag value and position it at the opening { of the switch ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"} check.recordObject(ident, Universe.Lookup("true")) tag = ident } check.expr(&x, tag) check.multipleDefaults(s.Body.List) for i, c := range s.Body.List { clause, _ := c.(*ast.CaseClause) if clause == nil { check.invalidAST(c.Pos(), "incorrect expression switch case") continue } if x.mode != invalid { check.caseValues(x, clause.List) } check.openScope(clause) inner := inner if i+1 < len(s.Body.List) { inner |= fallthroughOk } check.stmtList(inner, clause.Body) check.closeScope() } case *ast.TypeSwitchStmt: inner |= inBreakable check.openScope(s) defer check.closeScope() check.initStmt(s.Init) // A type switch guard must be of the form: // // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . // // The parser is checking syntactic correctness; // remaining syntactic errors are considered AST errors here. // TODO(gri) better factoring of error handling (invalid ASTs) // var lhs *ast.Ident // lhs identifier or nil var rhs ast.Expr switch guard := s.Assign.(type) { case *ast.ExprStmt: rhs = guard.X case *ast.AssignStmt: if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 { check.invalidAST(s.Pos(), "incorrect form of type switch guard") return } lhs, _ = guard.Lhs[0].(*ast.Ident) if lhs == nil { check.invalidAST(s.Pos(), "incorrect form of type switch guard") return } check.recordObject(lhs, nil) // lhs variable is implicitly declared in each cause clause rhs = guard.Rhs[0] default: check.invalidAST(s.Pos(), "incorrect form of type switch guard") return } // rhs must be of the form: expr.(type) and expr must be an interface expr, _ := rhs.(*ast.TypeAssertExpr) if expr == nil || expr.Type != nil { check.invalidAST(s.Pos(), "incorrect form of type switch guard") return } var x operand check.expr(&x, expr.X) if x.mode == invalid { return } xtyp, _ := x.typ.Underlying().(*Interface) if xtyp == nil { check.errorf(x.pos(), "%s is not an interface", &x) return } check.multipleDefaults(s.Body.List) var lhsVars []*Var // set of implicitly declared lhs variables seen := make(map[Type]token.Pos) // map of seen types to positions for _, s := range s.Body.List { clause, _ := s.(*ast.CaseClause) if clause == nil { check.invalidAST(s.Pos(), "incorrect type switch case") continue } // Check each type in this type switch case. T := check.caseTypes(&x, xtyp, clause.List, seen) check.openScope(clause) // If lhs exists, declare a corresponding variable in the case-local scope if necessary. if lhs != nil { // spec: "The TypeSwitchGuard may include a short variable declaration. // When that form is used, the variable is declared at the beginning of // the implicit block in each clause. In clauses with a case listing // exactly one type, the variable has that type; otherwise, the variable // has the type of the expression in the TypeSwitchGuard." if len(clause.List) != 1 || T == nil { T = x.typ } obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T) // For the "declared but not used" error, all lhs variables act as // one; i.e., if any one of them is 'used', all of them are 'used'. // Collect them for later analysis. lhsVars = append(lhsVars, obj) check.declare(check.topScope, nil, obj) check.recordImplicit(clause, obj) } check.stmtList(inner, clause.Body) check.closeScope() } // If a lhs variable was declared but there were no case clauses, make sure // we have at least one (dummy) 'unused' variable to force an error message. if len(lhsVars) == 0 && lhs != nil { lhsVars = []*Var{NewVar(lhs.Pos(), check.pkg, lhs.Name, x.typ)} } // Record lhs variables for this type switch, if any. if len(lhsVars) > 0 { check.lhsVarsList = append(check.lhsVarsList, lhsVars) } case *ast.SelectStmt: inner |= inBreakable check.multipleDefaults(s.Body.List) for _, s := range s.Body.List { clause, _ := s.(*ast.CommClause) if clause == nil { continue // error reported before } // clause.Comm must be a SendStmt, RecvStmt, or default case valid := false var rhs ast.Expr // rhs of RecvStmt, or nil switch s := clause.Comm.(type) { case nil, *ast.SendStmt: valid = true case *ast.AssignStmt: if len(s.Rhs) == 1 { rhs = s.Rhs[0] } case *ast.ExprStmt: rhs = s.X } // if present, rhs must be a receive operation if rhs != nil { if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW { valid = true } } if !valid { check.errorf(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)") continue } check.openScope(s) defer check.closeScope() if clause.Comm != nil { check.stmt(inner, clause.Comm) } check.stmtList(inner, clause.Body) } case *ast.ForStmt: inner |= inBreakable | inContinuable check.openScope(s) defer check.closeScope() check.initStmt(s.Init) if s.Cond != nil { var x operand check.expr(&x, s.Cond) if x.mode != invalid && !isBoolean(x.typ) { check.errorf(s.Cond.Pos(), "non-boolean condition in for statement") } } check.initStmt(s.Post) check.stmt(inner, s.Body) case *ast.RangeStmt: inner |= inBreakable | inContinuable check.openScope(s) defer check.closeScope() // check expression to iterate over decl := s.Tok == token.DEFINE var x operand check.expr(&x, s.X) if x.mode == invalid { // if we don't have a declaration, we can still check the loop's body // (otherwise we can't because we are missing the declared variables) if !decl { check.stmt(inner, s.Body) } return } // determine key/value types var key, val Type switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { key = Typ[Int] val = Typ[Rune] } case *Array: key = Typ[Int] val = typ.elem case *Slice: key = Typ[Int] val = typ.elem case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { key = Typ[Int] val = typ.elem } case *Map: key = typ.key val = typ.elem case *Chan: key = typ.elem val = Typ[Invalid] if typ.dir&ast.RECV == 0 { check.errorf(x.pos(), "cannot range over send-only channel %s", &x) // ok to continue } if s.Value != nil { check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x) // ok to continue } } if key == nil { check.errorf(x.pos(), "cannot range over %s", &x) // if we don't have a declaration, we can still check the loop's body if !decl { check.stmt(inner, s.Body) } return } // check assignment to/declaration of iteration variables // (irregular assignment, cannot easily map to existing assignment checks) if s.Key == nil { check.invalidAST(s.Pos(), "range clause requires index iteration variable") // ok to continue } // lhs expressions and initialization value (rhs) types lhs := [2]ast.Expr{s.Key, s.Value} rhs := [2]Type{key, val} if decl { // short variable declaration; variable scope starts after the range clause // (the for loop opens a new scope, so variables on the lhs never redeclare // previously declared variables) var vars []*Var for i, lhs := range lhs { if lhs == nil { continue } // determine lhs variable var obj *Var if ident, _ := lhs.(*ast.Ident); ident != nil { // declare new variable name := ident.Name obj = NewVar(ident.Pos(), check.pkg, name, nil) check.recordObject(ident, obj) // _ variables don't count as new variables if name != "_" { vars = append(vars, obj) } } else { check.errorf(lhs.Pos(), "cannot declare %s", lhs) obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable } // initialize lhs variable x.mode = value x.expr = lhs // we don't have a better rhs expression to use here x.typ = rhs[i] check.initVar(obj, &x) } // declare variables if len(vars) > 0 { for _, obj := range vars { check.declare(check.topScope, nil, obj) // recordObject already called } } else { check.errorf(s.TokPos, "no new variables on left side of :=") } } else { // ordinary assignment for i, lhs := range lhs { if lhs == nil { continue } x.mode = value x.expr = lhs // we don't have a better rhs expression to use here x.typ = rhs[i] check.assignVar(lhs, &x) } } check.stmt(inner, s.Body) default: check.errorf(s.Pos(), "invalid statement") } } ./go/types/eval.go0000644000014500017510000000631512246613010013527 0ustar michaelstaff// 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. // This file implements New, Eval and EvalNode. package types import ( "fmt" "go/ast" "go/parser" "go/token" "code.google.com/p/go.tools/go/exact" ) // New is a convenience function to create a new type from a given // expression or type literal string evaluated in Universe scope. // New(str) is shorthand for Eval(str, nil, nil), but only returns // the type result, and panics in case of an error. // Position info for objects in the result type is undefined. // func New(str string) Type { typ, _, err := Eval(str, nil, nil) if err != nil { panic(err) } return typ } // Eval returns the type and, if constant, the value for the // expression or type literal string str evaluated in scope. // If the expression contains function literals, the function // bodies are ignored (though they must be syntactically correct). // // If pkg == nil, the Universe scope is used and the provided // scope is ignored. Otherwise, the scope must belong to the // package (either the package scope, or nested within the // package scope). // // An error is returned if the scope is incorrect, the string // has syntax errors, or if it cannot be evaluated in the scope. // Position info for objects in the result type is undefined. // // Note: Eval should not be used instead of running Check to compute // types and values, but in addition to Check. Eval will re-evaluate // its argument each time, and it also does not know about the context // in which an expression is used (e.g., an assignment). Thus, top- // level untyped constants will return an untyped type rather then the // respective context-specific type. // func Eval(str string, pkg *Package, scope *Scope) (typ Type, val exact.Value, err error) { node, err := parser.ParseExpr(str) if err != nil { return nil, nil, err } // Create a file set that looks structurally identical to the // one created by parser.ParseExpr for correct error positions. fset := token.NewFileSet() fset.AddFile("", len(str), fset.Base()).SetLinesForContent([]byte(str)) return EvalNode(fset, node, pkg, scope) } // EvalNode is like Eval but instead of string it accepts // an expression node and respective file set. // // An error is returned if the scope is incorrect // if the node cannot be evaluated in the scope. // func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (typ Type, val exact.Value, err error) { // verify package/scope relationship if pkg == nil { scope = Universe } else { s := scope for s != nil && s != pkg.scope { s = s.parent } // s == nil || s == pkg.scope if s == nil { return nil, nil, fmt.Errorf("scope does not belong to package %s", pkg.name) } } // initialize checker var conf Config check := newChecker(&conf, fset, pkg) check.topScope = scope defer check.handleBailout(&err) // evaluate node var x operand check.exprOrType(&x, node) switch x.mode { case invalid, novalue: fallthrough default: unreachable() // or bailed out with error case constant: val = x.val fallthrough case typexpr, variable, mapindex, value, commaok: typ = x.typ } return } ./go/types/return.go0000644000014500017510000001036412246613010014116 0ustar michaelstaff// 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. // This file implements isTerminating. package types import ( "go/ast" "go/token" ) // isTerminating reports if s is a terminating statement. // If s is labeled, label is the label name; otherwise s // is "". func (check *checker) isTerminating(s ast.Stmt, label string) bool { switch s := s.(type) { default: unreachable() case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt, *ast.RangeStmt: // no chance case *ast.LabeledStmt: return check.isTerminating(s.Stmt, s.Label.Name) case *ast.ExprStmt: // the predeclared (possibly parenthesized) panic() function is terminating if call, _ := unparen(s.X).(*ast.CallExpr); call != nil { if id, _ := call.Fun.(*ast.Ident); id != nil { if obj := check.topScope.LookupParent(id.Name); obj != nil { if b, _ := obj.(*Builtin); b != nil && b.id == _Panic { return true } } } } case *ast.ReturnStmt: return true case *ast.BranchStmt: if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH { return true } case *ast.BlockStmt: return check.isTerminatingList(s.List, "") case *ast.IfStmt: if s.Else != nil && check.isTerminating(s.Body, "") && check.isTerminating(s.Else, "") { return true } case *ast.SwitchStmt: return check.isTerminatingSwitch(s.Body, label) case *ast.TypeSwitchStmt: return check.isTerminatingSwitch(s.Body, label) case *ast.SelectStmt: for _, s := range s.Body.List { cc := s.(*ast.CommClause) if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { return false } } return true case *ast.ForStmt: if s.Cond == nil && !hasBreak(s.Body, label, true) { return true } } return false } func (check *checker) isTerminatingList(list []ast.Stmt, label string) bool { n := len(list) return n > 0 && check.isTerminating(list[n-1], label) } func (check *checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool { hasDefault := false for _, s := range body.List { cc := s.(*ast.CaseClause) if cc.List == nil { hasDefault = true } if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { return false } } return hasDefault } // TODO(gri) For nested breakable statements, the current implementation of hasBreak // will traverse the same subtree repeatedly, once for each label. Replace // with a single-pass label/break matching phase. // hasBreak reports if s is or contains a break statement // referring to the label-ed statement or implicit-ly the // closest outer breakable statement. func hasBreak(s ast.Stmt, label string, implicit bool) bool { switch s := s.(type) { default: unreachable() case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt, *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt, *ast.ReturnStmt: // no chance case *ast.LabeledStmt: return hasBreak(s.Stmt, label, implicit) case *ast.BranchStmt: if s.Tok == token.BREAK { if s.Label == nil { return implicit } if s.Label.Name == label { return true } } case *ast.BlockStmt: return hasBreakList(s.List, label, implicit) case *ast.IfStmt: if hasBreak(s.Body, label, implicit) || s.Else != nil && hasBreak(s.Else, label, implicit) { return true } case *ast.CaseClause: return hasBreakList(s.Body, label, implicit) case *ast.SwitchStmt: if label != "" && hasBreak(s.Body, label, false) { return true } case *ast.TypeSwitchStmt: if label != "" && hasBreak(s.Body, label, false) { return true } case *ast.CommClause: return hasBreakList(s.Body, label, implicit) case *ast.SelectStmt: if label != "" && hasBreak(s.Body, label, false) { return true } case *ast.ForStmt: if label != "" && hasBreak(s.Body, label, false) { return true } case *ast.RangeStmt: if label != "" && hasBreak(s.Body, label, false) { return true } } return false } func hasBreakList(list []ast.Stmt, label string, implicit bool) bool { for _, s := range list { if hasBreak(s, label, implicit) { return true } } return false } ./go/types/issues_test.go0000644000014500017510000000441112246613010015145 0ustar michaelstaff// 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. // This file implements tests for various issues. package types_test import ( "go/ast" "go/parser" "strings" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) func TestIssue5770(t *testing.T) { src := `package p; type S struct{T}` f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Fatal(err) } _, err = Check(f.Name.Name, fset, []*ast.File{f}) // do not crash want := "undeclared name: T" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("got: %v; want: %s", err, want) } } func TestIssue5849(t *testing.T) { src := ` package p var ( s uint _ = uint8(8) _ = uint16(16) << s _ = uint32(32 << s) _ = uint64(64 << s + s) _ = (interface{})("foo") _ = (interface{})(nil) )` f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Fatal(err) } var conf Config types := make(map[ast.Expr]Type) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) if err != nil { t.Fatal(err) } for x, typ := range types { var want Type switch x := x.(type) { case *ast.BasicLit: switch x.Value { case `8`: want = Typ[Uint8] case `16`: want = Typ[Uint16] case `32`: want = Typ[Uint32] case `64`: want = Typ[Uint] // because of "+ s", s is of type uint case `"foo"`: want = Typ[String] } case *ast.Ident: if x.Name == "nil" { want = Typ[UntypedNil] } } if want != nil && !IsIdentical(typ, want) { t.Errorf("got %s; want %s", typ, want) } } } func TestIssue6413(t *testing.T) { src := ` package p func f() int { defer f() go f() return 0 } ` f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Fatal(err) } var conf Config types := make(map[ast.Expr]Type) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) if err != nil { t.Fatal(err) } want := Typ[Int] n := 0 for x, got := range types { if _, ok := x.(*ast.CallExpr); ok { if got != want { t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), got, want) } n++ } } if n != 2 { t.Errorf("got %d CallExprs; want 2", n) } } ./go/types/typestring.go0000644000014500017510000001021612246613010015003 0ustar michaelstaff// 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. // This file implements printing of types. package types import ( "bytes" "fmt" "go/ast" ) // TypeString returns the string representation of typ. // Named types are printed package-qualified if they // do not belong to this package. func TypeString(this *Package, typ Type) string { var buf bytes.Buffer WriteType(&buf, this, typ) return buf.String() } // WriteType writes the string representation of typ to buf. // Named types are printed package-qualified if they // do not belong to this package. func WriteType(buf *bytes.Buffer, this *Package, typ Type) { switch t := typ.(type) { case nil: buf.WriteString("") case *Basic: if t.kind == UnsafePointer { buf.WriteString("unsafe.") } buf.WriteString(t.name) case *Array: fmt.Fprintf(buf, "[%d]", t.len) WriteType(buf, this, t.elem) case *Slice: buf.WriteString("[]") WriteType(buf, this, t.elem) case *Struct: buf.WriteString("struct{") for i, f := range t.fields { if i > 0 { buf.WriteString("; ") } if !f.anonymous { buf.WriteString(f.name) buf.WriteByte(' ') } WriteType(buf, this, f.typ) if tag := t.Tag(i); tag != "" { fmt.Fprintf(buf, " %q", tag) } } buf.WriteByte('}') case *Pointer: buf.WriteByte('*') WriteType(buf, this, t.base) case *Tuple: writeTuple(buf, this, t, false) case *Signature: buf.WriteString("func") writeSignature(buf, this, t) case *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 } // } // buf.WriteString("interface{") for i, m := range t.methods { if i > 0 { buf.WriteString("; ") } buf.WriteString(m.name) writeSignature(buf, this, m.typ.(*Signature)) } for i, typ := range t.types { if i > 0 || len(t.methods) > 0 { buf.WriteString("; ") } WriteType(buf, this, typ) } buf.WriteByte('}') case *Map: buf.WriteString("map[") WriteType(buf, this, t.key) buf.WriteByte(']') WriteType(buf, this, t.elem) case *Chan: var s string var parens bool switch t.dir { case ast.SEND: s = "chan<- " case ast.RECV: s = "<-chan " default: s = "chan " // chan (<-chan T) requires parentheses if c, _ := t.elem.(*Chan); c != nil && c.dir == ast.RECV { parens = true } } buf.WriteString(s) if parens { buf.WriteByte('(') } WriteType(buf, this, t.elem) if parens { buf.WriteByte(')') } case *Named: s := "" if obj := t.obj; obj != nil { if obj.pkg != nil { if obj.pkg != this { buf.WriteString(obj.pkg.path) buf.WriteByte('.') } // TODO(gri): function-local named types should be displayed // differently from named types at package level to avoid // ambiguity. } s = t.obj.name } buf.WriteString(s) default: // For externally defined implementations of Type. buf.WriteString(t.String()) } } func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) { buf.WriteByte('(') if tup != nil { for i, v := range tup.vars { if i > 0 { buf.WriteString(", ") } if v.name != "" { buf.WriteString(v.name) buf.WriteByte(' ') } typ := v.typ if isVariadic && i == len(tup.vars)-1 { buf.WriteString("...") typ = typ.(*Slice).elem } WriteType(buf, this, typ) } } buf.WriteByte(')') } func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { writeTuple(buf, this, sig.params, sig.isVariadic) n := sig.results.Len() if n == 0 { // no result return } buf.WriteByte(' ') if n == 1 && sig.results.vars[0].name == "" { // single unnamed result WriteType(buf, this, sig.results.vars[0].typ) return } // multiple or named result(s) writeTuple(buf, this, sig.results, false) } ./go/types/stdlib_test.go0000644000014500017510000001277012246613010015122 0ustar michaelstaff// 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. // This file tests types.Check by using it to // typecheck the standard library and tests. package types_test import ( "flag" "fmt" "go/ast" "go/build" "go/parser" "go/scanner" "go/token" "io/ioutil" "os" "path/filepath" "runtime" "strings" "testing" "time" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) var verbose = flag.Bool("types.v", false, "verbose mode") var ( pkgCount int // number of packages processed start = time.Now() ) func TestStdlib(t *testing.T) { walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg")) if *verbose { fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) } } // firstComment returns the contents of the first comment in // the given file, assuming there's one within the first KB. func firstComment(filename string) string { f, err := os.Open(filename) if err != nil { return "" } defer f.Close() var src [1 << 10]byte // read at most 1KB n, _ := f.Read(src[:]) var s scanner.Scanner s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments) for { _, tok, lit := s.Scan() switch tok { case token.COMMENT: // remove trailing */ of multi-line comment if lit[1] == '*' { lit = lit[:len(lit)-2] } return strings.TrimSpace(lit[2:]) case token.EOF: return "" } } } func testTestDir(t *testing.T, path string, ignore ...string) { files, err := ioutil.ReadDir(path) if err != nil { t.Fatal(err) } excluded := make(map[string]bool) for _, filename := range ignore { excluded[filename] = true } fset := token.NewFileSet() for _, f := range files { // filter directory contents if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] { continue } // get per-file instructions expectErrors := false filename := filepath.Join(path, f.Name()) if cmd := firstComment(filename); cmd != "" { switch cmd { case "skip", "compiledir": continue // ignore this file case "errorcheck": expectErrors = true } } // parse and type-check file file, err := parser.ParseFile(fset, filename, nil, 0) if err == nil { _, err = Check(filename, fset, []*ast.File{file}) } if expectErrors { if err == nil { t.Errorf("expected errors but found none in %s", filename) } } else { if err != nil { t.Error(err) } } } } func TestStdTest(t *testing.T) { testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore "mapnan.go", "sigchld.go", // don't work on Windows; testTestDir should consult build tags ) } func TestStdFixed(t *testing.T) { testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug459.go", // likely incorrect test - see issue 6793 (pending spec clarification) "issue3924.go", // likely incorrect test - see issue 6671 (pending spec clarification) ) } func TestStdKen(t *testing.T) { testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) } // Package paths of excluded packages. var excluded = map[string]bool{ "builtin": true, } // typecheck typechecks the given package files. func typecheck(t *testing.T, path string, filenames []string) { fset := token.NewFileSet() // parse package files var files []*ast.File for _, filename := range filenames { file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) if err != nil { // the parser error may be a list of individual errors; report them all if list, ok := err.(scanner.ErrorList); ok { for _, err := range list { t.Error(err) } return } t.Error(err) return } if *verbose { if len(files) == 0 { fmt.Println("package", file.Name.Name) } fmt.Println("\t", filename) } files = append(files, file) } // typecheck package files var conf Config conf.Error = func(err error) { t.Error(err) } conf.Check(path, fset, files, nil) pkgCount++ } // pkgFilenames returns the list of package filenames for the given directory. func pkgFilenames(dir string) ([]string, error) { ctxt := build.Default ctxt.CgoEnabled = false pkg, err := ctxt.ImportDir(dir, 0) if err != nil { if _, nogo := err.(*build.NoGoError); nogo { return nil, nil // no *.go files, not an error } return nil, err } if excluded[pkg.ImportPath] { return nil, nil } var filenames []string for _, name := range pkg.GoFiles { filenames = append(filenames, filepath.Join(pkg.Dir, name)) } for _, name := range pkg.TestGoFiles { filenames = append(filenames, filepath.Join(pkg.Dir, name)) } return filenames, nil } // Note: Could use filepath.Walk instead of walkDirs but that wouldn't // necessarily be shorter or clearer after adding the code to // terminate early for -short tests. func walkDirs(t *testing.T, dir string) { // limit run time for short tests if testing.Short() && time.Since(start) >= 750*time.Millisecond { return } fis, err := ioutil.ReadDir(dir) if err != nil { t.Error(err) return } // typecheck package in directory files, err := pkgFilenames(dir) if err != nil { t.Error(err) return } if files != nil { typecheck(t, dir, files) } // traverse subdirectories, but don't walk into testdata for _, fi := range fis { if fi.IsDir() && fi.Name() != "testdata" { walkDirs(t, filepath.Join(dir, fi.Name())) } } } ./go/types/conversions.go0000644000014500017510000000721512246613010015150 0ustar michaelstaff// Copyright 2012 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 implements typechecking of conversions. package types import "code.google.com/p/go.tools/go/exact" // Conversion type-checks the conversion T(x). // The result is in x. func (check *checker) conversion(x *operand, T Type) { var ok bool switch { case x.mode == constant && isConstType(T): // constant conversion switch t := T.Underlying().(*Basic); { case isRepresentableConst(x.val, check.conf, t.kind, &x.val): ok = true case x.isInteger() && isString(t): codepoint := int64(-1) if i, ok := exact.Int64Val(x.val); ok { codepoint = i } // If codepoint < 0 the absolute value is too large (or unknown) for // conversion. This is the same as converting any other out-of-range // value - let string(codepoint) do the work. x.val = exact.MakeString(string(codepoint)) ok = true } case x.isConvertible(check.conf, T): // non-constant conversion x.mode = value ok = true } if !ok { check.errorf(x.pos(), "cannot convert %s to %s", x, T) x.mode = invalid return } // The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...". final := x.typ if isUntyped(final) { final = T // For conversions to interfaces, use the argument type's // default type instead. Keep untyped nil for untyped nil // arguments. if isInterface(T) { final = defaultType(x.typ) } } x.typ = T check.updateExprType(x.expr, final, true) } func (x *operand) isConvertible(conf *Config, T Type) bool { // "x is assignable to T" if x.isAssignableTo(conf, T) { return true } // "x's type and T have identical underlying types" V := x.typ Vu := V.Underlying() Tu := T.Underlying() if IsIdentical(Vu, Tu) { return true } // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types" if V, ok := V.(*Pointer); ok { if T, ok := T.(*Pointer); ok { if IsIdentical(V.base.Underlying(), T.base.Underlying()) { return true } } } // "x's type and T are both integer or floating point types" if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) { return true } // "x's type and T are both complex types" if isComplex(V) && isComplex(T) { return true } // "x is an integer or a slice of bytes or runes and T is a string type" if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { return true } // "x is a string and T is a slice of bytes or runes" if isString(V) && isBytesOrRunes(Tu) { return true } // package unsafe: // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { return true } // "and vice versa" if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { return true } return false } func isUintptr(typ Type) bool { t, ok := typ.Underlying().(*Basic) return ok && t.kind == Uintptr } func isUnsafePointer(typ Type) bool { // TODO(gri): Is this (typ.Underlying() instead of just typ) correct? // The spec does't say so, but gc claims it is. See also // issue 6326. t, ok := typ.Underlying().(*Basic) return ok && t.kind == UnsafePointer } func isPointer(typ Type) bool { _, ok := typ.Underlying().(*Pointer) return ok } func isBytesOrRunes(typ Type) bool { if s, ok := typ.(*Slice); ok { t, ok := s.elem.Underlying().(*Basic) return ok && (t.kind == Byte || t.kind == Rune) } return false } ./go/types/go12.go0000644000014500017510000000050612246613010013344 0ustar michaelstaff// 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 } ./go/types/selection.go0000644000014500017510000001100712246613010014557 0ustar michaelstaff// 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. // This file implements Selections. package types import ( "bytes" "fmt" ) // SelectionKind describes the kind of a selector expression x.f. type SelectionKind int const ( FieldVal SelectionKind = iota // x.f is a struct field selector MethodVal // x.f is a method selector MethodExpr // x.f is a method expression PackageObj // x.f is a qualified identifier ) // A Selection describes a selector expression x.f. // For the declarations: // // type T struct{ x int; E } // type E struct{} // func (e E) m() {} // var p *T // // the following relations exist: // // Selector Kind Recv Obj Type Index Indirect // // p.x FieldVal T x int {0} true // p.m MethodVal *T m func (e *T) m() {1, 0} true // T.m MethodExpr T m func m(_ T) {1, 0} false // math.Pi PackageObj nil Pi untyped numeric nil false // type Selection struct { kind SelectionKind recv Type // type of x, nil if kind == PackageObj obj Object // object denoted by x.f index []int // path from x to x.f, nil if kind == PackageObj indirect bool // set if there was any pointer indirection on the path, false if kind == PackageObj } // Kind returns the selection kind. func (s *Selection) Kind() SelectionKind { return s.kind } // Recv returns the type of x in x.f. // The result is nil if x.f is a qualified identifier (PackageObj). func (s *Selection) Recv() Type { return s.recv } // Obj returns the object denoted by x.f. // The following object types may appear: // // Kind Object // // FieldVal *Var field // MethodVal *Func method // MethodExpr *Func method // PackageObj *Const, *Type, *Var, *Func imported const, type, var, or func // func (s *Selection) Obj() Object { return s.obj } // Type returns the type of x.f, which may be different from the type of f. // See Selection for more information. func (s *Selection) Type() Type { switch s.kind { case MethodVal: // The type of x.f is a method with its receiver type set // to the type of x. sig := *s.obj.(*Func).typ.(*Signature) recv := *sig.recv recv.typ = s.recv sig.recv = &recv return &sig case MethodExpr: // The type of x.f is a function (without receiver) // and an additional first argument with the same type as x. // TODO(gri) Similar code is already in call.go - factor! sig := *s.obj.(*Func).typ.(*Signature) arg0 := *sig.recv arg0.typ = s.recv var params []*Var if sig.params != nil { params = sig.params.vars } sig.params = NewTuple(append([]*Var{&arg0}, params...)...) return &sig } // In all other cases, the type of x.f is the type of x. return s.obj.Type() } // Index describes the path from x to f in x.f. // The result is nil if x.f is a qualified identifier (PackageObj). // // The last index entry is the field or method index of the type declaring f; // either: // // 1) the list of declared methods of a named type; or // 2) the list of methods of an interface type; or // 3) the list of fields of a struct type. // // The earlier index entries are the indices of the embedded fields implicitly // traversed to get from (the type of) x to f, starting at embedding depth 0. func (s *Selection) Index() []int { return s.index } // Indirect reports whether any pointer indirection was required to get from // x to f in x.f. // The result is false if x.f is a qualified identifier (PackageObj). func (s *Selection) Indirect() bool { return s.indirect } func (s *Selection) String() string { return SelectionString(nil, s) } // SelectionString returns the string form of s. // Type names are printed package-qualified // only if they do not belong to this package. // func SelectionString(this *Package, s *Selection) string { var k string switch s.kind { case FieldVal: k = "field " case MethodVal: k = "method " case MethodExpr: k = "method expr " case PackageObj: return fmt.Sprintf("qualified ident %s", s.obj) default: unreachable() } var buf bytes.Buffer buf.WriteString(k) buf.WriteByte('(') WriteType(&buf, this, s.Recv()) fmt.Fprintf(&buf, ") %s", s.obj.Name()) writeSignature(&buf, this, s.Type().(*Signature)) return buf.String() } ./go/types/api.go0000644000014500017510000002145712246613010013355 0ustar michaelstaff// Copyright 2012 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 types declares the data types and implements // the algorithms for type-checking of Go packages. // Use Check and Config.Check to invoke the type-checker. // // Type-checking consists of several interdependent phases: // // Name resolution maps each identifier (ast.Ident) in the program to the // language object (Object) it denotes. // Use Info.Objects, Info.Implicits for the results of name resolution. // // Constant folding computes the exact constant value (exact.Value) for // every expression (ast.Expr) that is a compile-time constant. // Use Info.Values for the results of constant folding. // // Type inference computes the type (Type) of every expression (ast.Expr) // and checks for compliance with the language specification. // Use Info.Types for the results of type evaluation. // package types import ( "bytes" "fmt" "go/ast" "go/token" "code.google.com/p/go.tools/go/exact" ) // Check type-checks a package and returns the resulting complete package // object, or a nil package and the first error. The package is specified // by a list of *ast.Files and corresponding file set, and the import path // the package is identified with. The clean path must not be empty or dot ("."). // // For more control over type-checking and results, use Config.Check. func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) { var conf Config pkg, err := conf.Check(path, fset, files, nil) if err != nil { return nil, err } return pkg, nil } // An Error describes a type-checking error; // it implements the error interface. type Error struct { Fset *token.FileSet // file set for interpretation of Pos Pos token.Pos // error position Msg string // error message } // Error returns an error string formatted as follows: // filename:line:column: message func (err Error) Error() string { return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg) } // An importer resolves import paths to Packages. // The imports map records packages already known, // indexed by package path. The type-checker // will invoke Import with Config.Packages. // An importer must determine the canonical package path and // check imports to see if it is already present in the map. // If so, the Importer can return the map entry. Otherwise, // the importer must load the package data for the given path // into a new *Package, record it in imports map, and return // the package. // TODO(gri) Need to be clearer about requirements of completeness. type Importer func(map[string]*Package, string) (*Package, error) // A Config specifies the configuration for type checking. // The zero value for Config is a ready-to-use default configuration. type Config struct { // If IgnoreFuncBodies is set, function bodies are not // type-checked. IgnoreFuncBodies bool // If FakeImportC is set, `import "C"` (for packages requiring Cgo) // declares an empty "C" package and errors are omitted for qualified // identifiers referring to package C (which won't find an object). // This feature is intended for the standard library cmd/api tool. // // Caution: Effects may be unpredictable due to follow-up errors. // Do not use casually! FakeImportC bool // Packages is used to look up (and thus canonicalize) packages by // package path. If Packages is nil, it is set to a new empty map. // During type-checking, imported packages are added to the map. Packages map[string]*Package // If Error != nil, it is called with each error found // during type checking; err has dynamic type Error. Error func(err error) // If Import != nil, it is called for each imported package. // Otherwise, DefaultImport is called. Import Importer // If Sizes != nil, it provides the sizing functions for package unsafe. // Otherwise &StdSize{WordSize: 8, MaxAlign: 8} is used instead. Sizes Sizes } // DefaultImport is the default importer invoked if Config.Import == nil. // The declaration: // // import _ "code.google.com/p/go.tools/go/gcimporter" // // in a client of go/types will initialize DefaultImport to gcimporter.Import. var DefaultImport Importer // Info holds result type information for a type-checked package. // Only the information for which a map is provided is collected. // If the package has type errors, the collected information may // be incomplete. type Info struct { // Types maps expressions to their types. Identifiers on the // lhs of declarations are collected in Objects, not Types. // // For an expression denoting a predeclared built-in function // the recorded signature is call-site specific. If the call // result is not a constant, the recorded type is an argument- // specific signature. Otherwise, the recorded type is invalid. Types map[ast.Expr]Type // Values maps constant expressions to their values. Values map[ast.Expr]exact.Value // Objects maps identifiers to their corresponding objects (including // package names, dots "." of dot-imports, and blank "_" identifiers). // For identifiers that do not denote objects (e.g., the package name // in package clauses, blank identifiers on the lhs of assignments, or // symbolic variables t in t := x.(type) of type switch headers), the // corresponding objects are nil. Objects map[*ast.Ident]Object // Implicits maps nodes to their implicitly declared objects, if any. // The following node and object types may appear: // // node declared object // // *ast.ImportSpec *PkgName (imports w/o renames), or imported objects (dot-imports) // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default) // *ast.Field anonymous struct field or parameter *Var // Implicits map[ast.Node]Object // Selections maps selector expressions to their corresponding selections. Selections map[*ast.SelectorExpr]*Selection // Scopes maps ast.Nodes to the scopes they define. Note that package scopes // are not associated with a specific node but with all files belonging to a // package. Thus, the package scope can be found in the type-checked package // object. // // The following node types may appear in Scopes: // // *ast.File // *ast.FuncType // *ast.BlockStmt // *ast.IfStmt // *ast.SwitchStmt // *ast.TypeSwitchStmt // *ast.CaseClause // *ast.CommClause // *ast.ForStmt // *ast.RangeStmt // Scopes map[ast.Node]*Scope // InitOrder is the list of package-level initializers in the order in which // they must be executed. Initializers referring to variables related by an // initialization dependency appear in topological order, the others appear // in source order. Variables without an initialization expression do not // appear in this list. InitOrder []*Initializer } // An Initializer describes a package-level variable, or a list of variables in case // of a multi-valued initialization expression, and the corresponding initialization // expression. type Initializer struct { Lhs []*Var // var Lhs = Rhs Rhs ast.Expr } func (init *Initializer) String() string { var buf bytes.Buffer for i, lhs := range init.Lhs { if i > 0 { buf.WriteString(", ") } buf.WriteString(lhs.Name()) } buf.WriteString(" = ") WriteExpr(&buf, init.Rhs) return buf.String() } // Check type-checks a package and returns the resulting package object, // the first error if any, and if info != nil, additional type information. // The package is marked as complete if no errors occurred, otherwise it is // incomplete. // // The package is specified by a list of *ast.Files and corresponding // file set, and the package path the package is identified with. // The clean path must not be empty or dot ("."). func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) { pkg, err := conf.check(path, fset, files, info) if err == nil { pkg.complete = true } return pkg, err } // IsAssignableTo reports whether a value of type V is assignable to a variable of type T. func IsAssignableTo(V, T Type) bool { x := operand{mode: value, typ: V} return x.isAssignableTo(nil, T) // config not needed for non-constant x } // Implements reports whether a value of type V implements T, as follows: // // 1) For non-interface types V, or if static is set, V implements T if all // methods of T are present in V. Informally, this reports whether V is a // subtype of T. // // 2) For interface types V, and if static is not set, V implements T if all // methods of T which are also present in V have matching types. Informally, // this indicates whether a type assertion x.(T) where x is of type V would // be legal (the concrete dynamic type of x may implement T even if V does // not statically implement it). // func Implements(V Type, T *Interface, static bool) bool { f, _ := MissingMethod(V, T, static) return f == nil } ./go/types/check_test.go0000644000014500017510000001632512246613010014716 0ustar michaelstaff// 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 implements a typechecker test harness. The packages specified // in tests are typechecked. Error messages reported by the typechecker are // compared against the error messages expected in the test files. // // Expected errors are indicated in the test files by putting a comment // of the form /* ERROR "rx" */ immediately following an offending token. // The harness will verify that an error matching the regular expression // rx is reported at that source position. Consecutive comments may be // used to indicate multiple errors for the same token position. // // For instance, the following test file indicates that a "not declared" // error should be reported for the undeclared variable x: // // package p // func f() { // _ = x /* ERROR "not declared" */ + 1 // } package types_test import ( "flag" "go/ast" "go/parser" "go/scanner" "go/token" "io/ioutil" "regexp" "strings" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) var ( listErrors = flag.Bool("list", false, "list errors") testFiles = flag.String("files", "", "space-separated list of test files") ) // The test filenames do not end in .go so that they are invisible // to gofmt since they contain comments that must not change their // positions relative to surrounding tokens. // Each tests entry is list of files belonging to the same package. var tests = [][]string{ {"testdata/errors.src"}, {"testdata/importdecl0a.src", "testdata/importdecl0b.src"}, {"testdata/cycles.src"}, {"testdata/cycles1.src"}, {"testdata/cycles2.src"}, {"testdata/cycles3.src"}, {"testdata/cycles4.src"}, {"testdata/init0.src"}, {"testdata/init1.src"}, {"testdata/decls0.src"}, {"testdata/decls1.src"}, {"testdata/decls2a.src", "testdata/decls2b.src"}, {"testdata/decls3.src"}, {"testdata/const0.src"}, {"testdata/const1.src"}, {"testdata/constdecl.src"}, {"testdata/vardecl.src"}, {"testdata/expr0.src"}, {"testdata/expr1.src"}, {"testdata/expr2.src"}, {"testdata/expr3.src"}, {"testdata/methodsets.src"}, {"testdata/shifts.src"}, {"testdata/builtins.src"}, {"testdata/conversions.src"}, {"testdata/stmt0.src"}, {"testdata/stmt1.src"}, {"testdata/gotos.src"}, {"testdata/labels.src"}, } var fset = token.NewFileSet() // Positioned errors are of the form filename:line:column: message . var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`) // splitError splits an error's error message into a position string // and the actual error message. If there's no position information, // pos is the empty string, and msg is the entire error message. // func splitError(err error) (pos, msg string) { msg = err.Error() if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 { pos = m[1] msg = m[2] } return } func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) { var files []*ast.File var errlist []error for _, filename := range filenames { file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) if file == nil { t.Fatalf("%s: %s", filename, err) } files = append(files, file) if err != nil { if list, _ := err.(scanner.ErrorList); len(list) > 0 { for _, err := range list { errlist = append(errlist, err) } } else { errlist = append(errlist, err) } } } return files, errlist } // ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where // rx is a regular expression that matches the expected error message. // Space around "rx" or rx is ignored. // var errRx = regexp.MustCompile(`^ *ERROR *"?([^"]*)"?`) // errMap collects the regular expressions of ERROR comments found // in files and returns them as a map of error positions to error messages. // func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string { // map of position strings to lists of error message patterns errmap := make(map[string][]string) for _, file := range files { filename := fset.Position(file.Package).Filename src, err := ioutil.ReadFile(filename) if err != nil { t.Fatalf("%s: could not read %s", testname, filename) } var s scanner.Scanner s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments) var prev string // position string of last non-comment, non-semicolon token scanFile: for { pos, tok, lit := s.Scan() switch tok { case token.EOF: break scanFile case token.COMMENT: if lit[1] == '*' { lit = lit[:len(lit)-2] // strip trailing */ } if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 2 { errmap[prev] = append(errmap[prev], strings.TrimSpace(s[1])) } case token.SEMICOLON: // ignore automatically inserted semicolon if lit == "\n" { continue scanFile } fallthrough default: prev = fset.Position(pos).String() } } } return errmap } func eliminate(t *testing.T, errmap map[string][]string, errlist []error) { for _, err := range errlist { pos, gotMsg := splitError(err) list := errmap[pos] index := -1 // list index of matching message, if any // we expect one of the messages in list to match the error at pos for i, wantRx := range list { rx, err := regexp.Compile(wantRx) if err != nil { t.Errorf("%s: %v", pos, err) continue } if rx.MatchString(gotMsg) { index = i break } } if index >= 0 { // eliminate from list if n := len(list) - 1; n > 0 { // not the last entry - swap in last element and shorten list by 1 list[index] = list[n] errmap[pos] = list[:n] } else { // last entry - remove list from map delete(errmap, pos) } } else { t.Errorf("%s: no error expected: %q", pos, gotMsg) } } } func checkFiles(t *testing.T, testfiles []string) { // parse files and collect parser errors files, errlist := parseFiles(t, testfiles) pkgName := "" if len(files) > 0 { pkgName = files[0].Name.Name } if *listErrors && len(errlist) > 0 { t.Errorf("--- %s:", pkgName) for _, err := range errlist { t.Error(err) } } // typecheck and collect typechecker errors var conf Config conf.Error = func(err error) { if *listErrors { t.Error(err) return } // Ignore secondary error messages starting with "\t"; // they are clarifying messages for a primary error. if !strings.Contains(err.Error(), ": \t") { errlist = append(errlist, err) } } conf.Check(pkgName, fset, files, nil) if *listErrors { return } // match and eliminate errors; // we are expecting the following errors errmap := errMap(t, pkgName, files) eliminate(t, errmap, errlist) // there should be no expected errors left if len(errmap) > 0 { t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap)) for pos, list := range errmap { for _, rx := range list { t.Errorf("%s: %q", pos, rx) } } } } func TestCheck(t *testing.T) { // Declare builtins for testing. DefPredeclaredTestFuncs() // If explicit test files are specified, only check those. if files := *testFiles; files != "" { checkFiles(t, strings.Split(files, " ")) return } // Otherwise, run all the tests. for _, files := range tests { checkFiles(t, files) } } ./go/types/sizes.go0000644000014500017510000001125412246613010013733 0ustar michaelstaff// 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. // This file implements Sizes. package types // Sizes defines the sizing functions for package unsafe. type Sizes interface { // Alignof returns the alignment of a variable of type T. // Alignof must implement the alignment guarantees required by the spec. Alignof(T Type) int64 // Offsetsof returns the offsets of the given struct fields, in bytes. // Offsetsof must implement the offset guarantees required by the spec. Offsetsof(fields []*Var) []int64 // Sizeof returns the size of a variable of type T. // Sizeof must implement the size guarantees required by the spec. Sizeof(T Type) int64 } // StdSizes is a convenience type for creating commonly used Sizes. // It makes the following simplifying assumptions: // // - The size of explicitly sized basic types (int16, etc.) is the // specified size. // - The size of strings, functions, and interfaces is 2*WordSize. // - The size of slices is 3*WordSize. // - All other types have size WordSize. // - Arrays and structs are aligned per spec definition; all other // types are naturally aligned with a maximum alignment MaxAlign. // // *StdSizes implements Sizes. // type StdSizes struct { WordSize int64 // word size in bytes - must be >= 4 (32bits) MaxAlign int64 // maximum alignment in bytes - must be >= 1 } func (s *StdSizes) Alignof(T Type) int64 { // For arrays and structs, alignment is defined in terms // of alignment of the elements and fields, respectively. switch t := T.Underlying().(type) { case *Array: // spec: "For a variable x of array type: unsafe.Alignof(x) // is the same as unsafe.Alignof(x[0]), but at least 1." return s.Alignof(t.elem) case *Struct: // spec: "For a variable x of struct type: unsafe.Alignof(x) // is the largest of the values unsafe.Alignof(x.f) for each // field f of x, but at least 1." max := int64(1) for _, f := range t.fields { if a := s.Alignof(f.typ); a > max { max = a } } return max } a := s.Sizeof(T) // may be 0 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." if a < 1 { return 1 } if a > s.MaxAlign { return s.MaxAlign } return a } func (s *StdSizes) Offsetsof(fields []*Var) []int64 { offsets := make([]int64, len(fields)) var o int64 for i, f := range fields { a := s.Alignof(f.typ) o = align(o, a) offsets[i] = o o += s.Sizeof(f.typ) } return offsets } func (s *StdSizes) Sizeof(T Type) int64 { switch t := T.Underlying().(type) { case *Basic: if z := t.size; z > 0 { return z } if t.kind == String { return s.WordSize * 2 } case *Array: a := s.Alignof(t.elem) z := s.Sizeof(t.elem) return align(z, a) * t.len // may be 0 case *Slice: return s.WordSize * 3 case *Struct: n := t.NumFields() if n == 0 { return 0 } offsets := t.offsets if t.offsets == nil { // compute offsets on demand offsets = s.Offsetsof(t.fields) t.offsets = offsets } return offsets[n-1] + s.Sizeof(t.fields[n-1].typ) case *Signature, *Interface: return s.WordSize * 2 } return s.WordSize // catch-all } // stdSizes is used if Config.Sizes == nil. var stdSizes = StdSizes{8, 8} func (conf *Config) alignof(T Type) int64 { if s := conf.Sizes; s != nil { if a := s.Alignof(T); a >= 1 { return a } panic("Config.Sizes.Alignof returned an alignment < 1") } return stdSizes.Alignof(T) } func (conf *Config) offsetsof(T *Struct) []int64 { offsets := T.offsets if offsets == nil && T.NumFields() > 0 { // compute offsets on demand if s := conf.Sizes; s != nil { offsets = s.Offsetsof(T.fields) // sanity checks if len(offsets) != T.NumFields() { panic("Config.Sizes.Offsetsof returned the wrong number of offsets") } for _, o := range offsets { if o < 0 { panic("Config.Sizes.Offsetsof returned an offset < 0") } } } else { offsets = stdSizes.Offsetsof(T.fields) } T.offsets = offsets } return offsets } // offsetof returns the offset of the field specified via // the index sequence relative to typ. All embedded fields // must be structs (rather than pointer to structs). func (conf *Config) offsetof(typ Type, index []int) int64 { var o int64 for _, i := range index { s := typ.Underlying().(*Struct) o += conf.offsetsof(s)[i] typ = s.fields[i].typ } return o } func (conf *Config) sizeof(T Type) int64 { if s := conf.Sizes; s != nil { if z := s.Sizeof(T); z >= 0 { return z } panic("Config.Sizes.Sizeof returned a size < 0") } return stdSizes.Sizeof(T) } // align returns the smallest y >= x such that y % a == 0. func align(x, a int64) int64 { y := x + a - 1 return y - y%a } ./go/types/call.go0000644000014500017510000002622212246613010013512 0ustar michaelstaff// 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. // This file implements typechecking of call and selector expressions. package types import ( "go/ast" "go/token" ) func (check *checker) call(x *operand, e *ast.CallExpr) exprKind { check.exprOrType(x, e.Fun) switch x.mode { case invalid: check.use(e.Args) x.mode = invalid x.expr = e return statement case typexpr: // conversion T := x.typ x.mode = invalid switch n := len(e.Args); n { case 0: check.errorf(e.Rparen, "missing argument in conversion to %s", T) case 1: check.expr(x, e.Args[0]) if x.mode != invalid { check.conversion(x, T) if x.mode != invalid { check.conversions[e] = true // for cap/len checking } } default: check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T) } x.expr = e return conversion case builtin: id := x.id if !check.builtin(x, e, id) { x.mode = invalid } x.expr = e return predeclaredFuncs[id].kind default: // function/method call sig, _ := x.typ.Underlying().(*Signature) if sig == nil { check.invalidOp(x.pos(), "cannot call non-function %s", x) x.mode = invalid x.expr = e return statement } arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false) check.arguments(x, e, sig, arg, n) // determine result switch sig.results.Len() { case 0: x.mode = novalue case 1: x.mode = value x.typ = sig.results.vars[0].typ // unpack tuple default: x.mode = value x.typ = sig.results } x.expr = e return statement } } // use type-checks each list element. // Useful to make sure a list of expressions is evaluated // (and variables are "used") in the presence of other errors. func (check *checker) use(list []ast.Expr) { var x operand for _, e := range list { check.rawExpr(&x, e, nil) } } // A getter sets x as the i'th operand, where 0 <= i < n and n is the total // number of operands (context-specific, and maintained elsewhere). A getter // type-checks the i'th operand; the details of the actual check are getter- // specific. type getter func(x *operand, i int) // unpack takes a getter get and a number of operands n. If n == 1 and the // first operand is a function call, or a comma,ok expression and allowCommaOk // is set, the result is a new getter and operand count providing access to the // function results, or comma,ok values, respectively. The third result value // reports if it is indeed the comma,ok case. In all other cases, the incoming // getter and operand count are returned unchanged, and the third result value // is false. // // In other words, if there's exactly one operand that - after type-checking by // calling get - stands for multiple operands, the resulting getter provides access // to those operands instead. // // Note that unpack may call get(..., 0); but if the result getter is called // at most once for a given operand index i (including i == 0), that operand // is guaranteed to cause only one call of the incoming getter with that i. // func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) { if n == 1 { // possibly result of an n-valued function call or comma,ok value var x0 operand get(&x0, 0) if x0.mode == invalid { return func(x *operand, i int) { if i != 0 { unreachable() } x.mode = invalid }, 1, false } if t, ok := x0.typ.(*Tuple); ok { // result of an n-valued function call return func(x *operand, i int) { x.mode = value x.expr = x0.expr x.typ = t.At(i).typ }, t.Len(), false } if x0.mode == mapindex || x0.mode == commaok { // comma-ok value if allowCommaOk { a := [2]Type{x0.typ, Typ[UntypedBool]} return func(x *operand, i int) { x.mode = value x.expr = x0.expr x.typ = a[i] }, 2, true } x0.mode = value } // single value return func(x *operand, i int) { if i != 0 { unreachable() } *x = x0 }, 1, false } // zero or multiple values return get, n, false } // arguments checks argument passing for the call with the given signature. // The arg function provides the operand for the i'th argument. func (check *checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) { passSlice := false if call.Ellipsis.IsValid() { // last argument is of the form x... if sig.isVariadic { passSlice = true } else { check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun) // ok to continue } } // evaluate arguments for i := 0; i < n; i++ { arg(x, i) if x.mode != invalid { check.argument(sig, i, x, passSlice && i == n-1) } } // check argument count if sig.isVariadic { // a variadic function accepts an "empty" // last argument: count one extra n++ } if n < sig.params.Len() { check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun) // ok to continue } } // argument checks passing of argument x to the i'th parameter of the given signature. // If passSlice is set, the argument is followed by ... in the call. func (check *checker) argument(sig *Signature, i int, x *operand, passSlice bool) { n := sig.params.Len() // determine parameter type var typ Type switch { case i < n: typ = sig.params.vars[i].typ case sig.isVariadic: typ = sig.params.vars[n-1].typ if debug { if _, ok := typ.(*Slice); !ok { check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) } } default: check.errorf(x.pos(), "too many arguments") return } if passSlice { // argument is of the form x... if i != n-1 { check.errorf(x.pos(), "can only use ... with matching parameter") return } if _, ok := x.typ.Underlying().(*Slice); !ok { check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ) return } } else if sig.isVariadic && i >= n-1 { // use the variadic parameter slice's element type typ = typ.(*Slice).elem } if !check.assignment(x, typ) && x.mode != invalid { check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ) } } func (check *checker) selector(x *operand, e *ast.SelectorExpr) { // these must be declared before the "goto Error" statements var ( obj Object index []int indirect bool ) sel := e.Sel.Name // If the identifier refers to a package, handle everything here // so we don't need a "package" mode for operands: package names // can only appear in qualified identifiers which are mapped to // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { if pkg, _ := check.topScope.LookupParent(ident.Name).(*PkgName); pkg != nil { check.recordObject(ident, pkg) pkg.used = true exp := pkg.pkg.scope.Lookup(sel) if exp == nil { if !pkg.pkg.fake { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) } goto Error } if !exp.IsExported() { check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) // ok to continue } check.recordSelection(e, PackageObj, nil, exp, nil, false) // Simplified version of the code for *ast.Idents: // - imported packages use types.Scope and types.Objects // - imported objects are always fully initialized switch exp := exp.(type) { case *Const: assert(exp.Val() != nil) x.mode = constant x.typ = exp.typ x.val = exp.val case *TypeName: x.mode = typexpr x.typ = exp.typ case *Var: x.mode = variable x.typ = exp.typ case *Func: x.mode = value x.typ = exp.typ case *Builtin: x.mode = builtin x.typ = exp.typ x.id = exp.id default: unreachable() } x.expr = e return } } check.exprOrType(x, e.X) if x.mode == invalid { goto Error } obj, index, indirect = LookupFieldOrMethod(x.typ, check.pkg, sel) if obj == nil { if index != nil { // TODO(gri) should provide actual type where the conflict happens check.invalidOp(e.Pos(), "ambiguous selector %s", sel) } else { check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) } goto Error } if x.mode == typexpr { // method expression m, _ := obj.(*Func) if m == nil { check.invalidOp(e.Pos(), "%s has no method %s", x, sel) goto Error } // verify that m is in the method set of x.typ if !indirect && ptrRecv(m) { check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) goto Error } check.recordSelection(e, MethodExpr, x.typ, m, index, indirect) // the receiver type becomes the type of the first function // argument of the method expression's function type var params []*Var sig := m.typ.(*Signature) if sig.params != nil { params = sig.params.vars } x.mode = value x.typ = &Signature{ params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...), results: sig.results, isVariadic: sig.isVariadic, } check.addDeclDep(m) } else { // regular selector switch obj := obj.(type) { case *Var: check.recordSelection(e, FieldVal, x.typ, obj, index, indirect) if x.mode == variable || indirect { x.mode = variable } else { x.mode = value } x.typ = obj.typ case *Func: // TODO(gri) This code appears elsewhere, too. Factor! // verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable) // // spec: "A method call x.m() is valid if the method set of (the type of) x // contains m and the argument list can be assigned to the parameter // list of m. If x is addressable and &x's method set contains m, x.m() // is shorthand for (&x).m()". if !indirect && x.mode != variable && ptrRecv(obj) { check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x) goto Error } check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) if debug { // Verify that LookupFieldOrMethod and MethodSet.Lookup agree. typ := x.typ if x.mode == variable { // If typ is not an (unnamed) pointer or an interface, // use *typ instead, because the method set of *typ // includes the methods of typ. // Variables are addressable, so we can always take their // address. if _, ok := typ.(*Pointer); !ok && !isInterface(typ) { typ = &Pointer{base: typ} } } // If we created a synthetic pointer type above, we will throw // away the method set computed here after use. // TODO(gri) Method set computation should probably always compute // both, the value and the pointer receiver method set and represent // them in a single structure. // TODO(gri) Consider also using a method set cache for the lifetime // of checker once we rely on MethodSet lookup instead of individual // lookup. mset := typ.MethodSet() if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj { check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m) check.dump("%s\n", mset) panic("method sets and lookup don't agree") } } x.mode = value // remove receiver sig := *obj.typ.(*Signature) sig.recv = nil x.typ = &sig default: unreachable() } } // everything went well x.expr = e return Error: x.mode = invalid x.expr = e } ./go/types/labels.go0000644000014500017510000001563412246613010014046 0ustar michaelstaff// 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 types import ( "go/ast" "go/token" ) // labels checks correct label use in body. func (check *checker) labels(body *ast.BlockStmt) { // set of all labels in this body all := NewScope(nil) fwdJumps := check.blockBranches(all, nil, nil, body.List) // If there are any forward jumps left, no label was found for // the corresponding goto statements. Either those labels were // never defined, or they are inside blocks and not reachable // for the respective gotos. for _, jmp := range fwdJumps { var msg string name := jmp.Label.Name if alt := all.Lookup(name); alt != nil { msg = "goto %s jumps into block" alt.(*Label).used = true // avoid another error } else { msg = "label %s not declared" } check.errorf(jmp.Label.Pos(), msg, name) } // spec: "It is illegal to define a label that is never used." for _, obj := range all.elems { if lbl := obj.(*Label); !lbl.used { check.errorf(lbl.pos, "label %s declared but not used", lbl.name) } } } // A block tracks label declarations in a block and its enclosing blocks. type block struct { parent *block // enclosing block lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil labels map[string]*ast.LabeledStmt // allocated lazily } // insert records a new label declaration for the current block. // The label must not have been declared before in any block. func (b *block) insert(s *ast.LabeledStmt) { name := s.Label.Name if debug { assert(b.gotoTarget(name) == nil) } labels := b.labels if labels == nil { labels = make(map[string]*ast.LabeledStmt) b.labels = labels } labels[name] = s } // gotoTarget returns the labeled statement in the current // or an enclosing block with the given label name, or nil. func (b *block) gotoTarget(name string) *ast.LabeledStmt { for s := b; s != nil; s = s.parent { if t := s.labels[name]; t != nil { return t } } return nil } // enclosingTarget returns the innermost enclosing labeled // statement with the given label name, or nil. func (b *block) enclosingTarget(name string) *ast.LabeledStmt { for s := b; s != nil; s = s.parent { if t := s.lstmt; t != nil && t.Label.Name == name { return t } } return nil } // blockBranches processes a block's statement list and returns the set of outgoing forward jumps. // all is the scope of all declared labels, parent the set of labels declared in the immediately // enclosing block, and lstmt is the labeled statement this block is associated with (or nil). func (check *checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt { b := &block{parent: parent, lstmt: lstmt} var ( varDeclPos token.Pos fwdJumps, badJumps []*ast.BranchStmt ) // All forward jumps jumping over a variable declaration are possibly // invalid (they may still jump out of the block and be ok). // recordVarDecl records them for the given position. recordVarDecl := func(pos token.Pos) { varDeclPos = pos badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps } jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool { if varDeclPos.IsValid() { for _, bad := range badJumps { if jmp == bad { return true } } } return false } blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) { // Unresolved forward jumps inside the nested block // become forward jumps in the current block. fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...) } var stmtBranches func(ast.Stmt) stmtBranches = func(s ast.Stmt) { switch s := s.(type) { case *ast.DeclStmt: if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR { recordVarDecl(d.Pos()) } case *ast.LabeledStmt: // declare label name := s.Label.Name lbl := NewLabel(s.Label.Pos(), name) if alt := all.Insert(lbl); alt != nil { check.errorf(lbl.pos, "label %s already declared", name) check.reportAltDecl(alt) // ok to continue } else { b.insert(s) check.recordObject(s.Label, lbl) } // resolve matching forward jumps and remove them from fwdJumps i := 0 for _, jmp := range fwdJumps { if jmp.Label.Name == name { // match lbl.used = true check.recordObject(jmp.Label, lbl) if jumpsOverVarDecl(jmp) { check.errorf( jmp.Label.Pos(), "goto %s jumps over variable declaration at line %d", name, check.fset.Position(varDeclPos).Line, ) // ok to continue } } else { // no match - record new forward jump fwdJumps[i] = jmp i++ } } fwdJumps = fwdJumps[:i] lstmt = s stmtBranches(s.Stmt) case *ast.BranchStmt: if s.Label == nil { return // checked in 1st pass (check.stmt) } // determine and validate target name := s.Label.Name switch s.Tok { case token.BREAK: // spec: "If there is a label, it must be that of an enclosing // "for", "switch", or "select" statement, and that is the one // whose execution terminates." valid := false if t := b.enclosingTarget(name); t != nil { switch t.Stmt.(type) { case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt: valid = true } } if !valid { check.errorf(s.Label.Pos(), "invalid break label %s", name) return } case token.CONTINUE: // spec: "If there is a label, it must be that of an enclosing // "for" statement, and that is the one whose execution advances." valid := false if t := b.enclosingTarget(name); t != nil { switch t.Stmt.(type) { case *ast.ForStmt, *ast.RangeStmt: valid = true } } if !valid { check.errorf(s.Label.Pos(), "invalid continue label %s", name) return } case token.GOTO: if b.gotoTarget(name) == nil { // label may be declared later - add branch to forward jumps fwdJumps = append(fwdJumps, s) return } default: check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name) return } // record label use obj := all.Lookup(name) obj.(*Label).used = true check.recordObject(s.Label, obj) case *ast.AssignStmt: if s.Tok == token.DEFINE { recordVarDecl(s.Pos()) } case *ast.BlockStmt: blockBranches(lstmt, s.List) case *ast.IfStmt: stmtBranches(s.Body) if s.Else != nil { stmtBranches(s.Else) } case *ast.CaseClause: blockBranches(nil, s.Body) case *ast.SwitchStmt: stmtBranches(s.Body) case *ast.TypeSwitchStmt: stmtBranches(s.Body) case *ast.CommClause: blockBranches(nil, s.Body) case *ast.SelectStmt: stmtBranches(s.Body) case *ast.ForStmt: stmtBranches(s.Body) case *ast.RangeStmt: stmtBranches(s.Body) } } for _, s := range list { stmtBranches(s) } return fwdJumps } ./go/types/types.go0000644000014500017510000003206212246613010013742 0ustar michaelstaff// 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. package types import ( "go/ast" "sort" ) // TODO(gri) Revisit factory functions - make sure they have all relevant parameters. // A Type represents a type of Go. // All types implement the Type interface. type Type interface { // Underlying returns the underlying type of a type. Underlying() Type // MethodSet returns the method set of a type. MethodSet() *MethodSet // String returns a string representation of a type. String() string } // BasicKind describes the kind of basic type. type BasicKind int const ( Invalid BasicKind = iota // type is invalid // predeclared types Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 String UnsafePointer // types for untyped values UntypedBool UntypedInt UntypedRune UntypedFloat UntypedComplex UntypedString UntypedNil // aliases Byte = Uint8 Rune = Int32 ) // BasicInfo is a set of flags describing properties of a basic type. type BasicInfo int // Properties of basic types. const ( IsBoolean BasicInfo = 1 << iota IsInteger IsUnsigned IsFloat IsComplex IsString IsUntyped IsOrdered = IsInteger | IsFloat | IsString IsNumeric = IsInteger | IsFloat | IsComplex IsConstType = IsBoolean | IsNumeric | IsString ) // A Basic represents a basic type. type Basic struct { kind BasicKind info BasicInfo size int64 // use DefaultSizeof to get size name string } // Kind returns the kind of basic type b. func (b *Basic) Kind() BasicKind { return b.kind } // Info returns information about properties of basic type b. func (b *Basic) Info() BasicInfo { return b.info } // Name returns the name of basic type b. func (b *Basic) Name() string { return b.name } // An Array represents an array type. type Array struct { len int64 elem Type } // NewArray returns a new array type for the given element type and length. func NewArray(elem Type, len int64) *Array { return &Array{len, elem} } // Len returns the length of array a. func (a *Array) Len() int64 { return a.len } // Elem returns element type of array a. func (a *Array) Elem() Type { return a.elem } // A Slice represents a slice type. type Slice struct { elem Type } // NewSlice returns a new slice type for the given element type. func NewSlice(elem Type) *Slice { return &Slice{elem} } // Elem returns the element type of slice s. func (s *Slice) Elem() Type { return s.elem } // A Struct represents a struct type. type Struct struct { fields []*Var tags []string // field tags; nil if there are no tags // TODO(gri) access to offsets is not threadsafe - fix this offsets []int64 // field offsets in bytes, lazily initialized mset cachedMethodSet // method set, lazily initialized } // NewStruct returns a new struct with the given fields and corresponding field tags. // If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be // only as long as required to hold the tag with the largest index i. Consequently, // if no field has a tag, tags may be nil. func NewStruct(fields []*Var, tags []string) *Struct { var fset objset for _, f := range fields { if f.name != "_" && fset.insert(f) != nil { panic("multiple fields with the same name") } } if len(tags) > len(fields) { panic("more tags than fields") } return &Struct{fields: fields, tags: tags} } // NumFields returns the number of fields in the struct (including blank and anonymous fields). func (s *Struct) NumFields() int { return len(s.fields) } // Field returns the i'th field for 0 <= i < NumFields(). func (s *Struct) Field(i int) *Var { return s.fields[i] } // Tag returns the i'th field tag for 0 <= i < NumFields(). func (s *Struct) Tag(i int) string { if i < len(s.tags) { return s.tags[i] } return "" } // A Pointer represents a pointer type. type Pointer struct { base Type // element type mset cachedMethodSet // method set, lazily initialized } // NewPointer returns a new pointer type for the given element (base) type. func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} } // Elem returns the element type for the given pointer p. func (p *Pointer) Elem() Type { return p.base } // A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. // Tuples are used as components of signatures and to represent the type of multiple // assignments; they are not first class types of Go. type Tuple struct { vars []*Var } // NewTuple returns a new tuple for the given variables. func NewTuple(x ...*Var) *Tuple { if len(x) > 0 { return &Tuple{x} } return nil } // Len returns the number variables of tuple t. func (t *Tuple) Len() int { if t != nil { return len(t.vars) } return 0 } // At returns the i'th variable of tuple t. func (t *Tuple) At(i int) *Var { return t.vars[i] } // A Signature represents a (non-builtin) function or method type. type Signature struct { scope *Scope // function scope, always present recv *Var // nil if not a method params *Tuple // (incoming) parameters from left to right; or nil results *Tuple // (outgoing) results from left to right; or nil isVariadic bool // true if the last parameter's type is of the form ...T } // NewSignature returns a new function type for the given receiver, parameters, // and results, either of which may be nil. If isVariadic is set, the function // is variadic, it must have at least one parameter, and the last parameter // must be of unnamed slice type. func NewSignature(scope *Scope, recv *Var, params, results *Tuple, isVariadic bool) *Signature { // TODO(gri) Should we rely on the correct (non-nil) incoming scope // or should this function allocate and populate a scope? if isVariadic { n := params.Len() if n == 0 { panic("types.NewSignature: variadic function must have at least one parameter") } if _, ok := params.At(n - 1).typ.(*Slice); !ok { panic("types.NewSignature: variadic parameter must be of unnamed slice type") } } return &Signature{scope, recv, params, results, isVariadic} } // Recv returns the receiver of signature s (if a method), or nil if a // function. // // For an abstract method, Recv returns the enclosing interface either // as a *Named or an *Interface. Due to embedding, an interface may // contain methods whose receiver type is a different interface. func (s *Signature) Recv() *Var { return s.recv } // Params returns the parameters of signature s, or nil. func (s *Signature) Params() *Tuple { return s.params } // Results returns the results of signature s, or nil. func (s *Signature) Results() *Tuple { return s.results } // IsVariadic reports whether the signature s is variadic. func (s *Signature) IsVariadic() bool { return s.isVariadic } // An Interface represents an interface type. type Interface struct { methods []*Func // explicitly declared methods types []*Named // explicitly embedded types allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) mset cachedMethodSet // method set for interface, lazily initialized } // NewInterface returns a new interface for the given methods. func NewInterface(methods []*Func, types []*Named) *Interface { typ := new(Interface) var mset objset for _, m := range methods { if mset.insert(m) != nil { panic("multiple methods with the same name") } // set receiver // TODO(gri) Ideally, we should use a named type here instead of // typ, for less verbose printing of interface method signatures. m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ) } sort.Sort(byUniqueMethodName(methods)) if types != nil { panic("unimplemented") } return &Interface{methods: methods, allMethods: methods} } // NumMethods returns the number of methods of interface t. func (t *Interface) NumMethods() int { return len(t.allMethods) } // Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). func (t *Interface) Method(i int) *Func { return t.allMethods[i] } // A Map represents a map type. type Map struct { key, elem Type } // NewMap returns a new map for the given key and element types. func NewMap(key, elem Type) *Map { return &Map{key, elem} } // Key returns the key type of map m. func (m *Map) Key() Type { return m.key } // Elem returns the element type of map m. func (m *Map) Elem() Type { return m.elem } // A Chan represents a channel type. type Chan struct { dir ast.ChanDir elem Type } // NewChan returns a new channel type for the given direction and element type. func NewChan(dir ast.ChanDir, elem Type) *Chan { return &Chan{dir, elem} } // Dir returns the direction of channel c. func (c *Chan) Dir() ast.ChanDir { return c.dir } // Elem returns the element type of channel c. func (c *Chan) Elem() Type { return c.elem } // A Named represents a named type. type Named struct { obj *TypeName // corresponding declared object underlying Type // possibly a *Named if !complete; never a *Named if complete complete bool // if set, the underlying type has been determined methods []*Func // methods declared for this type (not the method set of this type) mset, pmset cachedMethodSet // method set for T, *T, lazily initialized } // NewNamed returns a new named type for the given type name, underlying type, and associated methods. // The underlying type must not be a *Named. func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { if _, ok := underlying.(*Named); ok { panic("types.NewNamed: underlying type must not be *Named") } typ := &Named{obj: obj, underlying: underlying, complete: underlying != nil, methods: methods} if obj.typ == nil { obj.typ = typ } return typ } // TypeName 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. func (t *Named) NumMethods() int { return len(t.methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). func (t *Named) Method(i int) *Func { return t.methods[i] } // SetUnderlying sets the underlying type and marks t as complete. // TODO(gri) determine if there's a better solution rather than providing this function func (t *Named) SetUnderlying(underlying Type) { if underlying == nil { panic("types.Named.SetUnderlying: underlying type must not be nil") } if _, ok := underlying.(*Named); ok { panic("types.Named.SetUnderlying: underlying type must not be *Named") } t.underlying = underlying t.complete = true } // AddMethod adds method m unless it is already in the method list. // TODO(gri) find a better solution instead of providing this function func (t *Named) AddMethod(m *Func) { if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { t.methods = append(t.methods, m) } } // Implementations for Type methods. func (t *Basic) Underlying() Type { return t } func (t *Array) Underlying() Type { return t } func (t *Slice) Underlying() Type { return t } func (t *Struct) Underlying() Type { return t } func (t *Pointer) Underlying() Type { return t } func (t *Tuple) Underlying() Type { return t } func (t *Signature) Underlying() Type { return t } func (t *Interface) Underlying() Type { return t } func (t *Map) Underlying() Type { return t } func (t *Chan) Underlying() Type { return t } func (t *Named) Underlying() Type { return t.underlying } func (t *Basic) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Array) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Slice) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Struct) MethodSet() *MethodSet { return t.mset.of(t) } func (t *Pointer) MethodSet() *MethodSet { if named, _ := t.base.(*Named); named != nil { // Avoid recomputing mset(*T) for each distinct Pointer // instance whose underlying type is a named type. return named.pmset.of(t) } return t.mset.of(t) } func (t *Tuple) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Signature) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Interface) MethodSet() *MethodSet { return t.mset.of(t) } func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Chan) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Named) MethodSet() *MethodSet { return t.mset.of(t) } func (t *Basic) String() string { return TypeString(nil, t) } func (t *Array) String() string { return TypeString(nil, t) } func (t *Slice) String() string { return TypeString(nil, t) } func (t *Struct) String() string { return TypeString(nil, t) } func (t *Pointer) String() string { return TypeString(nil, t) } func (t *Tuple) String() string { return TypeString(nil, t) } func (t *Signature) String() string { return TypeString(nil, t) } func (t *Interface) String() string { return TypeString(nil, t) } func (t *Map) String() string { return TypeString(nil, t) } func (t *Chan) String() string { return TypeString(nil, t) } func (t *Named) String() string { return TypeString(nil, t) } ./go/types/lookup.go0000644000014500017510000002450612246613010014113 0ustar michaelstaff// 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. // This file implements various field and method lookup functions. package types // TODO(gri) The named type consolidation and seen maps below must be // indexed by unique keys for a given type. Verify that named // types always have only one representation (even when imported // indirectly via different packages.) // LookupFieldOrMethod looks up a field or method with given package and name // in T and returns the corresponding *Var or *Func, an index sequence, and a // bool indicating if there were any pointer indirections on the path to the // field or method. // // The last index entry is the field or method index in the (possibly embedded) // type where the entry was found, either: // // 1) the list of declared methods of a named type; or // 2) the list of all methods (method set) of an interface type; or // 3) the list of fields of a struct type. // // The earlier index entries are the indices of the embedded fields traversed // to get to the found entry, starting at depth 0. // // If no entry is found, a nil object is returned. In this case, the returned // index sequence points to an ambiguous entry if it exists, or it is nil. // func LookupFieldOrMethod(T Type, pkg *Package, name string) (obj Object, index []int, indirect bool) { obj, index, indirect = lookupFieldOrMethod(T, pkg, name) if obj != nil { return } // TODO(gri) The code below is not needed if we are looking for methods only, // and it can be done always if we look for fields only. Consider // providing LookupField and LookupMethod as well. // If we didn't find anything, we still might have a field p.x as in: // // type S struct{ x int } // func (*S) m() {} // type P *S // var p P // // which requires that we start the search with the underlying type // of P (i.e., *S). We cannot do this always because we might find // methods that don't exist for P but for S (e.g., m). Thus, if the // result is a method we need to discard it. // // TODO(gri) WTF? There isn't a more direct way? Perhaps we should // outlaw named types to pointer types - they are almost // never what one wants, anyway. if t, _ := T.(*Named); t != nil { u := t.underlying if _, ok := u.(*Pointer); ok { // typ is a named type with an underlying type of the form *T, // start the search with the underlying type *T if obj2, index2, indirect2 := lookupFieldOrMethod(u, pkg, name); obj2 != nil { // only if the result is a field can we keep it if _, ok := obj2.(*Var); ok { return obj2, index2, indirect2 } } } } return } func lookupFieldOrMethod(T Type, pkg *Package, name string) (obj Object, index []int, indirect bool) { // WARNING: The code in this function is extremely subtle - do not modify casually! // This function and NewMethodSet should be kept in sync. if name == "_" { return // blank fields/methods are never found } typ, isPtr := deref(T) named, _ := typ.(*Named) // *typ where typ is an interface has no methods. if isPtr { utyp := typ if named != nil { utyp = named.underlying } if _, ok := utyp.(*Interface); ok { return } } // Start with typ as single entry at shallowest depth. // If typ is not a named type, insert a nil type instead. current := []embeddedType{{named, nil, isPtr, false}} // named types that we have seen already, allocated lazily var seen map[*Named]bool // search current depth for len(current) > 0 { var next []embeddedType // embedded types found at current depth // look for (pkg, name) in all types at current depth for _, e := range current { // The very first time only, e.typ may be nil. // In this case, we don't have a named type and // we simply continue with the underlying type. if e.typ != nil { if seen[e.typ] { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth // were consolidated before). The type at that depth shadows // this same type at the current depth, so we can ignore // this one. continue } if seen == nil { seen = make(map[*Named]bool) } seen[e.typ] = true // look for a matching attached method if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil { // potential match assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { obj = nil // collision return } obj = m indirect = e.indirect continue // we can't have a matching field or interface method } // continue with underlying type typ = e.typ.underlying } switch t := typ.(type) { case *Struct: // look for a matching field and collect embedded types for i, f := range t.fields { if f.sameId(pkg, name) { assert(f.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { obj = nil // collision return } obj = f indirect = e.indirect continue // we can't have a matching interface method } // Collect embedded struct fields for searching the next // lower depth, but only if we have not seen a match yet // (if we have a match it is either the desired field or // we have a name collision on the same depth; in either // case we don't need to look further). // Embedded fields are always of the form T or *T where // T is a named type. If e.typ appeared multiple times at // this depth, f.typ appears multiple times at the next // depth. if obj == nil && f.anonymous { // Ignore embedded basic types - only user-defined // named types can have methods or struct fields. typ, isPtr := deref(f.typ) if t, _ := typ.(*Named); t != nil { next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) } } } case *Interface: // look for a matching method // TODO(gri) t.allMethods is sorted - use binary search if i, m := lookupMethod(t.allMethods, pkg, name); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { obj = nil // collision return } obj = m indirect = e.indirect } } } if obj != nil { return // found a match } current = consolidateMultiples(next) } index = nil indirect = false return // not found } // embeddedType represents an embedded named type type embeddedType struct { typ *Named // nil means use the outer typ variable instead index []int // embedded field indices, starting with index at depth 0 indirect bool // if set, there was a pointer indirection on the path to this field multiples bool // if set, typ appears multiple times at this depth } // consolidateMultiples collects multiple list entries with the same type // into a single entry marked as containing multiples. The result is the // consolidated list. func consolidateMultiples(list []embeddedType) []embeddedType { if len(list) <= 1 { return list // at most one entry - nothing to do } n := 0 // number of entries w/ unique type prev := make(map[*Named]int) // index at which type was previously seen for _, e := range list { if i, found := prev[e.typ]; found { list[i].multiples = true // ignore this entry } else { prev[e.typ] = n list[n] = e n++ } } return list[:n] } // MissingMethod returns (nil, false) if V implements T, otherwise it // returns a missing method required by T and whether it is missing or // just has the wrong type. // // For non-interface types V, or if static is set, V implements T if all // methods of T are present in V. Otherwise (V is an interface and static // is not set), MissingMethod only checks that methods of T which are also // present in V have matching types (e.g., for a type assertion x.(T) where // x is of interface type typ). // func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { // fast path for common case if T.NumMethods() == 0 { return } // TODO(gri) Consider using method sets here. Might be more efficient. if ityp, _ := V.Underlying().(*Interface); ityp != nil { // TODO(gri) allMethods is sorted - can do this more efficiently for _, m := range T.allMethods { _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name) switch { case obj == nil: if static { return m, false } case !IsIdentical(obj.Type(), m.typ): return m, true } } return } // A concrete type implements T if it implements all methods of T. for _, m := range T.allMethods { obj, _, indirect := lookupFieldOrMethod(V, m.pkg, m.name) if obj == nil { return m, false } f, _ := obj.(*Func) if f == nil { return m, false } // verify that f is in the method set of typ if !indirect && ptrRecv(f) { return m, false } if !IsIdentical(obj.Type(), m.typ) { return m, true } } return } // deref dereferences typ if it is a *Pointer and returns its base and true. // Otherwise it returns (typ, false). func deref(typ Type) (Type, bool) { if p, _ := typ.(*Pointer); p != nil { return p.base, true } return typ, false } // derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a // (named or unnamed) struct and returns its base. Otherwise it returns typ. func derefStructPtr(typ Type) Type { if p, _ := typ.Underlying().(*Pointer); p != nil { if _, ok := p.base.Underlying().(*Struct); ok { return p.base } } return typ } // concat returns the result of concatenating list and i. // The result does not share its underlying array with list. func concat(list []int, i int) []int { var t []int t = append(t, list...) return append(t, i) } // fieldIndex returns the index for the field with matching package and name, or a value < 0. func fieldIndex(fields []*Var, pkg *Package, name string) int { if name != "_" { for i, f := range fields { if f.sameId(pkg, name) { return i } } } return -1 } // lookupMethod returns the index of and method with matching package and name, or (-1, nil). func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { if name != "_" { for i, m := range methods { if m.sameId(pkg, name) { return i, m } } } return -1, nil } ./go/types/builtins.go0000644000014500017510000003676012246613010014440 0ustar michaelstaff// Copyright 2012 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 implements typechecking of builtin function calls. package types import ( "go/ast" "go/token" "code.google.com/p/go.tools/go/exact" ) // builtin type-checks a call to the built-in specified by id and // returns true if the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. // func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] if call.Ellipsis.IsValid() && id != _Append { check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name) check.use(call.Args) return } // determine actual arguments var arg getter nargs := len(call.Args) switch id { default: // make argument getter arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false) // evaluate first argument, if present if nargs > 0 { arg(x, 0) if x.mode == invalid { return } } case _Make, _New, _Offsetof, _Trace: // arguments require special handling } // check argument count { msg := "" if nargs < bin.nargs { msg = "not enough" } else if !bin.variadic && nargs > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs) return } } switch id { case _Append: // append(s S, x ...T) S, where T is the element type of S // spec: "The variadic function append appends zero or more values x to s of type // S, which must be a slice type, and returns the resulting slice, also of type S. // The values x are passed to a parameter of type ...T where T is the element type // of S and the respective parameter passing rules apply." S := x.typ var T Type if s, _ := S.Underlying().(*Slice); s != nil { T = s.elem } else { check.invalidArg(x.pos(), "%s is not a slice", x) return } // remember arguments that have been evaluated already alist := []operand{*x} // spec: "As a special case, append also accepts a first argument assignable // to type []byte with a second argument of string type followed by ... . // This form appends the bytes of the string. if nargs == 2 && call.Ellipsis.IsValid() && x.isAssignableTo(check.conf, NewSlice(Typ[Byte])) { arg(x, 1) if x.mode == invalid { return } if isString(x.typ) { if check.Types != nil { sig := makeSig(S, S, NewSlice(Typ[Byte])) sig.isVariadic = true check.recordBuiltinType(call.Fun, sig) } x.mode = value x.typ = S break } alist = append(alist, *x) // fallthrough } // check general case by creating custom signature sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature sig.isVariadic = true check.arguments(x, call, sig, func(x *operand, i int) { // only evaluate arguments that have not been evaluated before if i < len(alist) { *x = alist[i] return } arg(x, i) }, nargs) // ok to continue even if check.arguments reported errors x.mode = value x.typ = S if check.Types != nil { check.recordBuiltinType(call.Fun, sig) } case _Cap, _Len: // cap(x) // len(x) mode := invalid var typ Type var val exact.Value switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant { mode = constant val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.containsCallsOrReceives(x.expr) { mode = constant val = exact.MakeInt64(t.len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) return } x.mode = mode x.typ = Typ[Int] x.val = val if check.Types != nil && mode != constant { check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) } case _Close: // close(c) c, _ := x.typ.Underlying().(*Chan) if c == nil { check.invalidArg(x.pos(), "%s is not a channel", x) return } if c.dir&ast.SEND == 0 { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, c)) } case _Complex: // complex(x, y realT) complexT if !check.complexArg(x) { return } var y operand arg(&y, 1) if y.mode == invalid { return } if !check.complexArg(&y) { return } check.convertUntyped(x, y.typ) if x.mode == invalid { return } check.convertUntyped(&y, x.typ) if y.mode == invalid { return } if !IsIdentical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) return } if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } realT := x.typ complexT := Typ[Invalid] switch realT.Underlying().(*Basic).kind { case Float32: complexT = Typ[Complex64] case Float64: complexT = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: if x.mode == constant { realT = defaultType(realT).(*Basic) complexT = Typ[UntypedComplex] } else { // untyped but not constant; probably because one // operand is a non-constant shift of untyped lhs realT = Typ[Float64] complexT = Typ[Complex128] } default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") return } x.typ = complexT if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT)) } if x.mode != constant { // The arguments have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. // (If the result is constant, the arguments are never // materialized and there is nothing to do.) check.updateExprType(x.expr, realT, true) check.updateExprType(y.expr, realT, true) } case _Copy: // copy(x, y []T) int var dst Type if t, _ := x.typ.Underlying().(*Slice); t != nil { dst = t.elem } var y operand arg(&y, 1) if y.mode == invalid { return } var src Type switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = Typ[Byte] } case *Slice: src = t.elem } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) return } if !IsIdentical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) return } x.mode = value x.typ = Typ[Int] if check.Types != nil { S := NewSlice(dst) check.recordBuiltinType(call.Fun, makeSig(x.typ, S, S)) } case _Delete: // delete(m, k) m, _ := x.typ.Underlying().(*Map) if m == nil { check.invalidArg(x.pos(), "%s is not a map", x) return } arg(x, 1) // k if x.mode == invalid { return } if !x.isAssignableTo(check.conf, m.key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) } case _Imag, _Real: // imag(complexT) realT // real(complexT) realT if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) return } if x.mode == constant { if id == _Real { x.val = exact.Real(x.val) } else { x.val = exact.Imag(x.val) } } else { x.mode = value } var k BasicKind switch x.typ.Underlying().(*Basic).kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ)) } x.typ = Typ[k] case _Make: // make(T, n) // make(T, n, m) // (no argument evaluated yet) arg0 := call.Args[0] T := check.typ(arg0, nil, false) if T == Typ[Invalid] { return } var min int // minimum number of arguments switch T.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) return } if nargs < min || min+1 < nargs { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs) return } var sizes []int64 // constant integer arguments, if any for _, arg := range call.Args[1:] { if s, ok := check.index(arg, -1); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(call.Args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = variable x.typ = T if check.Types != nil { params := [...]Type{T, Typ[Int], Typ[Int]} check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...)) } case _New: // new(T) // (no argument evaluated yet) T := check.typ(call.Args[0], nil, false) if T == Typ[Invalid] { return } x.mode = variable x.typ = &Pointer{base: T} if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ, T)) } case _Panic: // panic(x) T := new(Interface) if !check.assignment(x, T) { assert(x.mode == invalid) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, T)) } case _Print, _Println: // print(x, y, ...) // println(x, y, ...) var params []Type if nargs > 0 { params = make([]Type, nargs) for i := 0; i < nargs; i++ { if i > 0 { arg(x, i) // first argument already evaluated } if !check.assignment(x, nil) { assert(x.mode == invalid) return } params[i] = x.typ } } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, params...)) } case _Recover: // recover() interface{} x.mode = value x.typ = new(Interface) if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ)) } case _Alignof: // unsafe.Alignof(x T) uintptr if !check.assignment(x, nil) { assert(x.mode == invalid) return } x.mode = constant x.val = exact.MakeInt64(check.conf.alignof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector // (no argument evaluated yet) arg0 := call.Args[0] selx, _ := unparen(arg0).(*ast.SelectorExpr) if selx == nil { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) check.rawExpr(x, arg0, nil) // evaluate to avoid spurious "declared but not used" errors return } check.expr(x, selx.X) if x.mode == invalid { return } base := derefStructPtr(x.typ) sel := selx.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, check.pkg, sel) switch obj.(type) { case nil: check.invalidArg(x.pos(), "%s has no single field %s", base, sel) return case *Func: check.invalidArg(arg0.Pos(), "%s is a method value", arg0) return } if indirect { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) return } // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr if !check.assignment(x, nil) { assert(x.mode == invalid) return } x.mode = constant x.val = exact.MakeInt64(check.conf.sizeof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) return } if x.val.Kind() != exact.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) return } if !exact.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } // result is constant - no need to record signature case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. // (no argument evaluated yet) if nargs == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue break } var t operand x1 := x for _, arg := range call.Args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } // trace is only available in test mode - no need to record signature default: unreachable() } return true } // makeSig makes a signature for the given argument and result types. // Default types are used for untyped arguments, and res may be nil. func makeSig(res Type, args ...Type) *Signature { list := make([]*Var, len(args)) for i, param := range args { list[i] = NewVar(token.NoPos, nil, "", defaultType(param)) } params := NewTuple(list...) var result *Tuple if res != nil { assert(!isUntyped(res)) result = NewTuple(NewVar(token.NoPos, nil, "", res)) } return &Signature{params: params, results: result} } // implicitArrayDeref returns A if typ is of the form *A and A is an array; // otherwise it returns typ. // func implicitArrayDeref(typ Type) Type { if p, ok := typ.(*Pointer); ok { if a, ok := p.base.Underlying().(*Array); ok { return a } } return typ } // containsCallsOrReceives reports if x contains function calls or channel receives. // Expects that x was type-checked already. // func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) { ast.Inspect(x, func(x ast.Node) bool { switch x := x.(type) { case *ast.CallExpr: // calls and conversions look the same if !check.conversions[x] { found = true } case *ast.UnaryExpr: if x.Op == token.ARROW { found = true } } return !found // no need to continue if found }) return } // unparen removes any parentheses surrounding an expression and returns // the naked expression. // func unparen(x ast.Expr) ast.Expr { if p, ok := x.(*ast.ParenExpr); ok { return unparen(p.X) } return x } func (check *checker) complexArg(x *operand) bool { t, _ := x.typ.Underlying().(*Basic) if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) { return true } check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x) return false } ./go/types/exprstring.go0000644000014500017510000001052212246613010015000 0ustar michaelstaff// 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. // This file implements printing of expressions. package types import ( "bytes" "go/ast" ) // ExprString returns the (possibly simplified) string representation for x. func ExprString(x ast.Expr) string { var buf bytes.Buffer WriteExpr(&buf, x) return buf.String() } // WriteExpr writes the (possibly simplified) string representation for x to buf. func WriteExpr(buf *bytes.Buffer, x ast.Expr) { // The AST preserves source-level parentheses so there is // no need to introduce them here to correct for different // operator precedences. (This assumes that the AST was // generated by a Go parser.) switch x := x.(type) { default: buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr case *ast.Ident: buf.WriteString(x.Name) case *ast.Ellipsis: buf.WriteString("...") if x.Elt != nil { WriteExpr(buf, x.Elt) } case *ast.BasicLit: buf.WriteString(x.Value) case *ast.FuncLit: buf.WriteByte('(') WriteExpr(buf, x.Type) buf.WriteString(" literal)") // simplified case *ast.CompositeLit: buf.WriteByte('(') WriteExpr(buf, x.Type) buf.WriteString(" literal)") // simplified case *ast.ParenExpr: buf.WriteByte('(') WriteExpr(buf, x.X) buf.WriteByte(')') case *ast.SelectorExpr: WriteExpr(buf, x.X) buf.WriteByte('.') buf.WriteString(x.Sel.Name) case *ast.IndexExpr: WriteExpr(buf, x.X) buf.WriteByte('[') WriteExpr(buf, x.Index) buf.WriteByte(']') case *ast.SliceExpr: WriteExpr(buf, x.X) buf.WriteByte('[') if x.Low != nil { WriteExpr(buf, x.Low) } buf.WriteByte(':') if x.High != nil { WriteExpr(buf, x.High) } if x.Slice3 { buf.WriteByte(':') if x.Max != nil { WriteExpr(buf, x.Max) } } buf.WriteByte(']') case *ast.TypeAssertExpr: WriteExpr(buf, x.X) buf.WriteString(".(") WriteExpr(buf, x.Type) buf.WriteByte(')') case *ast.CallExpr: WriteExpr(buf, x.Fun) buf.WriteByte('(') for i, arg := range x.Args { if i > 0 { buf.WriteString(", ") } WriteExpr(buf, arg) } if x.Ellipsis.IsValid() { buf.WriteString("...") } buf.WriteByte(')') case *ast.StarExpr: buf.WriteByte('*') WriteExpr(buf, x.X) case *ast.UnaryExpr: buf.WriteString(x.Op.String()) WriteExpr(buf, x.X) case *ast.BinaryExpr: WriteExpr(buf, x.X) buf.WriteByte(' ') buf.WriteString(x.Op.String()) buf.WriteByte(' ') WriteExpr(buf, x.Y) case *ast.ArrayType: buf.WriteByte('[') if x.Len != nil { WriteExpr(buf, x.Len) } buf.WriteByte(']') WriteExpr(buf, x.Elt) case *ast.StructType: buf.WriteString("struct{") writeFieldList(buf, x.Fields, "; ", false) buf.WriteByte('}') case *ast.FuncType: buf.WriteString("func") writeSigExpr(buf, x) case *ast.InterfaceType: buf.WriteString("interface{") writeFieldList(buf, x.Methods, "; ", true) buf.WriteByte('}') case *ast.MapType: buf.WriteString("map[") WriteExpr(buf, x.Key) buf.WriteByte(']') WriteExpr(buf, x.Value) case *ast.ChanType: var s string switch x.Dir { case ast.SEND: s = "chan<- " case ast.RECV: s = "<-chan " default: s = "chan " } buf.WriteString(s) WriteExpr(buf, x.Value) } } func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) { buf.WriteByte('(') writeFieldList(buf, sig.Params, ", ", false) buf.WriteByte(')') res := sig.Results n := res.NumFields() if n == 0 { // no result return } buf.WriteByte(' ') if n == 1 && len(res.List[0].Names) == 0 { // single unnamed result WriteExpr(buf, res.List[0].Type) return } // multiple or named result(s) buf.WriteByte('(') writeFieldList(buf, res, ", ", false) buf.WriteByte(')') } func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) { for i, f := range fields.List { if i > 0 { buf.WriteString(sep) } // field list names for i, name := range f.Names { if i > 0 { buf.WriteString(", ") } buf.WriteString(name.Name) } // types of interface methods consist of signatures only if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { writeSigExpr(buf, sig) continue } // named fields are separated with a blank from the field type if len(f.Names) > 0 { buf.WriteByte(' ') } WriteExpr(buf, f.Type) // ignore tag } } ./go/types/typestring_test.go0000644000014500017510000000731512246613010016050 0ustar michaelstaff// Copyright 2012 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 types_test import ( "go/ast" "go/parser" "go/token" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) const filename = "" func makePkg(t *testing.T, src string) (*Package, error) { fset := token.NewFileSet() file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) if err != nil { return nil, err } // use the package name as package path return Check(file.Name.Name, fset, []*ast.File{file}) } type testEntry struct { src, str string } // dup returns a testEntry where both src and str are the same. func dup(s string) testEntry { return testEntry{s, s} } // types that don't depend on any other type declarations var independentTestTypes = []testEntry{ // basic types dup("int"), dup("float32"), dup("string"), // arrays dup("[10]int"), // slices dup("[]int"), dup("[][]int"), // structs dup("struct{}"), dup("struct{x int}"), {`struct { x, y int z float32 "foo" }`, `struct{x int; y int; z float32 "foo"}`}, {`struct { string elems []complex128 }`, `struct{string; elems []complex128}`}, // pointers dup("*int"), dup("***struct{}"), dup("*struct{a int; b float32}"), // functions dup("func()"), dup("func(x int)"), {"func(x, y int)", "func(x int, y int)"}, {"func(x, y int, z string)", "func(x int, y int, z string)"}, dup("func(int)"), {"func(int, string, byte)", "func(int, string, byte)"}, dup("func() int"), {"func() (string)", "func() string"}, dup("func() (u int)"), {"func() (u, v int, w string)", "func() (u int, v int, w string)"}, dup("func(int) string"), dup("func(x int) string"), dup("func(x int) (u string)"), {"func(x, y int) (u string)", "func(x int, y int) (u string)"}, dup("func(...int) string"), dup("func(x ...int) string"), dup("func(x ...int) (u string)"), {"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"}, // interfaces dup("interface{}"), dup("interface{m()}"), dup(`interface{String() string; m(int) float32}`), // maps dup("map[string]int"), {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"}, // channels dup("chan<- chan int"), dup("chan<- <-chan int"), dup("<-chan <-chan int"), dup("chan (<-chan int)"), dup("chan<- func()"), dup("<-chan []func() int"), } // types that depend on other type declarations (src in TestTypes) var dependentTestTypes = []testEntry{ // interfaces dup(`interface{io.Reader; io.Writer}`), dup(`interface{m() int; io.Writer}`), {`interface{m() interface{T}}`, `interface{m() interface{p.T}}`}, } func TestTypeString(t *testing.T) { var tests []testEntry tests = append(tests, independentTestTypes...) tests = append(tests, dependentTestTypes...) for _, test := range tests { src := `package p; import "io"; type _ io.Writer; type T ` + test.src pkg, err := makePkg(t, src) if err != nil { t.Errorf("%s: %s", src, err) continue } typ := pkg.Scope().Lookup("T").Type().Underlying() if got := typ.String(); got != test.str { t.Errorf("%s: got %s, want %s", test.src, got, test.str) } } } func TestQualifiedTypeString(t *testing.T) { p, _ := pkgFor("p.go", "package p; type T int", nil) q, _ := pkgFor("q.go", "package q", nil) pT := p.Scope().Lookup("T").Type() for _, test := range []struct { typ Type this *Package want string }{ {pT, nil, "p.T"}, {pT, p, "T"}, {pT, q, "p.T"}, {NewPointer(pT), p, "*T"}, {NewPointer(pT), q, "*p.T"}, } { if got := TypeString(test.this, test.typ); got != test.want { t.Errorf("TypeString(%s, %s) = %s, want %s", test.this, test.typ, got, test.want) } } } ./go/types/objset.go0000644000014500017510000000163712246613010014070 0ustar michaelstaff// 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. // This file implements objsets. // // An objset is similar to a Scope but objset elements // are identified by their unique id, instead of their // object name. package types // An objset is a set of objects identified by their unique id. // The zero value for objset is a ready-to-use empty objset. type objset map[string]Object // initialized lazily // insert attempts to insert an object obj into objset s. // If s already contains an alternative object alt with // the same name, insert leaves s unchanged and returns alt. // Otherwise it inserts obj and returns nil. func (s *objset) insert(obj Object) Object { id := obj.Id() if alt := (*s)[id]; alt != nil { return alt } if *s == nil { *s = make(map[string]Object) } (*s)[id] = obj return nil } ./go/types/api_test.go0000644000014500017510000001746412246613010014417 0ustar michaelstaff// 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. // TODO(gri) This file needs to be expanded significantly. package types_test import ( "go/ast" "go/parser" "go/token" "strings" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) func pkgFor(path, source string, info *Info) (*Package, error) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, path, source, 0) if err != nil { return nil, err } var conf Config return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) } func mustTypecheck(t *testing.T, path, source string, info *Info) string { pkg, err := pkgFor(path, source, info) if err != nil { name := path if pkg != nil { name = "package " + pkg.Name() } t.Fatalf("%s: didn't type-check (%s)", name, err) } return pkg.Name() } func TestCommaOkTypes(t *testing.T) { var tests = []struct { src string expr string // comma-ok expression string typ string // typestring of comma-ok value }{ {`package p0; var x interface{}; var _, _ = x.(int)`, `x.(int)`, `(int, bool)`, }, {`package p1; var x interface{}; func _() { _, _ = x.(int) }`, `x.(int)`, `(int, bool)`, }, {`package p2; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`, `m["foo"]`, `(complex128, p2.mybool)`, }, {`package p3; var c chan string; var _, _ = <-c`, `<-c`, `(string, bool)`, }, {`package issue6796_a; var x interface{}; var _, _ = (x.(int))`, `x.(int)`, `(int, bool)`, }, {`package issue6796_b; var c chan string; var _, _ = (<-c)`, `(<-c)`, `(string, bool)`, }, {`package issue6796_c; var c chan string; var _, _ = (<-c)`, `<-c`, `(string, bool)`, }, {`package issue6796_d; var c chan string; var _, _ = ((<-c))`, `(<-c)`, `(string, bool)`, }, {`package issue6796_e; func f(c chan string) { _, _ = ((<-c)) }`, `(<-c)`, `(string, bool)`, }, } for _, test := range tests { info := Info{Types: make(map[ast.Expr]Type)} name := mustTypecheck(t, "CommaOkTypes", test.src, &info) // look for comma-ok expression type var typ Type for e, t := range info.Types { if ExprString(e) == test.expr { typ = t break } } if typ == nil { t.Errorf("package %s: no type found for %s", name, test.expr) continue } // check that type is correct if got := typ.String(); got != test.typ { t.Errorf("package %s: got %s; want %s", name, got, test.typ) } } } func TestScopesInfo(t *testing.T) { var tests = []struct { src string scopes []string // list of scope descriptors of the form kind:varlist }{ {`package p0`, []string{ "file:", }}, {`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{ "file:fmt m", }}, {`package p2; func _() {}`, []string{ "file:", "func:", }}, {`package p3; func _(x, y int) {}`, []string{ "file:", "func:x y", }}, {`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{ "file:", "func:x y z", // redeclaration of x }}, {`package p5; func _(x, y int) (u, _ int) { return }`, []string{ "file:", "func:u x y", }}, {`package p6; func _() { { var x int; _ = x } }`, []string{ "file:", "func:", "block:x", }}, {`package p7; func _() { if true {} }`, []string{ "file:", "func:", "if:", "block:", }}, {`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{ "file:", "func:", "if:x", "block:y", }}, {`package p9; func _() { switch x := 0; x {} }`, []string{ "file:", "func:", "switch:x", }}, {`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{ "file:", "func:", "switch:x", "case:y", "case:", }}, {`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{ "file:", "func:t", "type switch:", }}, {`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{ "file:", "func:t", "type switch:t", }}, {`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{ "file:", "func:t", "type switch:", "case:x", // x implicitly declared }}, {`package p14; func _() { select{} }`, []string{ "file:", "func:", }}, {`package p15; func _(c chan int) { select{ case <-c: } }`, []string{ "file:", "func:c", "comm:", }}, {`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{ "file:", "func:c", "comm:i x", }}, {`package p17; func _() { for{} }`, []string{ "file:", "func:", "for:", "block:", }}, {`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{ "file:", "func:n", "for:i", "block:", }}, {`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{ "file:", "func:a", "range:i", "block:", }}, {`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{ "file:", "func:a", "range:i x", "block:", }}, } for _, test := range tests { info := Info{Scopes: make(map[ast.Node]*Scope)} name := mustTypecheck(t, "ScopesInfo", test.src, &info) // number of scopes must match if len(info.Scopes) != len(test.scopes) { t.Errorf("package %s: got %d scopes; want %d", name, len(info.Scopes), len(test.scopes)) } // scope descriptions must match for node, scope := range info.Scopes { kind := "" switch node.(type) { case *ast.File: kind = "file" case *ast.FuncType: kind = "func" case *ast.BlockStmt: kind = "block" case *ast.IfStmt: kind = "if" case *ast.SwitchStmt: kind = "switch" case *ast.TypeSwitchStmt: kind = "type switch" case *ast.CaseClause: kind = "case" case *ast.CommClause: kind = "comm" case *ast.ForStmt: kind = "for" case *ast.RangeStmt: kind = "range" } // look for matching scope description desc := kind + ":" + strings.Join(scope.Names(), " ") found := false for _, d := range test.scopes { if desc == d { found = true break } } if !found { t.Errorf("package %s: no matching scope found for %s", name, desc) } } } } func TestInitOrder(t *testing.T) { var tests = []struct { src string inits []string }{ {`package p0; var (x = 1; y = x)`, []string{ "x = 1", "y = x", }}, {`package p1; var (a = 1; b = 2; c = 3)`, []string{ "a = 1", "b = 2", "c = 3", }}, {`package p2; var (a, b, c = 1, 2, 3)`, []string{ "a = 1", "b = 2", "c = 3", }}, {`package p3; var _ = f(); func f() int { return 1 }`, []string{ "_ = f()", // blank var }}, {`package p4; var (a = 0; x = y; y = z; z = 0)`, []string{ "a = 0", "z = 0", "y = z", "x = y", }}, {`package p5; var (a, _ = m[0]; m map[int]string)`, []string{ "a, _ = m[0]", // blank var }}, {`package p6; var a, b = f(); func f() (_, _ int) { return z, z }; var z = 0`, []string{ "z = 0", "a, b = f()", }}, {`package p7; var (a = func() int { return b }(); b = 1)`, []string{ "b = 1", "a = (func() int literal)()", }}, {`package p8; var (a, b = func() (_, _ int) { return c, c }(); c = 1)`, []string{ "c = 1", "a, b = (func() (_, _ int) literal)()", }}, {`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{ "y = 1", "x = T.m", }}, } for _, test := range tests { info := Info{} name := mustTypecheck(t, "InitOrder", test.src, &info) // number of initializers must match if len(info.InitOrder) != len(test.inits) { t.Errorf("package %s: got %d initializers; want %d", name, len(info.InitOrder), len(test.inits)) continue } // initializers must match for i, want := range test.inits { got := info.InitOrder[i].String() if got != want { t.Errorf("package %s, init %d: got %s; want %s", name, i, got, want) continue } } } } ./go/types/token_test.go0000644000014500017510000000232412246613010014753 0ustar michaelstaff// 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. // This file checks invariants of token.Token ordering that we rely on // since package go/token doesn't provide any guarantees at the moment. package types import ( "go/token" "testing" ) var assignOps = map[token.Token]token.Token{ token.ADD_ASSIGN: token.ADD, token.SUB_ASSIGN: token.SUB, token.MUL_ASSIGN: token.MUL, token.QUO_ASSIGN: token.QUO, token.REM_ASSIGN: token.REM, token.AND_ASSIGN: token.AND, token.OR_ASSIGN: token.OR, token.XOR_ASSIGN: token.XOR, token.SHL_ASSIGN: token.SHL, token.SHR_ASSIGN: token.SHR, token.AND_NOT_ASSIGN: token.AND_NOT, } func TestZeroTok(t *testing.T) { // zero value for token.Token must be token.ILLEGAL var zero token.Token if token.ILLEGAL != zero { t.Errorf("%s == %d; want 0", token.ILLEGAL, zero) } } func TestAssignOp(t *testing.T) { // there are fewer than 256 tokens for i := 0; i < 256; i++ { tok := token.Token(i) got := assignOp(tok) want := assignOps[tok] if got != want { t.Errorf("for assignOp(%s): got %s; want %s", tok, got, want) } } } ./go/types/objects.go0000644000014500017510000002110212246613010014220 0ustar michaelstaff// 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 types import ( "bytes" "fmt" "go/ast" "go/token" "code.google.com/p/go.tools/go/exact" ) // TODO(gri) Document factory, accessor methods, and fields. General clean-up. // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. // All objects implement the Object interface. // type Object interface { Parent() *Scope // scope in which this object is declared Pos() token.Pos // position of object identifier in declaration Pkg() *Package // nil for objects in the Universe scope and labels Name() string // package local object name Type() Type // object type IsExported() bool // reports whether the name starts with a capital letter Id() string // object id (see Id below) // String returns a human-readable string of the object. String() string // isUsed reports whether the object was marked as 'used'. isUsed() bool // setParent sets the parent scope of the object. setParent(*Scope) // sameId reports whether obj.Id() and Id(pkg, name) are the same. sameId(pkg *Package, name string) bool } // Id returns name if it is exported, otherwise it // returns the name qualified with the package path. func Id(pkg *Package, name string) string { if ast.IsExported(name) { return name } // unexported names need the package path for differentiation path := "" // TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition? // if pkg == nil { // panic("nil package in lookup of unexported name") // } if pkg != nil { path = pkg.path if path == "" { path = "?" } } return path + "." + name } // An object implements the common parts of an Object. type object struct { parent *Scope pos token.Pos pkg *Package name string typ Type used bool } func (obj *object) Parent() *Scope { return obj.parent } func (obj *object) Pos() token.Pos { return obj.pos } func (obj *object) Pkg() *Package { return obj.pkg } func (obj *object) Name() string { return obj.name } func (obj *object) Type() Type { return obj.typ } func (obj *object) IsExported() bool { return ast.IsExported(obj.name) } func (obj *object) Id() string { return Id(obj.pkg, obj.name) } func (obj *object) String() string { panic("abstract") } func (obj *object) isUsed() bool { return obj.used } func (obj *object) setParent(parent *Scope) { obj.parent = parent } func (obj *object) sameId(pkg *Package, name string) bool { // spec: // "Two identifiers are different if they are spelled differently, // or if they appear in different packages and are not exported. // Otherwise, they are the same." if name != obj.name { return false } // obj.Name == name if obj.IsExported() { return true } // not exported, so packages must be the same (pkg == nil for // fields in Universe scope; this can only happen for types // introduced via Eval) if pkg == nil || obj.pkg == nil { return pkg == obj.pkg } // pkg != nil && obj.pkg != nil return pkg.path == obj.pkg.path } // A PkgName represents an imported Go package. type PkgName struct { object } func NewPkgName(pos token.Pos, pkg *Package, name string) *PkgName { return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], false}} } // A Const represents a declared constant. type Const struct { object val exact.Value visited bool // for initialization cycle detection } func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const { return &Const{object: object{nil, pos, pkg, name, typ, false}, val: val} } func (obj *Const) Val() exact.Value { return obj.val } // A TypeName represents a declared type. type TypeName struct { object } func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { return &TypeName{object{nil, pos, pkg, name, typ, false}} } // A Variable represents a declared variable (including function parameters and results, and struct fields). type Var struct { object anonymous bool // if set, the variable is an anonymous struct field, and name is the type name visited bool // for initialization cycle detection isField bool // var is struct field } func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var { return &Var{object: object{nil, pos, pkg, name, typ, false}} } func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { return &Var{object: object{nil, pos, pkg, name, typ, true}} // parameters are always 'used' } func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { return &Var{object: object{nil, pos, pkg, name, typ, false}, anonymous: anonymous, isField: true} } func (obj *Var) Anonymous() bool { return obj.anonymous } func (obj *Var) IsField() bool { return obj.isField } // A Func represents a declared function, concrete method, or abstract // (interface) method. Its Type() is always a *Signature. // An abstract method may belong to many interfaces due to embedding. type Func struct { object } func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { // don't store a nil signature var typ Type if sig != nil { typ = sig } return &Func{object{nil, pos, pkg, name, typ, false}} } // FullName returns the package- or receiver-type-qualified name of // function or method obj. func (obj *Func) FullName() string { var buf bytes.Buffer writeFuncName(&buf, nil, obj) return buf.String() } // A Label represents a declared label. type Label struct { object } func NewLabel(pos token.Pos, name string) *Label { return &Label{object{pos: pos, name: name, typ: Typ[Invalid]}} } // A Builtin represents a built-in function. // Builtins don't have a valid type. type Builtin struct { object id builtinId } func newBuiltin(id builtinId) *Builtin { return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id} } // Nil represents the predeclared value nil. type Nil struct { object } func writeObject(buf *bytes.Buffer, this *Package, obj Object) { typ := obj.Type() switch obj := obj.(type) { case *PkgName: buf.WriteString("package") typ = nil case *Const: buf.WriteString("const") case *TypeName: buf.WriteString("type") typ = typ.Underlying() case *Var: if obj.isField { buf.WriteString("field") } else { buf.WriteString("var") } case *Func: buf.WriteString("func ") writeFuncName(buf, this, obj) if typ != nil { writeSignature(buf, this, typ.(*Signature)) } return case *Label: buf.WriteString("label") typ = nil case *Builtin: buf.WriteString("builtin") typ = nil case *Nil: buf.WriteString("nil") return default: panic(fmt.Sprintf("writeObject(%T)", obj)) } buf.WriteByte(' ') // For package-level objects, package-qualify the name, // except for intra-package references (this != nil). if pkg := obj.Pkg(); pkg != nil && this != pkg && pkg.scope.Lookup(obj.Name()) == obj { buf.WriteString(pkg.path) buf.WriteByte('.') } buf.WriteString(obj.Name()) if typ != nil { buf.WriteByte(' ') WriteType(buf, this, typ) } } // ObjectString returns the string form of obj. // Object and type names are printed package-qualified // only if they do not belong to this package. // func ObjectString(this *Package, obj Object) string { var buf bytes.Buffer writeObject(&buf, this, obj) return buf.String() } func (obj *PkgName) String() string { return ObjectString(nil, obj) } func (obj *Const) String() string { return ObjectString(nil, obj) } func (obj *TypeName) String() string { return ObjectString(nil, obj) } func (obj *Var) String() string { return ObjectString(nil, obj) } func (obj *Func) String() string { return ObjectString(nil, obj) } func (obj *Label) String() string { return ObjectString(nil, obj) } func (obj *Builtin) String() string { return ObjectString(nil, obj) } func (obj *Nil) String() string { return ObjectString(nil, obj) } func writeFuncName(buf *bytes.Buffer, this *Package, f *Func) { if f.typ != nil { sig := f.typ.(*Signature) if recv := sig.Recv(); recv != nil { buf.WriteByte('(') if _, ok := recv.Type().(*Interface); ok { // gcimporter creates abstract methods of // named interfaces using the interface type // (not the named type) as the receiver. // Don't print it in full. buf.WriteString("interface") } else { WriteType(buf, this, recv.Type()) } buf.WriteByte(')') buf.WriteByte('.') } else if f.pkg != nil && f.pkg != this { buf.WriteString(f.pkg.path) buf.WriteByte('.') } } buf.WriteString(f.name) } ./go/types/operand.go0000644000014500017510000001666112246613010014235 0ustar michaelstaff// Copyright 2012 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 defines operands and associated operations. package types import ( "bytes" "go/ast" "go/token" "code.google.com/p/go.tools/go/exact" ) // An operandMode specifies the (addressing) mode of an operand. type operandMode int const ( invalid operandMode = iota // operand is invalid novalue // operand represents no value (result of a function call w/o result) builtin // operand is a built-in function typexpr // operand is a type constant // operand is a constant; the operand's typ is a Basic type variable // operand is an addressable variable mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment) value // operand is a computed value commaok // like value, but operand may be used in a comma,ok expression ) var operandModeString = [...]string{ invalid: "invalid operand", novalue: "no value", builtin: "built-in", typexpr: "type", constant: "constant", variable: "variable", mapindex: "map index expression", value: "value", commaok: "comma, ok expression", } // An operand represents an intermediate value during type checking. // Operands have an (addressing) mode, the expression evaluating to // the operand, the operand's type, a value for constants, and an id // for built-in functions. // The zero value of operand is a ready to use invalid operand. // type operand struct { mode operandMode expr ast.Expr typ Type val exact.Value id builtinId } // pos returns the position of the expression corresponding to x. // If x is invalid the position is token.NoPos. // func (x *operand) pos() token.Pos { // x.expr may not be set if x is invalid if x.expr == nil { return token.NoPos } return x.expr.Pos() } // Operand string formats // (not all "untyped" cases can appear due to the type system, // but they fall out naturally here) // // mode format // // invalid ( ) // novalue ( ) // builtin ( ) // typexpr ( ) // // constant ( ) // constant ( of type ) // constant ( ) // constant ( of type ) // // variable ( ) // variable ( of type ) // // mapindex ( ) // mapindex ( of type ) // // value ( ) // value ( of type ) // // commaok ( ) // commaok ( of type ) // func (x *operand) String() string { var buf bytes.Buffer var expr string if x.expr != nil { expr = ExprString(x.expr) } else { switch x.mode { case builtin: expr = predeclaredFuncs[x.id].name case typexpr: expr = TypeString(nil, x.typ) case constant: expr = x.val.String() } } // ( if expr != "" { buf.WriteString(expr) buf.WriteString(" (") } // hasType := false switch x.mode { case invalid, novalue, builtin, typexpr: // no type default: // has type if isUntyped(x.typ) { buf.WriteString(x.typ.(*Basic).name) buf.WriteByte(' ') break } hasType = true } // buf.WriteString(operandModeString[x.mode]) // if x.mode == constant { if s := x.val.String(); s != expr { buf.WriteByte(' ') buf.WriteString(s) } } // if hasType { if x.typ != Typ[Invalid] { buf.WriteString(" of type ") WriteType(&buf, nil, x.typ) } else { buf.WriteString(" with invalid type") } } // ) if expr != "" { buf.WriteByte(')') } return buf.String() } // setConst sets x to the untyped constant for literal lit. func (x *operand) setConst(tok token.Token, lit string) { val := exact.MakeFromLiteral(lit, tok) if val == nil { // TODO(gri) Should we make it an unknown constant instead? x.mode = invalid return } var kind BasicKind switch tok { case token.INT: kind = UntypedInt case token.FLOAT: kind = UntypedFloat case token.IMAG: kind = UntypedComplex case token.CHAR: kind = UntypedRune case token.STRING: kind = UntypedString } x.mode = constant x.typ = Typ[kind] x.val = val } // isNil reports whether x is the nil value. func (x *operand) isNil() bool { return x.mode == value && x.typ == Typ[UntypedNil] } // TODO(gri) The functions operand.isAssignableTo, checker.convertUntyped, // checker.isRepresentable, and checker.assignment are // overlapping in functionality. Need to simplify and clean up. // isAssignableTo reports whether x is assignable to a variable of type T. func (x *operand) isAssignableTo(conf *Config, T Type) bool { if x.mode == invalid || T == Typ[Invalid] { return true // avoid spurious errors } V := x.typ // x's type is identical to T if IsIdentical(V, T) { return true } Vu := V.Underlying() Tu := T.Underlying() // T is an interface type and x implements T // (Do this check first as it might succeed early.) if Ti, ok := Tu.(*Interface); ok { if m, _ := MissingMethod(x.typ, Ti, true); m == nil { return true } } // x's type V and T have identical underlying types // and at least one of V or T is not a named type if IsIdentical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) { return true } // x is a bidirectional channel value, T is a channel // type, x's type V and T have identical element types, // and at least one of V or T is not a named type if Vc, ok := Vu.(*Chan); ok && Vc.dir == ast.SEND|ast.RECV { if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.elem, Tc.elem) { return !isNamed(V) || !isNamed(T) } } // x is the predeclared identifier nil and T is a pointer, // function, slice, map, channel, or interface type if x.isNil() { switch t := Tu.(type) { case *Basic: if t.kind == UnsafePointer { return true } case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface: return true } return false } // x is an untyped constant representable by a value of type T // TODO(gri) This is borrowing from checker.convertUntyped and // checker.isRepresentable. Need to clean up. if isUntyped(Vu) { switch t := Tu.(type) { case *Basic: if x.mode == constant { return isRepresentableConst(x.val, conf, t.kind, nil) } // The result of a comparison is an untyped boolean, // but may not be a constant. if Vb, _ := Vu.(*Basic); Vb != nil { return Vb.kind == UntypedBool && isBoolean(Tu) } case *Interface: return x.isNil() || t.NumMethods() == 0 case *Pointer, *Signature, *Slice, *Map, *Chan: return x.isNil() } } return false } // isInteger reports whether x is a (typed or untyped) integer value. func (x *operand) isInteger() bool { return x.mode == invalid || isInteger(x.typ) || x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt } ./go/types/expr.go0000644000014500017510000011350612246613010013557 0ustar michaelstaff// Copyright 2012 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 implements typechecking of expressions. package types import ( "go/ast" "go/token" "math" "code.google.com/p/go.tools/go/exact" ) /* Basic algorithm: Expressions are checked recursively, top down. Expression checker functions are generally of the form: func f(x *operand, e *ast.Expr, ...) where e is the expression to be checked, and x is the result of the check. The check performed by f may fail in which case x.mode == invalid, and related error messages will have been issued by f. If a hint argument is present, it is the composite literal element type of an outer composite literal; it is used to type-check composite literal elements that have no explicit type specification in the source (e.g.: []T{{...}, {...}}, the hint is the type T in this case). All expressions are checked via rawExpr, which dispatches according to expression kind. Upon returning, rawExpr is recording the types and constant values for all expressions that have an untyped type (those types may change on the way up in the expression tree). Usually these are constants, but the results of comparisons or non-constant shifts of untyped constants may also be untyped, but not constant. Untyped expressions may eventually become fully typed (i.e., not untyped), typically when the value is assigned to a variable, or is used otherwise. The updateExprType method is used to record this final type and update the recorded types: the type-checked expression tree is again traversed down, and the new type is propagated as needed. Untyped constant expression values that become fully typed must now be representable by the full type (constant sub-expression trees are left alone except for their roots). This mechanism ensures that a client sees the actual (run-time) type an untyped value would have. It also permits type-checking of lhs shift operands "as if the shift were not present": when updateExprType visits an untyped lhs shift operand and assigns it it's final type, that type must be an integer type, and a constant lhs must be representable as an integer. When an expression gets its final type, either on the way out from rawExpr, on the way down in updateExprType, or at the end of the type checker run, the type (and constant value, if any) is recorded via Info.Types and Values, if present. */ type opPredicates map[token.Token]func(Type) bool var unaryOpPredicates = opPredicates{ token.ADD: isNumeric, token.SUB: isNumeric, token.XOR: isInteger, token.NOT: isBoolean, } func (check *checker) op(m opPredicates, x *operand, op token.Token) bool { if pred := m[op]; pred != nil { if !pred(x.typ) { check.invalidOp(x.pos(), "operator %s not defined for %s", op, x) return false } } else { check.invalidAST(x.pos(), "unknown operator %s", op) return false } return true } func (check *checker) unary(x *operand, op token.Token) { switch op { case token.AND: // spec: "As an exception to the addressability // requirement x may also be a composite literal." if _, ok := unparen(x.expr).(*ast.CompositeLit); ok { x.mode = variable } if x.mode != variable { check.invalidOp(x.pos(), "cannot take address of %s", x) x.mode = invalid return } x.typ = &Pointer{base: x.typ} return case token.ARROW: typ, ok := x.typ.Underlying().(*Chan) if !ok { check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) x.mode = invalid return } if typ.dir&ast.RECV == 0 { check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) x.mode = invalid return } x.mode = commaok x.typ = typ.elem return } if !check.op(unaryOpPredicates, x, op) { x.mode = invalid return } if x.mode == constant { typ := x.typ.Underlying().(*Basic) size := -1 if isUnsigned(typ) { size = int(check.conf.sizeof(typ)) } x.val = exact.UnaryOp(op, x.val, size) // Typed constants must be representable in // their type after each constant operation. if isTyped(typ) { check.isRepresentableAs(x, typ) } return } x.mode = value // x.typ remains unchanged } func isShift(op token.Token) bool { return op == token.SHL || op == token.SHR } func isComparison(op token.Token) bool { // Note: tokens are not ordered well to make this much easier switch op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return true } return false } func fitsFloat32(x exact.Value) bool { f, _ := exact.Float64Val(x) // spec: "In all non-constant conversions involving floating-point // or complex values, if the result type cannot represent the value // the conversion succeeds but the result value is implementation- // dependent." // // We assume that float32(f) returns an Inf if f is too large for // a float32, or if f is an Inf; and that it returns 0 for values // with too small a magnitude. return !math.IsInf(float64(float32(f)), 0) } func roundFloat32(x exact.Value) exact.Value { f, _ := exact.Float64Val(x) f = float64(float32(f)) if !math.IsInf(f, 0) { return exact.MakeFloat64(f) } return nil } func fitsFloat64(x exact.Value) bool { f, _ := exact.Float64Val(x) return !math.IsInf(f, 0) } func roundFloat64(x exact.Value) exact.Value { f, _ := exact.Float64Val(x) if !math.IsInf(f, 0) { return exact.MakeFloat64(f) } return nil } // isRepresentableConst reports whether x can be represented as // value of the given basic type kind and for the configuration // provided (only needed for int/uint sizes). // // If rounded != nil, *rounded is set to the rounded value of x for // representable floating-point values; it is left alone otherwise. // It is ok to provide the addressof the first argument for rounded. func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool { switch x.Kind() { case exact.Unknown: return true case exact.Bool: return as == Bool || as == UntypedBool case exact.Int: if x, ok := exact.Int64Val(x); ok { switch as { case Int: var s = uint(conf.sizeof(Typ[as])) * 8 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 case Int8: const s = 8 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int16: const s = 16 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int32: const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int64: return true case Uint, Uintptr: if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 { return 0 <= x && x <= int64(1)<= 0 && n <= int(s) case Uint64: return exact.Sign(x) >= 0 && n <= 64 case Float32, Complex64: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64, Complex128: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedInt, UntypedFloat, UntypedComplex: return true } case exact.Float: switch as { case Float32, Complex64: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64, Complex128: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedFloat, UntypedComplex: return true } case exact.Complex: switch as { case Complex64: if rounded == nil { return fitsFloat32(exact.Real(x)) && fitsFloat32(exact.Imag(x)) } re := roundFloat32(exact.Real(x)) im := roundFloat32(exact.Imag(x)) if re != nil && im != nil { *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) return true } case Complex128: if rounded == nil { return fitsFloat64(exact.Real(x)) && fitsFloat64(exact.Imag(x)) } re := roundFloat64(exact.Real(x)) im := roundFloat64(exact.Imag(x)) if re != nil && im != nil { *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) return true } case UntypedComplex: return true } case exact.String: return as == String || as == UntypedString default: unreachable() } return false } // isRepresentableAs checks that a constant operand is representable in the given basic type. func (check *checker) isRepresentableAs(x *operand, typ *Basic) { assert(x.mode == constant) if !isRepresentableConst(x.val, check.conf, typ.kind, &x.val) { var msg string if isNumeric(x.typ) && isNumeric(typ) { // numeric conversion : error msg // // integer -> integer : overflows // integer -> float : overflows (actually not possible) // float -> integer : truncated // float -> float : overflows // if !isInteger(x.typ) && isInteger(typ) { msg = "%s truncated to %s" } else { msg = "%s overflows %s" } } else { msg = "cannot convert %s to %s" } check.errorf(x.pos(), msg, x, typ) x.mode = invalid } } // updateExprType updates the type of x to typ and invokes itself // recursively for the operands of x, depending on expression kind. // If typ is still an untyped and not the final type, updateExprType // only updates the recorded untyped type for x and possibly its // operands. Otherwise (i.e., typ is not an untyped type anymore, // or it is the final type for x), the type and value are recorded. // Also, if x is a constant, it must be representable as a value of typ, // and if x is the (formerly untyped) lhs operand of a non-constant // shift, it must be an integer value. // func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) { old, found := check.untyped[x] if !found { return // nothing to do } // update operands of x if necessary switch x := x.(type) { case *ast.BadExpr, *ast.FuncLit, *ast.CompositeLit, *ast.IndexExpr, *ast.SliceExpr, *ast.TypeAssertExpr, *ast.StarExpr, *ast.KeyValueExpr, *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: // These expression are never untyped - nothing to do. // The respective sub-expressions got their final types // upon assignment or use. if debug { check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ) unreachable() } return case *ast.CallExpr: // Resulting in an untyped constant (e.g., built-in complex). // The respective calls take care of calling updateExprType // for the arguments if necessary. case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr: // An identifier denoting a constant, a constant literal, // or a qualified identifier (imported untyped constant). // No operands to take care of. case *ast.ParenExpr: check.updateExprType(x.X, typ, final) case *ast.UnaryExpr: // If x is a constant, the operands were constants. // They don't need to be updated since they never // get "materialized" into a typed value; and they // will be processed at the end of the type check. if old.val != nil { break } check.updateExprType(x.X, typ, final) case *ast.BinaryExpr: if old.val != nil { break // see comment for unary expressions } if isComparison(x.Op) { // The result type is independent of operand types // and the operand types must have final types. } else if isShift(x.Op) { // The result type depends only on lhs operand. // The rhs type was updated when checking the shift. check.updateExprType(x.X, typ, final) } else { // The operand types match the result type. check.updateExprType(x.X, typ, final) check.updateExprType(x.Y, typ, final) } default: unreachable() } // If the new type is not final and still untyped, just // update the recorded type. if !final && isUntyped(typ) { old.typ = typ.Underlying().(*Basic) check.untyped[x] = old return } // Otherwise we have the final (typed or untyped type). // Remove it from the map. delete(check.untyped, x) // If x is the lhs of a shift, its final type must be integer. // We already know from the shift check that it is representable // as an integer if it is a constant. if old.isLhs && !isInteger(typ) { check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ) return } // Everything's fine, record final type and value for x. check.recordTypeAndValue(x, typ, old.val) } // convertUntyped attempts to set the type of an untyped value to the target type. func (check *checker) convertUntyped(x *operand, target Type) { if x.mode == invalid || isTyped(x.typ) { return } // TODO(gri) Sloppy code - clean up. This function is central // to assignment and expression checking. if isUntyped(target) { // both x and target are untyped xkind := x.typ.(*Basic).kind tkind := target.(*Basic).kind if isNumeric(x.typ) && isNumeric(target) { if xkind < tkind { x.typ = target check.updateExprType(x.expr, target, false) } } else if xkind != tkind { goto Error } return } // typed target switch t := target.Underlying().(type) { case *Basic: if x.mode == constant { check.isRepresentableAs(x, t) if x.mode == invalid { return } } else { // Non-constant untyped values may appear as the // result of comparisons (untyped bool), intermediate // (delayed-checked) rhs operands of shifts, and as // the value nil. switch x.typ.(*Basic).kind { case UntypedBool: if !isBoolean(target) { goto Error } case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex: if !isNumeric(target) { goto Error } case UntypedString: // Non-constant untyped string values are not // permitted by the spec and should not occur. unreachable() case UntypedNil: // Unsafe.Pointer is a basic type that includes nil. if !hasNil(target) { goto Error } default: goto Error } } case *Interface: if !x.isNil() && t.NumMethods() > 0 /* empty interfaces are ok */ { goto Error } // Update operand types to the default type rather then // the target (interface) type: values must have concrete // dynamic types. If the value is nil, keep it untyped // (this is important for tools such as go vet which need // the dynamic type for argument checking of say, print // functions) if x.isNil() { target = Typ[UntypedNil] } else { // cannot assign untyped values to non-empty interfaces if t.NumMethods() > 0 { goto Error } target = defaultType(x.typ) } case *Pointer, *Signature, *Slice, *Map, *Chan: if !x.isNil() { goto Error } // keep nil untyped - see comment for interfaces, above target = Typ[UntypedNil] default: goto Error } x.typ = target check.updateExprType(x.expr, target, true) // UntypedNils are final return Error: check.errorf(x.pos(), "cannot convert %s to %s", x, target) x.mode = invalid } func (check *checker) comparison(x, y *operand, op token.Token) { // spec: "In any comparison, the first operand must be assignable // to the type of the second operand, or vice versa." err := "" if x.isAssignableTo(check.conf, y.typ) || y.isAssignableTo(check.conf, x.typ) { defined := false switch op { case token.EQL, token.NEQ: // spec: "The equality operators == and != apply to operands that are comparable." defined = isComparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ) case token.LSS, token.LEQ, token.GTR, token.GEQ: // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered." defined = isOrdered(x.typ) default: unreachable() } if !defined { typ := x.typ if x.isNil() { typ = y.typ } err = check.sprintf("operator %s not defined for %s", op, typ) } } else { err = check.sprintf("mismatched types %s and %s", x.typ, y.typ) } if err != "" { check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err) x.mode = invalid return } if x.mode == constant && y.mode == constant { x.val = exact.MakeBool(exact.Compare(x.val, op, y.val)) // The operands are never materialized; no need to update // their types. } else { x.mode = value // The operands have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. check.updateExprType(x.expr, defaultType(x.typ), true) check.updateExprType(y.expr, defaultType(y.typ), true) } // spec: "Comparison operators compare two operands and yield // an untyped boolean value." x.typ = Typ[UntypedBool] } func (check *checker) shift(x, y *operand, op token.Token) { untypedx := isUntyped(x.typ) // The lhs must be of integer type or be representable // as an integer; otherwise the shift has no chance. if !isInteger(x.typ) && (!untypedx || !isRepresentableConst(x.val, nil, UntypedInt, nil)) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } // spec: "The right operand in a shift expression must have unsigned // integer type or be an untyped constant that can be converted to // unsigned integer type." switch { case isInteger(y.typ) && isUnsigned(y.typ): // nothing to do case isUntyped(y.typ): check.convertUntyped(y, Typ[UntypedInt]) if y.mode == invalid { x.mode = invalid return } default: check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } if x.mode == constant { if y.mode == constant { // rhs must be within reasonable bounds const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64 s, ok := exact.Uint64Val(y.val) if !ok || s > stupidShift { check.invalidOp(y.pos(), "stupid shift count %s", y) x.mode = invalid return } // The lhs is representable as an integer but may not be an integer // (e.g., 2.0, an untyped float) - this can only happen for untyped // non-integer numeric constants. Correct the type so that the shift // result is of integer type. if !isInteger(x.typ) { x.typ = Typ[UntypedInt] } x.val = exact.Shift(x.val, op, uint(s)) return } // non-constant shift with constant lhs if untypedx { // spec: "If the left operand of a non-constant shift // expression is an untyped constant, the type of the // constant is what it would be if the shift expression // were replaced by its left operand alone.". // // Delay operand checking until we know the final type: // The lhs expression must be in the untyped map, mark // the entry as lhs shift operand. info, found := check.untyped[x.expr] assert(found) info.isLhs = true check.untyped[x.expr] = info // keep x's type x.mode = value return } } // constant rhs must be >= 0 if y.mode == constant && exact.Sign(y.val) < 0 { check.invalidOp(y.pos(), "shift count %s must not be negative", y) } // non-constant shift - lhs must be an integer if !isInteger(x.typ) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } x.mode = value } var binaryOpPredicates = opPredicates{ token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) }, token.SUB: isNumeric, token.MUL: isNumeric, token.QUO: isNumeric, token.REM: isInteger, token.AND: isInteger, token.OR: isInteger, token.XOR: isInteger, token.AND_NOT: isInteger, token.LAND: isBoolean, token.LOR: isBoolean, } func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) { var y operand check.expr(x, lhs) check.expr(&y, rhs) if x.mode == invalid { return } if y.mode == invalid { x.mode = invalid x.expr = y.expr return } if isShift(op) { check.shift(x, &y, op) return } check.convertUntyped(x, y.typ) if x.mode == invalid { return } check.convertUntyped(&y, x.typ) if y.mode == invalid { x.mode = invalid return } if isComparison(op) { check.comparison(x, &y, op) return } if !IsIdentical(x.typ, y.typ) { // only report an error if we have valid types // (otherwise we had an error reported elsewhere already) if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] { check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ) } x.mode = invalid return } if !check.op(binaryOpPredicates, x, op) { x.mode = invalid return } if (op == token.QUO || op == token.REM) && (x.mode == constant || isInteger(x.typ)) && y.mode == constant && exact.Sign(y.val) == 0 { check.invalidOp(y.pos(), "division by zero") x.mode = invalid return } if x.mode == constant && y.mode == constant { typ := x.typ.Underlying().(*Basic) // force integer division of integer operands if op == token.QUO && isInteger(typ) { op = token.QUO_ASSIGN } x.val = exact.BinaryOp(x.val, op, y.val) // Typed constants must be representable in // their type after each constant operation. if isTyped(typ) { check.isRepresentableAs(x, typ) } return } x.mode = value // x.typ is unchanged } // index checks an index expression for validity. // If max >= 0, it is the upper bound for index. // If index is valid and the result i >= 0, then i is the constant value of index. func (check *checker) index(index ast.Expr, max int64) (i int64, valid bool) { var x operand check.expr(&x, index) if x.mode == invalid { return } // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "index %s must be integer", &x) return } // a constant index i must be in bounds if x.mode == constant { if exact.Sign(x.val) < 0 { check.invalidArg(x.pos(), "index %s must not be negative", &x) return } i, valid = exact.Int64Val(x.val) if !valid || max >= 0 && i >= max { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < max ] return i, true } return -1, true } // indexElts checks the elements (elts) of an array or slice composite literal // against the literal's element type (typ), and the element indices against // the literal length if known (length >= 0). It returns the length of the // literal (maximum index value + 1). // func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 { visited := make(map[int64]bool, len(elts)) var index, max int64 for _, e := range elts { // determine and check index validIndex := false eval := e if kv, _ := e.(*ast.KeyValueExpr); kv != nil { if i, ok := check.index(kv.Key, length); ok { if i >= 0 { index = i validIndex = true } else { check.errorf(e.Pos(), "index %s must be integer constant", kv.Key) } } eval = kv.Value } else if length >= 0 && index >= length { check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length) } else { validIndex = true } // if we have a valid index, check for duplicate entries if validIndex { if visited[index] { check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index) } visited[index] = true } index++ if index > max { max = index } // check element against composite literal element type var x operand check.exprWithHint(&x, eval, typ) if !check.assignment(&x, typ) && x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ) } } return max } // exprKind describes the kind of an expression; the kind // determines if an expression is valid in 'statement context'. type exprKind int const ( conversion exprKind = iota expression statement ) // rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { if trace { check.trace(e.Pos(), "%s", e) check.indent++ } kind := check.exprInternal(x, e, hint) // convert x into a user-friendly set of values record := true var typ Type var val exact.Value switch x.mode { case invalid: typ = Typ[Invalid] record = false // nothing to do case novalue: typ = (*Tuple)(nil) case constant: typ = x.typ val = x.val default: typ = x.typ } assert(x.expr != nil && typ != nil) if isUntyped(typ) { // delay notification until it becomes typed // or until the end of type checking check.untyped[x.expr] = exprInfo{false, typ.(*Basic), val} } else if record { // TODO(gri) ensure that literals always report // their dynamic (never interface) type. // This is not the case yet. check.recordTypeAndValue(e, typ, val) } if trace { check.indent-- check.trace(e.Pos(), "=> %s", x) } return kind } // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. // func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was issue 5770) x.mode = invalid x.typ = Typ[Invalid] switch e := e.(type) { case *ast.BadExpr: goto Error // error was reported before case *ast.Ident: check.ident(x, e, nil, false) case *ast.Ellipsis: // ellipses are handled explicitly where they are legal // (array composite literals and parameter lists) check.errorf(e.Pos(), "invalid use of '...'") goto Error case *ast.BasicLit: x.setConst(e.Kind, e.Value) if x.mode == invalid { check.invalidAST(e.Pos(), "invalid literal %v", e.Value) goto Error } case *ast.FuncLit: if sig, ok := check.typ(e.Type, nil, false).(*Signature); ok { x.mode = value x.typ = sig // Anonymous functions are considered part of the // init expression/func declaration which contains // them: use the current package-level declaration // info. check.later(nil, check.decl, sig, e.Body) } else { check.invalidAST(e.Pos(), "invalid function literal %s", e) goto Error } case *ast.CompositeLit: typ := hint openArray := false if e.Type != nil { // [...]T array types may only appear with composite literals. // Check for them here so we don't have to handle ... in general. typ = nil if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { // We have an "open" [...]T array type. // Create a new ArrayType with unknown length (-1) // and finish setting it up after analyzing the literal. typ = &Array{len: -1, elem: check.typ(atyp.Elt, nil, false)} openArray = true } } if typ == nil { typ = check.typ(e.Type, nil, false) } } if typ == nil { check.errorf(e.Pos(), "missing type in composite literal") goto Error } switch typ, _ := deref(typ); utyp := typ.Underlying().(type) { case *Struct: if len(e.Elts) == 0 { break } fields := utyp.fields if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { // all elements must have keys visited := make([]bool, len(fields)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal") continue } key, _ := kv.Key.(*ast.Ident) if key == nil { check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue } i := fieldIndex(utyp.fields, check.pkg, key.Name) if i < 0 { check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) continue } fld := fields[i] check.recordObject(key, fld) // 0 <= i < len(fields) if visited[i] { check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) continue } visited[i] = true check.expr(x, kv.Value) etyp := fld.typ if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } } else { // no element must have a key for i, e := range e.Elts { if kv, _ := e.(*ast.KeyValueExpr); kv != nil { check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") continue } check.expr(x, e) if i >= len(fields) { check.errorf(x.pos(), "too many values in struct literal") break // cannot continue } // i < len(fields) etyp := fields[i].typ if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } if len(e.Elts) < len(fields) { check.errorf(e.Rbrace, "too few values in struct literal") // ok to continue } } case *Array: n := check.indexedElts(e.Elts, utyp.elem, utyp.len) // if we have an "open" [...]T array, set the length now that we know it if openArray { utyp.len = n } case *Slice: check.indexedElts(e.Elts, utyp.elem, -1) case *Map: visited := make(map[interface{}]bool, len(e.Elts)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.errorf(e.Pos(), "missing key in map literal") continue } check.expr(x, kv.Key) if !check.assignment(x, utyp.key) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key) } continue } if x.mode == constant { if visited[x.val] { check.errorf(x.pos(), "duplicate key %s in map literal", x.val) continue } visited[x.val] = true } check.exprWithHint(x, kv.Value, utyp.elem) if !check.assignment(x, utyp.elem) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elem) } continue } } default: check.errorf(e.Pos(), "invalid composite literal type %s", typ) goto Error } x.mode = value x.typ = typ case *ast.ParenExpr: kind := check.rawExpr(x, e.X, nil) x.expr = e return kind case *ast.SelectorExpr: check.selector(x, e) case *ast.IndexExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { valid = true if x.mode == constant { length = int64(len(exact.StringVal(x.val))) } // an indexed string always yields a byte value // (not a constant) even if the string and the // index are constant x.mode = value x.typ = Typ[Byte] } case *Array: valid = true length = typ.len if x.mode != variable { x.mode = value } x.typ = typ.elem case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true length = typ.len x.mode = variable x.typ = typ.elem } case *Slice: valid = true x.mode = variable x.typ = typ.elem case *Map: var key operand check.expr(&key, e.Index) if !check.assignment(&key, typ.key) { if key.mode != invalid { check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key) } goto Error } x.mode = mapindex x.typ = typ.elem x.expr = e return expression } if !valid { check.invalidOp(x.pos(), "cannot index %s", x) goto Error } if e.Index == nil { check.invalidAST(e.Pos(), "missing index for %s", x) goto Error } check.index(e.Index, length) // ok to continue case *ast.SliceExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { if slice3(e) { check.invalidOp(x.pos(), "3-index slice of string") goto Error } valid = true if x.mode == constant { length = int64(len(exact.StringVal(x.val))) } // spec: "For untyped string operands the result // is a non-constant value of type string." x.mode = value if typ.kind == UntypedString { x.typ = Typ[String] } } case *Array: valid = true length = typ.len if x.mode != variable { check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) goto Error } x.typ = &Slice{elem: typ.elem} case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true length = typ.len x.mode = variable x.typ = &Slice{elem: typ.elem} } case *Slice: valid = true x.mode = variable // x.typ doesn't change } if !valid { check.invalidOp(x.pos(), "cannot slice %s", x) goto Error } // spec: "Only the first index may be omitted; it defaults to 0." if slice3(e) && (e.High == nil || sliceMax(e) == nil) { check.errorf(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)} { x := int64(-1) switch { case expr != nil: // The "capacity" is only known statically for strings, arrays, // and pointers to arrays, and it is the same as the length for // those types. max := int64(-1) if length >= 0 { max = length + 1 } if t, ok := check.index(expr, max); ok && t >= 0 { x = t } case i == 0: // default is 0 for the first index x = 0 case length >= 0: // default is length (== capacity) otherwise x = length } ind[i] = x } // constant indices must be in range // (check.index already checks that existing indices >= 0) L: for i, x := range ind[:len(ind)-1] { if x > 0 { for _, y := range ind[i+1:] { if y >= 0 && x > y { check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y) break L // only report one error, ok to continue } } } } case *ast.TypeAssertExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } xtyp, _ := x.typ.Underlying().(*Interface) if xtyp == nil { check.invalidOp(x.pos(), "%s is not an interface", x) goto Error } // x.(type) expressions are handled explicitly in type switches if e.Type == nil { check.invalidAST(e.Pos(), "use of .(type) outside type switch") goto Error } T := check.typ(e.Type, nil, false) if T == Typ[Invalid] { goto Error } check.typeAssertion(x.pos(), x, xtyp, T) x.mode = commaok x.typ = T case *ast.CallExpr: return check.call(x, e) case *ast.StarExpr: check.exprOrType(x, e.X) switch x.mode { case invalid: goto Error case typexpr: x.typ = &Pointer{base: x.typ} default: if typ, ok := x.typ.Underlying().(*Pointer); ok { x.mode = variable x.typ = typ.base } else { check.invalidOp(x.pos(), "cannot indirect %s", x) goto Error } } case *ast.UnaryExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } check.unary(x, e.Op) if x.mode == invalid { goto Error } if e.Op == token.ARROW { x.expr = e return statement // receive operations may appear in statement context } case *ast.BinaryExpr: check.binary(x, e.X, e.Y, e.Op) if x.mode == invalid { goto Error } case *ast.KeyValueExpr: // key:value expressions are handled in composite literals check.invalidAST(e.Pos(), "no key:value expected") goto Error case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: x.mode = typexpr x.typ = check.typ(e, nil, false) // Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue // even though check.typ has already called it. This is fine as both // times the same expression and type are recorded. It is also not a // performance issue because we only reach here for composite literal // types, which are comparatively rare. default: if debug { check.dump("expr = %v (%T)", e, e) } unreachable() } // everything went well x.expr = e return expression Error: x.mode = invalid x.expr = e return statement // avoid follow-up errors } // typeAssertion checks that x.(T) is legal; xtyp must be the type of x. func (check *checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) { method, wrongType := MissingMethod(T, xtyp, false) if method == nil { return } var msg string if wrongType { msg = "wrong type for method" } else { msg = "missing method" } check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name) } // expr typechecks expression e and initializes x with the expression value. // If an error occurred, x.mode is set to invalid. // func (check *checker) expr(x *operand, e ast.Expr) { check.rawExpr(x, e, nil) var msg string switch x.mode { default: return case novalue: msg = "used as value" case builtin: msg = "must be called" case typexpr: msg = "is not an expression" } check.errorf(x.pos(), "%s %s", x, msg) x.mode = invalid } // exprWithHint typechecks expression e and initializes x with the expression value. // If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type) { assert(hint != nil) check.rawExpr(x, e, hint) var msg string switch x.mode { default: return case novalue: msg = "used as value" case builtin: msg = "must be called" case typexpr: msg = "is not an expression" } check.errorf(x.pos(), "%s %s", x, msg) x.mode = invalid } // exprOrType typechecks expression or type e and initializes x with the expression value or type. // If an error occurred, x.mode is set to invalid. // func (check *checker) exprOrType(x *operand, e ast.Expr) { check.rawExpr(x, e, nil) if x.mode == novalue { check.errorf(x.pos(), "%s used as value or type", x) x.mode = invalid } } ./go/types/typexpr.go0000644000014500017510000004146612246613010014321 0ustar michaelstaff// 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. // This file implements type-checking of identifiers and type expressions. package types import ( "go/ast" "go/token" "sort" "strconv" "code.google.com/p/go.tools/go/exact" ) // ident type-checks identifier e and initializes x with the value or type of e. // If an error occurred, x.mode is set to invalid. // For the meaning of def and cycleOk, see check.typ, below. // func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) { x.mode = invalid x.expr = e obj := check.topScope.LookupParent(e.Name) if obj == nil { if e.Name == "_" { check.errorf(e.Pos(), "cannot use _ as value or type") } else { check.errorf(e.Pos(), "undeclared name: %s", e.Name) } return } check.recordObject(e, obj) typ := obj.Type() if typ == nil { // object not yet declared if check.objMap == nil { check.dump("%s: %s should have been declared (we are inside a function)", e.Pos(), e) unreachable() } check.objDecl(obj, def, cycleOk) typ = obj.Type() } assert(typ != nil) switch obj := obj.(type) { case *PkgName: check.errorf(e.Pos(), "use of package %s not in selector", obj.name) return case *Const: // The constant may be dot-imported. Mark it as used so that // later we can determine if the corresponding dot-imported // package was used. Same applies for other objects, below. // (This code is only used for dot-imports. Without them, we // would only have to mark Vars.) obj.used = true if typ == Typ[Invalid] { return } if obj == universeIota { if check.iota == nil { check.errorf(e.Pos(), "cannot use iota outside constant declaration") return } x.val = check.iota } else { x.val = obj.val } assert(x.val != nil) x.mode = constant case *TypeName: obj.used = true x.mode = typexpr named, _ := typ.(*Named) if !cycleOk && named != nil && !named.complete { check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name) // maintain x.mode == typexpr despite error typ = Typ[Invalid] } if def != nil { def.underlying = typ } case *Var: obj.used = true x.mode = variable if typ != Typ[Invalid] { check.addDeclDep(obj) } case *Func: obj.used = true x.mode = value if typ != Typ[Invalid] { check.addDeclDep(obj) } case *Builtin: obj.used = true // for built-ins defined by package unsafe x.mode = builtin x.id = obj.id case *Nil: // no need to "use" the nil object x.mode = value default: unreachable() } x.typ = typ } // typ type-checks the type expression e and returns its type, or Typ[Invalid]. // If def != nil, e is the type specification for the named type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. // If cycleOk is set, e (or elements of e) may refer to a named type that is not // yet completely set up. // func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type { if trace { check.trace(e.Pos(), "%s", e) check.indent++ } t := check.typInternal(e, def, cycleOk) assert(e != nil && t != nil && isTyped(t)) check.recordTypeAndValue(e, t, nil) if trace { check.indent-- check.trace(e.Pos(), "=> %s", t) } return t } // funcType type-checks a function or method type and returns its signature. func (check *checker) funcType(recv *ast.FieldList, ftyp *ast.FuncType, def *Named) *Signature { sig := new(Signature) if def != nil { def.underlying = sig } scope := NewScope(check.topScope) check.recordScope(ftyp, scope) recv_, _ := check.collectParams(scope, recv, false) params, isVariadic := check.collectParams(scope, ftyp.Params, true) results, _ := check.collectParams(scope, ftyp.Results, false) if len(recv_) > 0 { // There must be exactly one receiver. if len(recv_) > 1 { check.invalidAST(recv_[1].Pos(), "method must have exactly one receiver") // ok to continue } recv := recv_[0] // spec: "The receiver type must be of the form T or *T where T is a type name." // (ignore invalid types - error was reported before) if t, _ := deref(recv.typ); t != Typ[Invalid] { var err string if T, _ := t.(*Named); T != nil { // spec: "The type denoted by T is called the receiver base type; it must not // be a pointer or interface type and it must be declared in the same package // as the method." if T.obj.pkg != check.pkg { err = "type not defined in this package" } else { // TODO(gri) This is not correct if the underlying type is unknown yet. switch u := T.underlying.(type) { case *Basic: // unsafe.Pointer is treated like a regular pointer if u.kind == UnsafePointer { err = "unsafe.Pointer" } case *Pointer, *Interface: err = "pointer or interface type" } } } else { err = "basic or unnamed type" } if err != "" { check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err) // ok to continue } } sig.recv = recv } sig.scope = scope sig.params = NewTuple(params...) sig.results = NewTuple(results...) sig.isVariadic = isVariadic return sig } // typInternal contains the core of type checking of types. // Must only be called by typ. // func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type { switch e := e.(type) { case *ast.BadExpr: // ignore - error reported before case *ast.Ident: var x operand check.ident(&x, e, def, cycleOk) switch x.mode { case typexpr: return x.typ case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) default: check.errorf(x.pos(), "%s is not a type", &x) } case *ast.SelectorExpr: var x operand check.selector(&x, e) switch x.mode { case typexpr: return x.typ case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) default: check.errorf(x.pos(), "%s is not a type", &x) } case *ast.ParenExpr: return check.typ(e.X, def, cycleOk) case *ast.ArrayType: if e.Len != nil { var x operand check.expr(&x, e.Len) if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } break } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", &x) break } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", &x) break } typ := new(Array) if def != nil { def.underlying = typ } typ.len = n typ.elem = check.typ(e.Elt, nil, cycleOk) return typ } else { typ := new(Slice) if def != nil { def.underlying = typ } typ.elem = check.typ(e.Elt, nil, true) return typ } case *ast.StructType: typ := new(Struct) if def != nil { def.underlying = typ } typ.fields, typ.tags = check.collectFields(e.Fields, cycleOk) return typ case *ast.StarExpr: typ := new(Pointer) if def != nil { def.underlying = typ } typ.base = check.typ(e.X, nil, true) return typ case *ast.FuncType: return check.funcType(nil, e, def) case *ast.InterfaceType: return check.interfaceType(e, def, cycleOk) case *ast.MapType: typ := new(Map) if def != nil { def.underlying = typ } typ.key = check.typ(e.Key, nil, true) typ.elem = check.typ(e.Value, nil, true) // spec: "The comparison operators == and != must be fully defined // for operands of the key type; thus the key type must not be a // function, map, or slice." // // Delay this check because it requires fully setup types; // it is safe to continue in any case (was issue 6667). check.delay(func() { if !isComparable(typ.key) { check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) } }) return typ case *ast.ChanType: typ := new(Chan) if def != nil { def.underlying = typ } typ.dir = e.Dir typ.elem = check.typ(e.Value, nil, true) return typ default: check.errorf(e.Pos(), "%s is not a type", e) } return Typ[Invalid] } // typeOrNil type-checks the type expression (or nil value) e // and returns the typ of e, or nil. // If e is neither a type nor nil, typOrNil returns Typ[Invalid]. // func (check *checker) typOrNil(e ast.Expr) Type { var x operand check.rawExpr(&x, e, nil) switch x.mode { case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) case typexpr: return x.typ case value: if x.isNil() { return nil } fallthrough default: check.errorf(x.pos(), "%s is not a type", &x) } return Typ[Invalid] } func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) { if list == nil { return } for i, field := range list.List { ftype := field.Type if t, _ := ftype.(*ast.Ellipsis); t != nil { ftype = t.Elt if variadicOk && i == len(list.List)-1 { isVariadic = true } else { check.invalidAST(field.Pos(), "... not permitted") // ignore ... and continue } } typ := check.typ(ftype, nil, true) // The parser ensures that f.Tag is nil and we don't // care if a constructed AST contains a non-nil tag. if len(field.Names) > 0 { // named parameter for _, name := range field.Names { par := NewParam(name.Pos(), check.pkg, name.Name, typ) check.declare(scope, name, par) params = append(params, par) } } else { // anonymous parameter par := NewParam(ftype.Pos(), check.pkg, "", typ) check.recordImplicit(field, par) params = append(params, par) } } // For a variadic function, change the last parameter's type from T to []T. if isVariadic && len(params) > 0 { last := params[len(params)-1] last.typ = &Slice{elem: last.typ} } return } func (check *checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool { if alt := oset.insert(obj); alt != nil { check.errorf(pos, "%s redeclared", obj.Name()) check.reportAltDecl(alt) return false } return true } func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk bool) *Interface { iface := new(Interface) if def != nil { def.underlying = iface } // empty interface: common case if ityp.Methods == nil { return iface } // The parser ensures that field tags are nil and we don't // care if a constructed AST contains non-nil tags. // Phase 1: Collect explicitly declared methods, the corresponding // signature (AST) expressions, and the list of embedded // type (AST) expressions. Do not resolve signatures or // embedded types yet to avoid cycles referring to this // interface. var ( mset objset signatures []ast.Expr // list of corresponding method signatures embedded []ast.Expr // list of embedded types ) for _, f := range ityp.Methods.List { if len(f.Names) > 0 { // The parser ensures that there's only one method // and we don't care if a constructed AST has more. name := f.Names[0] pos := name.Pos() // Don't type-check signature yet - use an // empty signature now and update it later. m := NewFunc(pos, check.pkg, name.Name, new(Signature)) // spec: "As with all method sets, in an interface type, // each method must have a unique name." // (The spec does not exclude blank _ identifiers for // interface methods.) if check.declareInSet(&mset, pos, m) { iface.methods = append(iface.methods, m) iface.allMethods = append(iface.allMethods, m) signatures = append(signatures, f.Type) check.recordObject(name, m) } } else { // embedded type embedded = append(embedded, f.Type) } } // Phase 2: Resolve embedded interfaces. Because an interface must not // embed itself (directly or indirectly), each embedded interface // can be fully resolved without depending on any method of this // interface (if there is a cycle or another error, the embedded // type resolves to an invalid type and is ignored). // In particular, the list of methods for each embedded interface // must be complete (it cannot depend on this interface), and so // those methods can be added to the list of all methods of this // interface. for _, e := range embedded { pos := e.Pos() typ := check.typ(e, nil, cycleOk) if typ == Typ[Invalid] { continue } named, _ := typ.(*Named) if named == nil { check.invalidAST(pos, "%s is not named type", typ) continue } // determine underlying (possibly incomplete) type // by following its forward chain // TODO(gri) should this be part of Underlying()? u := named.underlying for { n, _ := u.(*Named) if n == nil { break } u = n.underlying } if u == Typ[Invalid] { continue } embed, _ := u.(*Interface) if embed == nil { check.errorf(pos, "%s is not an interface", named) continue } iface.types = append(iface.types, named) // collect embedded methods for _, m := range embed.allMethods { if check.declareInSet(&mset, pos, m) { iface.allMethods = append(iface.allMethods, m) } } } // Phase 3: At this point all methods have been collected for this interface. // It is now safe to type-check the signatures of all explicitly // declared methods, even if they refer to this interface via a cycle // and embed the methods of this interface in a parameter of interface // type. // determine receiver type var recv Type = iface if def != nil { def.underlying = iface recv = def // use named receiver type if available } for i, m := range iface.methods { expr := signatures[i] typ := check.typ(expr, nil, true) if typ == Typ[Invalid] { continue // keep method with empty method signature } sig, _ := typ.(*Signature) if sig == nil { check.invalidAST(expr.Pos(), "%s is not a method signature", typ) continue // keep method with empty method signature } sig.recv = NewVar(m.pos, check.pkg, "", recv) *m.typ.(*Signature) = *sig // update signature (don't replace it!) } sort.Sort(byUniqueMethodName(iface.allMethods)) return iface } // byUniqueMethodName method lists can be sorted by their unique method names. type byUniqueMethodName []*Func func (a byUniqueMethodName) Len() int { return len(a) } func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() } func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (check *checker) tag(t *ast.BasicLit) string { if t != nil { if t.Kind == token.STRING { if val, err := strconv.Unquote(t.Value); err == nil { return val } } check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value) } return "" } func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Var, tags []string) { if list == nil { return } var fset objset var typ Type // current field typ var tag string // current field tag add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) { if tag != "" && tags == nil { tags = make([]string, len(fields)) } if tags != nil { tags = append(tags, tag) } fld := NewField(pos, check.pkg, name, typ, anonymous) // spec: "Within a struct, non-blank field names must be unique." if name == "_" || check.declareInSet(&fset, pos, fld) { fields = append(fields, fld) if ident != nil { check.recordObject(ident, fld) } } } for _, f := range list.List { typ = check.typ(f.Type, nil, cycleOk) tag = check.tag(f.Tag) if len(f.Names) > 0 { // named fields for _, name := range f.Names { add(f, name, name.Name, false, name.Pos()) } } else { // anonymous field pos := f.Type.Pos() t, isPtr := deref(typ) switch t := t.(type) { case *Basic: if t == Typ[Invalid] { // error was reported before continue } // unsafe.Pointer is treated like a regular pointer if t.kind == UnsafePointer { check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") continue } add(f, nil, t.name, true, pos) case *Named: // spec: "An embedded type must be specified as a type name // T or as a pointer to a non-interface type name *T, and T // itself may not be a pointer type." switch u := t.Underlying().(type) { case *Basic: // unsafe.Pointer is treated like a regular pointer if u.kind == UnsafePointer { check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") continue } case *Pointer: check.errorf(pos, "anonymous field type cannot be a pointer") continue case *Interface: if isPtr { check.errorf(pos, "anonymous field type cannot be a pointer to an interface") continue } } add(f, nil, t.obj.name, true, pos) default: check.invalidAST(pos, "anonymous field type %s must be named", typ) } } } return } ./go/types/errors.go0000644000014500017510000000363612246613010014117 0ustar michaelstaff// Copyright 2012 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 implements various error reporters. package types import ( "fmt" "go/ast" "go/token" "strings" ) func assert(p bool) { if !p { panic("assertion failed") } } func unreachable() { panic("unreachable") } func (check *checker) sprintf(format string, args ...interface{}) string { for i, arg := range args { switch a := arg.(type) { case nil: args[i] = "" case operand: panic("internal error: should always pass *operand") case token.Pos: args[i] = check.fset.Position(a).String() case ast.Expr: args[i] = ExprString(a) } } return fmt.Sprintf(format, args...) } func (check *checker) trace(pos token.Pos, format string, args ...interface{}) { fmt.Printf("%s:\t%s%s\n", check.fset.Position(pos), strings.Repeat(". ", check.indent), check.sprintf(format, args...), ) } // dump is only needed for debugging func (check *checker) dump(format string, args ...interface{}) { fmt.Println(check.sprintf(format, args...)) } func (check *checker) err(pos token.Pos, msg string) { err := Error{check.fset, pos, msg} if check.firstErr == nil { check.firstErr = err } f := check.conf.Error if f == nil { panic(bailout{}) // report only first error } f(err) } func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) { check.err(pos, check.sprintf(format, args...)) } func (check *checker) invalidAST(pos token.Pos, format string, args ...interface{}) { check.errorf(pos, "invalid AST: "+format, args...) } func (check *checker) invalidArg(pos token.Pos, format string, args ...interface{}) { check.errorf(pos, "invalid argument: "+format, args...) } func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{}) { check.errorf(pos, "invalid operation: "+format, args...) } ./go/types/testdata/0000755000014500017510000000000012246613010014055 5ustar michaelstaff./go/types/testdata/builtins.src0000644000014500017510000005260312246613010016425 0ustar michaelstaff// Copyright 2012 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. // builtin calls package builtins import "unsafe" func f0() {} func append1() { var b byte var x int var s []byte _ = append() // ERROR not enough arguments _ = append("foo" /* ERROR not a slice */ ) _ = append(nil /* ERROR not a slice */ , s) _ = append(x /* ERROR not a slice */ , s) _ = append(s) append /* ERROR not used */ (s) _ = append(s, b) _ = append(s, x /* ERROR cannot pass argument x */ ) _ = append(s, s /* ERROR cannot pass argument s */ ) _ = append(s /* ERROR can only use ... with matching parameter */ ...) _ = append(s, b, s /* ERROR can only use ... with matching parameter */ ...) _ = append(s, 1, 2, 3) _ = append(s, 1, 2, 3, x /* ERROR cannot pass argument x */ , 5, 6, 6) _ = append(s, 1, 2, s /* ERROR can only use ... with matching parameter */ ...) _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false) type S []byte type T string var t T _ = append(s, "foo" /* ERROR cannot convert */ ) _ = append(s, "foo"...) _ = append(S(s), "foo" /* ERROR cannot convert */ ) _ = append(S(s), "foo"...) _ = append(s, t /* ERROR cannot pass argument t */ ) _ = append(s, t...) _ = append(s, T("foo")...) _ = append(S(s), t /* ERROR cannot pass argument t */ ) _ = append(S(s), t...) _ = append(S(s), T("foo")...) _ = append([]string{}, t /* ERROR cannot pass argument t */ , "foo") _ = append([]T{}, t, "foo") } // from the spec func append2() { s0 := []int{0, 0} s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2} s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7} s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0} var t []interface{} t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"} var b []byte b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' } _ = s4 } func append3() { f1 := func() (s []int) { return } f2 := func() (s []int, x int) { return } f3 := func() (s []int, x, y int) { return } f5 := func() (s []interface{}, x int, y float32, z string, b bool) { return } ff := func() (int, float32) { return 0, 0 } _ = append(f0 /* ERROR used as value */ ()) _ = append(f1()) _ = append(f2()) _ = append(f3()) _ = append(f5()) _ = append(ff /* ERROR not a slice */ ()) // TODO(gri) better error message } func cap1() { var a [10]bool var p *[20]int var c chan string _ = cap() // ERROR not enough arguments _ = cap(1, 2) // ERROR too many arguments _ = cap(42 /* ERROR invalid */) const _3 = cap(a) assert(_3 == 10) const _4 = cap(p) assert(_4 == 20) _ = cap(c) cap /* ERROR not used */ (c) // issue 4744 type T struct{ a [10]int } const _ = cap(((*T)(nil)).a) var s [][]byte _ = cap(s) _ = cap(s... /* ERROR invalid use of \.\.\. */ ) } func cap2() { f1a := func() (a [10]int) { return } f1s := func() (s []int) { return } f2 := func() (s []int, x int) { return } _ = cap(f0 /* ERROR used as value */ ()) _ = cap(f1a()) _ = cap(f1s()) _ = cap(f2()) // ERROR too many arguments } func close1() { var c chan int var r <-chan int close() // ERROR not enough arguments close(1, 2) // ERROR too many arguments close(42 /* ERROR not a channel */) close(r /* ERROR receive-only channel */) close(c) _ = close /* ERROR used as value */ (c) var s []chan int close(s... /* ERROR invalid use of \.\.\. */ ) } func close2() { f1 := func() (ch chan int) { return } f2 := func() (ch chan int, x int) { return } close(f0 /* ERROR used as value */ ()) close(f1()) close(f2()) // ERROR too many arguments } func complex1() { var i32 int32 var f32 float32 var f64 float64 var c64 complex64 var c128 complex128 _ = complex() // ERROR not enough arguments _ = complex(1) // ERROR not enough arguments _ = complex(true /* ERROR invalid argument */ , 0) _ = complex(i32 /* ERROR invalid argument */ , 0) _ = complex("foo" /* ERROR invalid argument */ , 0) _ = complex(c64 /* ERROR invalid argument */ , 0) _ = complex(0, true /* ERROR invalid argument */ ) _ = complex(0, i32 /* ERROR invalid argument */ ) _ = complex(0, "foo" /* ERROR invalid argument */ ) _ = complex(0, c64 /* ERROR invalid argument */ ) _ = complex(f32, f32) _ = complex(f32, 1) _ = complex(f32, 1.0) _ = complex(f32, 'a') _ = complex(f64, f64) _ = complex(f64, 1) _ = complex(f64, 1.0) _ = complex(f64, 'a') _ = complex(f32 /* ERROR mismatched types */ , f64) _ = complex(f64 /* ERROR mismatched types */ , f32) _ = complex(1, 1) _ = complex(1, 1.1) _ = complex(1, 'a') complex /* ERROR not used */ (1, 2) var _ complex64 = complex(f32, f32) var _ complex64 = complex /* ERROR cannot initialize */ (f64, f64) var _ complex128 = complex /* ERROR cannot initialize */ (f32, f32) var _ complex128 = complex(f64, f64) // untyped constants const _ int = complex(1, 0) const _ float32 = complex(1, 0) const _ complex64 = complex(1, 0) const _ complex128 = complex(1, 0) const _ int = complex /* ERROR int */ (1.1, 0) const _ float32 = complex /* ERROR float32 */ (1, 2) // untyped values var s uint _ = complex(1 /* ERROR integer */ < 10: if x == 11 { break L3 } if x == 12 { continue L3 /* ERROR "invalid continue label L3" */ } goto L3 } L4: if true { if x == 13 { break L4 /* ERROR "invalid break label L4" */ } if x == 14 { continue L4 /* ERROR "invalid continue label L4" */ } if x == 15 { goto L4 } } L5: f1() if x == 16 { break L5 /* ERROR "invalid break label L5" */ } if x == 17 { continue L5 /* ERROR "invalid continue label L5" */ } if x == 18 { goto L5 } for { if x == 19 { break L1 /* ERROR "invalid break label L1" */ } if x == 20 { continue L1 /* ERROR "invalid continue label L1" */ } if x == 21 { goto L1 } } } // Additional tests not in the original files. func f2() { L1 /* ERROR "label L1 declared but not used" */ : if x == 0 { for { continue L1 /* ERROR "invalid continue label L1" */ } } } func f3() { L1: L2: L3: for { break L1 /* ERROR "invalid break label L1" */ break L2 /* ERROR "invalid break label L2" */ break L3 continue L1 /* ERROR "invalid continue label L1" */ continue L2 /* ERROR "invalid continue label L2" */ continue L3 goto L1 goto L2 goto L3 } }./go/types/testdata/cycles.src0000644000014500017510000000332112246613010016047 0ustar michaelstaff// 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 cycles type ( T0 int T1 /* ERROR "cycle" */ T1 T2 *T2 T3 /* ERROR "cycle" */ T4 T4 T5 T5 T3 T6 T7 T7 *T8 T8 T6 // arrays A0 /* ERROR "cycle" */ [10]A0 A1 [10]*A1 A2 /* ERROR "cycle" */ [10]A3 A3 [10]A4 A4 A2 A5 [10]A6 A6 *A5 // slices L0 []L0 // structs S0 /* ERROR "cycle" */ struct{ _ S0 } S1 /* ERROR "cycle" */ struct{ S1 } S2 struct{ _ *S2 } S3 struct{ *S3 } S4 /* ERROR "cycle" */ struct{ S5 } S5 struct{ S6 } S6 S4 // pointers P0 *P0 // functions F0 func(F0) F1 func() F1 F2 func(F2) F2 // interfaces I0 /* ERROR "cycle" */ interface{ I0 } I1 /* ERROR "cycle" */ interface{ I2 } I2 interface{ I3 } I3 interface{ I1 } I4 interface{ f(I4) } // testcase for issue 5090 I5 interface{ f(I6) } I6 interface{ I5 } // maps M0 map[M0 /* ERROR "invalid map key" */ ]M0 // channels C0 chan C0 ) func _() { type ( t1 /* ERROR "cycle" */ t1 t2 *t2 t3 t4 /* ERROR "undeclared" */ t4 t5 /* ERROR "undeclared" */ t5 t3 // arrays a0 /* ERROR "cycle" */ [10]a0 a1 [10]*a1 // slices l0 []l0 // structs s0 /* ERROR "cycle" */ struct{ _ s0 } s1 /* ERROR "cycle" */ struct{ s1 } s2 struct{ _ *s2 } s3 struct{ *s3 } // pointers p0 *p0 // functions f0 func(f0) f1 func() f1 f2 func(f2) f2 // interfaces i0 /* ERROR "cycle" */ interface{ i0 } // maps m0 map[m0 /* ERROR "invalid map key" */ ]m0 // channels c0 chan c0 ) } // test cases for issue 6667 type A [10]map[A /* ERROR invalid map key */ ]bool type S struct { m map[S /* ERROR invalid map key */ ]bool } ./go/types/testdata/expr0.src0000644000014500017510000000566712246613010015642 0ustar michaelstaff// Copyright 2012 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. // unary expressions package expr0 var ( // bool b0 = true b1 bool = b0 b2 = !true b3 = !b1 b4 bool = !true b5 bool = !b4 b6 = +b0 /* ERROR "not defined" */ b7 = -b0 /* ERROR "not defined" */ b8 = ^b0 /* ERROR "not defined" */ b9 = *b0 /* ERROR "cannot indirect" */ b10 = &true /* ERROR "cannot take address" */ b11 = &b0 b12 = <-b0 /* ERROR "cannot receive" */ // int i0 = 1 i1 int = i0 i2 = +1 i3 = +i0 i4 int = +1 i5 int = +i4 i6 = -1 i7 = -i0 i8 int = -1 i9 int = -i4 i10 = !i0 /* ERROR "not defined" */ i11 = ^1 i12 = ^i0 i13 int = ^1 i14 int = ^i4 i15 = *i0 /* ERROR "cannot indirect" */ i16 = &i0 i17 = *i16 i18 = <-i16 /* ERROR "cannot receive" */ // uint u0 = uint(1) u1 uint = u0 u2 = +1 u3 = +u0 u4 uint = +1 u5 uint = +u4 u6 = -1 u7 = -u0 u8 uint = - /* ERROR "overflows" */ 1 u9 uint = -u4 u10 = !u0 /* ERROR "not defined" */ u11 = ^1 u12 = ^i0 u13 uint = ^ /* ERROR "overflows" */ 1 u14 uint = ^u4 u15 = *u0 /* ERROR "cannot indirect" */ u16 = &u0 u17 = *u16 u18 = <-u16 /* ERROR "cannot receive" */ u19 = ^uint(0) // float64 f0 = float64(1) f1 float64 = f0 f2 = +1 f3 = +f0 f4 float64 = +1 f5 float64 = +f4 f6 = -1 f7 = -f0 f8 float64 = -1 f9 float64 = -f4 f10 = !f0 /* ERROR "not defined" */ f11 = ^1 f12 = ^i0 f13 float64 = ^1 f14 float64 = ^f4 /* ERROR "not defined" */ f15 = *f0 /* ERROR "cannot indirect" */ f16 = &f0 f17 = *u16 f18 = <-u16 /* ERROR "cannot receive" */ // complex128 c0 = complex128(1) c1 complex128 = c0 c2 = +1 c3 = +c0 c4 complex128 = +1 c5 complex128 = +c4 c6 = -1 c7 = -c0 c8 complex128 = -1 c9 complex128 = -c4 c10 = !c0 /* ERROR "not defined" */ c11 = ^1 c12 = ^i0 c13 complex128 = ^1 c14 complex128 = ^c4 /* ERROR "not defined" */ c15 = *c0 /* ERROR "cannot indirect" */ c16 = &c0 c17 = *u16 c18 = <-u16 /* ERROR "cannot receive" */ // string s0 = "foo" s1 = +"foo" /* ERROR "not defined" */ s2 = -s0 /* ERROR "not defined" */ s3 = !s0 /* ERROR "not defined" */ s4 = ^s0 /* ERROR "not defined" */ s5 = *s4 /* ERROR "cannot indirect" */ s6 = &s4 s7 = *s6 s8 = <-s7 /* ERROR "cannot receive" */ // channel ch chan int rc <-chan float64 sc chan <- string ch0 = +ch /* ERROR "not defined" */ ch1 = -ch /* ERROR "not defined" */ ch2 = !ch /* ERROR "not defined" */ ch3 = ^ch /* ERROR "not defined" */ ch4 = *ch /* ERROR "cannot indirect" */ ch5 = &ch ch6 = *ch5 ch7 = <-ch ch8 = <-rc ch9 = <-sc /* ERROR "cannot receive" */ ) // address of composite literals type T struct{x, y int} func f() T { return T{} } var ( _ = &T{1, 2} _ = &[...]int{} _ = &[]int{} _ = &[]int{} _ = &map[string]T{} _ = &(T{1, 2}) _ = &((((T{1, 2})))) _ = &f /* ERROR "cannot take address" */ () ) // recursive pointer types type P *P var ( p1 P = new(P) p2 P = *p1 p3 P = &p2 ) ./go/types/testdata/init0.src0000644000014500017510000000273212246613010015615 0ustar michaelstaff// 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. // initialization cycles package init0 // type-checking, not initialization cycles (we don't know the types) // (avoid duplicate errors) var ( x0 /* ERROR cycle */ = x0 x1 /* ERROR cycle */ = y1 y1 = x1 a1 = b1 b1 /* ERROR cycle */ = c1 c1 = d1 d1 = b1 ) // initialization cycles (we know the types) var ( x00 /* ERROR initialization cycle */ int = x00 x2 /* ERROR initialization cycle */ int = y2 y2 = x2 a2 = b2 b2 /* ERROR initialization cycle */ int = c2 c2 = d2 d2 = b2 ) // cycles via struct fields type S1 struct { f int } var x3 /* ERROR initialization cycle */ S1 = S1{x3.f} // cycles via functions var x4 = x5 var x5 /* ERROR initialization cycle */ = f1() func f1() int { return x5*10 } var x6 /* ERROR initialization cycle */ , x7 = f2() var x8 = x7 func f2() (int, int) { return f3() + f3(), 0 } func f3() int { return x8 } // cycles via closures var x9 /* ERROR initialization cycle */ = func() int { return x9 }() var x10 /* ERROR initialization cycle */ = f4() func f4() int { _ = func() { _ = x10 } return 0 } // cycles via method expressions type T1 struct{} func (T1) m() bool { _ = x11; return false } var x11 /* ERROR initialization cycle */ = T1.m(T1{}) // no cycles via method values type T2 struct{} func (T2) m() bool { _ = x12; return false } var t1 T2 var x12 = t1.m ./go/types/testdata/expr2.src0000644000014500017510000001050512246613010015627 0ustar michaelstaff// Copyright 2012 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. // comparisons package expr2 func _bool() { const t = true == true const f = true == false _ = t /* ERROR "cannot compare" */ < f _ = 0 /* ERROR "cannot convert" */ == t var b bool var x, y float32 b = x < y _ = b _ = struct{b bool}{x < y} } // corner cases var ( v0 = nil /* ERROR "cannot compare" */ == nil ) func arrays() { // basics var a, b [10]int _ = a == b _ = a != b _ = a /* ERROR < not defined */ < b _ = a == nil /* ERROR cannot convert */ type C [10]int var c C _ = a == c type D [10]int var d D _ = c /* ERROR mismatched types */ == d var e [10]func() int _ = e /* ERROR == not defined */ == e } func structs() { // basics var s, t struct { x int a [10]float32 _ bool } _ = s == t _ = s != t _ = s /* ERROR < not defined */ < t _ = s == nil /* ERROR cannot convert */ type S struct { x int a [10]float32 _ bool } type T struct { x int a [10]float32 _ bool } var ss S var tt T _ = s == ss _ = ss /* ERROR mismatched types */ == tt var u struct { x int a [10]map[string]int } _ = u /* ERROR cannot compare */ == u } func pointers() { // nil _ = nil /* ERROR == not defined */ == nil _ = nil /* ERROR != not defined */ != nil _ = nil /* ERROR < not defined */ < nil _ = nil /* ERROR <= not defined */ <= nil _ = nil /* ERROR > not defined */ > nil _ = nil /* ERROR >= not defined */ >= nil // basics var p, q *int _ = p == q _ = p != q _ = p == nil _ = p != nil _ = nil == q _ = nil != q _ = p /* ERROR < not defined */ < q _ = p /* ERROR <= not defined */ <= q _ = p /* ERROR > not defined */ > q _ = p /* ERROR >= not defined */ >= q // various element types type ( S1 struct{} S2 struct{} P1 *S1 P2 *S2 ) var ( ps1 *S1 ps2 *S2 p1 P1 p2 P2 ) _ = ps1 == ps1 _ = ps1 /* ERROR mismatched types */ == ps2 _ = ps2 /* ERROR mismatched types */ == ps1 _ = p1 == p1 _ = p1 /* ERROR mismatched types */ == p2 _ = p1 == ps1 } func channels() { // basics var c, d chan int _ = c == d _ = c != d _ = c == nil _ = c /* ERROR < not defined */ < d // various element types (named types) type ( C1 chan int C1r <-chan int C1s chan<- int C2 chan float32 ) var ( c1 C1 c1r C1r c1s C1s c1a chan int c2 C2 ) _ = c1 == c1 _ = c1 /* ERROR mismatched types */ == c1r _ = c1 /* ERROR mismatched types */ == c1s _ = c1r /* ERROR mismatched types */ == c1s _ = c1 == c1a _ = c1a == c1 _ = c1 /* ERROR mismatched types */ == c2 _ = c1a /* ERROR mismatched types */ == c2 // various element types (unnamed types) var ( d1 chan int d1r <-chan int d1s chan<- int d1a chan<- int d2 chan float32 ) _ = d1 == d1 _ = d1 == d1r _ = d1 == d1s _ = d1r /* ERROR mismatched types */ == d1s _ = d1 == d1a _ = d1a == d1 _ = d1 /* ERROR mismatched types */ == d2 _ = d1a /* ERROR mismatched types */ == d2 } // for interfaces test type S1 struct{} type S11 struct{} type S2 struct{} func (*S1) m() int func (*S11) m() int func (*S11) n() func (*S2) m() float32 func interfaces() { // basics var i, j interface{ m() int } _ = i == j _ = i != j _ = i == nil _ = i /* ERROR < not defined */ < j // various interfaces var ii interface { m() int; n() } var k interface { m() float32 } _ = i == ii _ = i /* ERROR mismatched types */ == k // interfaces vs values var s1 S1 var s11 S11 var s2 S2 _ = i == 0 /* ERROR cannot convert */ _ = i /* ERROR mismatched types */ == s1 _ = i == &s1 _ = i == &s11 _ = i /* ERROR mismatched types */ == s2 _ = i /* ERROR mismatched types */ == &s2 } func slices() { // basics var s []int _ = s == nil _ = s != nil _ = s /* ERROR < not defined */ < nil // slices are not otherwise comparable _ = s /* ERROR == not defined */ == s _ = s /* ERROR < not defined */ < s } func maps() { // basics var m map[string]int _ = m == nil _ = m != nil _ = m /* ERROR < not defined */ < nil // maps are not otherwise comparable _ = m /* ERROR == not defined */ == m _ = m /* ERROR < not defined */ < m } func funcs() { // basics var f func(int) float32 _ = f == nil _ = f != nil _ = f /* ERROR < not defined */ < nil // funcs are not otherwise comparable _ = f /* ERROR == not defined */ == f _ = f /* ERROR < not defined */ < f } ./go/types/testdata/cycles3.src0000644000014500017510000000125312246613010016134 0ustar michaelstaff// 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 p import "unsafe" var ( _ A = A(nil).a().b().c().d().e().f() _ A = A(nil).b().c().d().e().f() _ A = A(nil).c().d().e().f() _ A = A(nil).d().e().f() _ A = A(nil).e().f() _ A = A(nil).f() _ A = A(nil) ) type ( A interface { a() B B } B interface { b() C C } C interface { c() D D } D interface { d() E E } E interface { e() F F } F interface { f() A } ) type ( U /* ERROR illegal cycle */ interface { V } V interface { v() [unsafe.Sizeof(u)]int } ) var u U ./go/types/testdata/decls0.src0000644000014500017510000001032512246613010015741 0ustar michaelstaff// 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. // type declarations package decls0 import "unsafe" const pi = 3.1415 type ( N undeclared /* ERROR "undeclared" */ B bool I int32 A [10]P T struct { x, y P } P *T R (*R) F func(A) I Y interface { f(A) I } S [](((P))) M map[I]F C chan<- I // blank types must be typechecked _ pi /* ERROR "not a type" */ _ struct{} _ struct{ pi /* ERROR "not a type" */ } ) // declarations of init const _, init /* ERROR "cannot declare init" */ , _ = 0, 1, 2 type init /* ERROR "cannot declare init" */ struct{} var _, init /* ERROR "cannot declare init" */ int func init() {} func init /* ERROR "missing function body" */ () func _() { const init = 0 } func _() { type init int } func _() { var init int; _ = init } // invalid array types type ( iA0 [... /* ERROR "invalid use of '...'" */ ]byte iA1 [1 /* ERROR "invalid array length" */ <<100]int iA2 [- /* ERROR "invalid array length" */ 1]complex128 iA3 ["foo" /* ERROR "must be integer" */ ]string ) type ( p1 pi /* ERROR "no field or method foo" */ .foo p2 unsafe.Pointer ) type ( Pi pi /* ERROR "not a type" */ a /* ERROR "illegal cycle" */ a a /* ERROR "redeclared" */ int // where the cycle error appears depends on the // order in which declarations are processed // (which depends on the order in which a map // is iterated through) b /* ERROR "illegal cycle" */ c c d d e e b t *t U V V *W W U P1 *S2 P2 P1 S0 struct { } S1 struct { a, b, c int u, v, a /* ERROR "redeclared" */ float32 } S2 struct { S0 // anonymous field S0 /* ERROR "redeclared" */ int } S3 struct { x S2 } S4/* ERROR "illegal cycle" */ struct { S4 } S5 /* ERROR "illegal cycle" */ struct { S6 } S6 struct { field S7 } S7 struct { S5 } L1 []L1 L2 []int A1 [10.0]int A2 /* ERROR "illegal cycle" */ [10]A2 A3 /* ERROR "illegal cycle" */ [10]struct { x A4 } A4 [10]A3 F1 func() F2 func(x, y, z float32) F3 func(x, y, x /* ERROR "redeclared" */ float32) F4 func() (x, y, x /* ERROR "redeclared" */ float32) F5 func(x int) (x /* ERROR "redeclared" */ float32) F6 func(x ...int) I1 interface{} I2 interface { m1() } I3 interface { m1() m1 /* ERROR "redeclared" */ () } I4 interface { m1(x, y, x /* ERROR "redeclared" */ float32) m2() (x, y, x /* ERROR "redeclared" */ float32) m3(x int) (x /* ERROR "redeclared" */ float32) } I5 interface { m1(I5) } I6 interface { S0 /* ERROR "not an interface" */ } I7 interface { I1 I1 } I8 /* ERROR "illegal cycle" */ interface { I8 } // Use I09 (rather than I9) because it appears lexically before // I10 so that we get the illegal cycle here rather then in the // declaration of I10. If the implementation sorts by position // rather than name, the error message will still be here. I09 /* ERROR "illegal cycle" */ interface { I10 } I10 interface { I11 } I11 interface { I09 } C1 chan int C2 <-chan int C3 chan<- C3 C4 chan C5 C5 chan C6 C6 chan C4 M1 map[Last]string M2 map[string]M2 Last int ) // cycles in function/method declarations // (test cases for issue 5217 and variants) func f1(x f1 /* ERROR "not a type" */ ) {} func f2(x *f2 /* ERROR "not a type" */ ) {} func f3() (x f3 /* ERROR "not a type" */ ) { return } func f4() (x *f4 /* ERROR "not a type" */ ) { return } func (S0) m1(x S0 /* ERROR "field or method" */ .m1) {} func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {} func (S0) m3() (x S0 /* ERROR "field or method" */ .m3) { return } func (S0) m4() (x *S0 /* ERROR "field or method" */ .m4) { return } // interfaces may have at most one blank method type BlankI interface { _() _ /* ERROR redeclared */ () } // non-interface types may have multiple blank methods type BlankT struct{} func (BlankT) _() {} func (BlankT) _(int) {} func (BlankT) _() int { return 0 } func (BlankT) _(int) int { return 0} // no type can ever satisfy an interface with a _ method func _() { var i BlankI var x BlankT i = x /* ERROR "cannot assign" */ _ = i } // assignability of interfaces with blank methods func _() { var x1, x1b interface { _() } var x2 interface { _() } x1 = x2 _ = x1 x1 = x1b }./go/types/testdata/gotos.src0000644000014500017510000001343112246613010015723 0ustar michaelstaff// 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 a modified copy of $GOROOT/test/goto.go. package gotos var ( i, n int x []int c chan int m map[int]int s string ) // goto after declaration okay func _() { x := 1 goto L L: _ = x } // goto before declaration okay func _() { goto L L: x := 1 _ = x } // goto across declaration not okay func _() { goto L /* ERROR "goto L jumps over variable declaration at line 36" */ x := 1 _ = x L: } // goto across declaration in inner scope okay func _() { goto L { x := 1 _ = x } L: } // goto across declaration after inner scope not okay func _() { goto L /* ERROR "goto L jumps over variable declaration at line 58" */ { x := 1 _ = x } x := 1 _ = x L: } // goto across declaration in reverse okay func _() { L: x := 1 _ = x goto L } func _() { L: L1: x := 1 _ = x goto L goto L1 } // error shows first offending variable func _() { goto L /* ERROR "goto L jumps over variable declaration at line 84" */ x := 1 _ = x y := 1 _ = y L: } // goto not okay even if code path is dead func _() { goto L /* ERROR "goto L jumps over variable declaration" */ x := 1 _ = x y := 1 _ = y return L: } // goto into outer block okay func _() { { goto L } L: } func _() { { goto L goto L1 } L: L1: } // goto backward into outer block okay func _() { L: { goto L } } func _() { L: L1: { goto L goto L1 } } // goto into inner block not okay func _() { goto L /* ERROR "goto L jumps into block" */ { L: } } func _() { goto L /* ERROR "goto L jumps into block" */ goto L1 /* ERROR "goto L1 jumps into block" */ { L: L1: } } // goto backward into inner block still not okay func _() { { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { { L: L1: } goto L /* ERROR "goto L jumps into block" */ goto L1 /* ERROR "goto L1 jumps into block" */ } // error shows first (outermost) offending block func _() { goto L /* ERROR "goto L jumps into block" */ { { { L: } } } } // error prefers block diagnostic over declaration diagnostic func _() { goto L /* ERROR "goto L jumps into block" */ x := 1 _ = x { L: } } // many kinds of blocks, all invalid to jump into or among, // but valid to jump out of // if func _() { L: if true { goto L } } func _() { L: if true { goto L } else { } } func _() { L: if false { } else { goto L } } func _() { goto L /* ERROR "goto L jumps into block" */ if true { L: } } func _() { goto L /* ERROR "goto L jumps into block" */ if true { L: } else { } } func _() { goto L /* ERROR "goto L jumps into block" */ if true { } else { L: } } func _() { if false { L: } else { goto L /* ERROR "goto L jumps into block" */ } } func _() { if true { goto L /* ERROR "goto L jumps into block" */ } else { L: } } func _() { if true { goto L /* ERROR "goto L jumps into block" */ } else if false { L: } } func _() { if true { goto L /* ERROR "goto L jumps into block" */ } else if false { L: } else { } } func _() { if true { goto L /* ERROR "goto L jumps into block" */ } else if false { } else { L: } } func _() { if true { goto L /* ERROR "goto L jumps into block" */ } else { L: } } func _() { if true { L: } else { goto L /* ERROR "goto L jumps into block" */ } } // for func _() { for { goto L } L: } func _() { for { goto L L: } } func _() { for { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { for { goto L L1: } L: goto L1 /* ERROR "goto L1 jumps into block" */ } func _() { for i < n { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { for i = 0; i < n; i++ { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { for i = range x { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { for i = range c { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { for i = range m { L: } goto L /* ERROR "goto L jumps into block" */ } func _() { for i = range s { L: } goto L /* ERROR "goto L jumps into block" */ } // switch func _() { L: switch i { case 0: goto L } } func _() { L: switch i { case 0: default: goto L } } func _() { switch i { case 0: default: L: goto L } } func _() { switch i { case 0: default: goto L L: } } func _() { switch i { case 0: goto L L: ; default: } } func _() { goto L /* ERROR "goto L jumps into block" */ switch i { case 0: L: } } func _() { goto L /* ERROR "goto L jumps into block" */ switch i { case 0: L: ; default: } } func _() { goto L /* ERROR "goto L jumps into block" */ switch i { case 0: default: L: } } func _() { switch i { default: goto L /* ERROR "goto L jumps into block" */ case 0: L: } } func _() { switch i { case 0: L: ; default: goto L /* ERROR "goto L jumps into block" */ } } // select // different from switch. the statement has no implicit block around it. func _() { L: select { case <-c: goto L } } func _() { L: select { case c <- 1: default: goto L } } func _() { select { case <-c: default: L: goto L } } func _() { select { case c <- 1: default: goto L L: } } func _() { select { case <-c: goto L L: ; default: } } func _() { goto L /* ERROR "goto L jumps into block" */ select { case c <- 1: L: } } func _() { goto L /* ERROR "goto L jumps into block" */ select { case c <- 1: L: ; default: } } func _() { goto L /* ERROR "goto L jumps into block" */ select { case <-c: default: L: } } func _() { select { default: goto L /* ERROR "goto L jumps into block" */ case <-c: L: } } func _() { select { case <-c: L: ; default: goto L /* ERROR "goto L jumps into block" */ } } ./go/types/testdata/const1.src0000644000014500017510000001763012246613010016004 0ustar michaelstaff// Copyright 2012 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. // constant conversions package const1 const( mi = ^int(0) mu = ^uint(0) mp = ^uintptr(0) logSizeofInt = uint(mi>>8&1 + mi>>16&1 + mi>>32&1) logSizeofUint = uint(mu>>8&1 + mu>>16&1 + mu>>32&1) logSizeofUintptr = uint(mp>>8&1 + mp>>16&1 + mp>>32&1) ) const ( minInt8 = -1<<(8< 0) _ = assert(smallestFloat64 > 0) ) const ( maxFloat32 = 1<<127 * (1<<24 - 1) / (1.0<<23) maxFloat64 = 1<<1023 * (1<<53 - 1) / (1.0<<52) ) const ( _ int8 = minInt8 /* ERROR "overflows" */ - 1 _ int8 = minInt8 _ int8 = maxInt8 _ int8 = maxInt8 /* ERROR "overflows" */ + 1 _ int8 = smallestFloat64 /* ERROR "truncated" */ _ = int8(minInt8 /* ERROR "cannot convert" */ - 1) _ = int8(minInt8) _ = int8(maxInt8) _ = int8(maxInt8 /* ERROR "cannot convert" */ + 1) _ = int8(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ int16 = minInt16 /* ERROR "overflows" */ - 1 _ int16 = minInt16 _ int16 = maxInt16 _ int16 = maxInt16 /* ERROR "overflows" */ + 1 _ int16 = smallestFloat64 /* ERROR "truncated" */ _ = int16(minInt16 /* ERROR "cannot convert" */ - 1) _ = int16(minInt16) _ = int16(maxInt16) _ = int16(maxInt16 /* ERROR "cannot convert" */ + 1) _ = int16(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ int32 = minInt32 /* ERROR "overflows" */ - 1 _ int32 = minInt32 _ int32 = maxInt32 _ int32 = maxInt32 /* ERROR "overflows" */ + 1 _ int32 = smallestFloat64 /* ERROR "truncated" */ _ = int32(minInt32 /* ERROR "cannot convert" */ - 1) _ = int32(minInt32) _ = int32(maxInt32) _ = int32(maxInt32 /* ERROR "cannot convert" */ + 1) _ = int32(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ int64 = minInt64 /* ERROR "overflows" */ - 1 _ int64 = minInt64 _ int64 = maxInt64 _ int64 = maxInt64 /* ERROR "overflows" */ + 1 _ int64 = smallestFloat64 /* ERROR "truncated" */ _ = int64(minInt64 /* ERROR "cannot convert" */ - 1) _ = int64(minInt64) _ = int64(maxInt64) _ = int64(maxInt64 /* ERROR "cannot convert" */ + 1) _ = int64(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ int = minInt /* ERROR "overflows" */ - 1 _ int = minInt _ int = maxInt _ int = maxInt /* ERROR "overflows" */ + 1 _ int = smallestFloat64 /* ERROR "truncated" */ _ = int(minInt /* ERROR "cannot convert" */ - 1) _ = int(minInt) _ = int(maxInt) _ = int(maxInt /* ERROR "cannot convert" */ + 1) _ = int(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ uint8 = 0 /* ERROR "overflows" */ - 1 _ uint8 = 0 _ uint8 = maxUint8 _ uint8 = maxUint8 /* ERROR "overflows" */ + 1 _ uint8 = smallestFloat64 /* ERROR "truncated" */ _ = uint8(0 /* ERROR "cannot convert" */ - 1) _ = uint8(0) _ = uint8(maxUint8) _ = uint8(maxUint8 /* ERROR "cannot convert" */ + 1) _ = uint8(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ uint16 = 0 /* ERROR "overflows" */ - 1 _ uint16 = 0 _ uint16 = maxUint16 _ uint16 = maxUint16 /* ERROR "overflows" */ + 1 _ uint16 = smallestFloat64 /* ERROR "truncated" */ _ = uint16(0 /* ERROR "cannot convert" */ - 1) _ = uint16(0) _ = uint16(maxUint16) _ = uint16(maxUint16 /* ERROR "cannot convert" */ + 1) _ = uint16(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ uint32 = 0 /* ERROR "overflows" */ - 1 _ uint32 = 0 _ uint32 = maxUint32 _ uint32 = maxUint32 /* ERROR "overflows" */ + 1 _ uint32 = smallestFloat64 /* ERROR "truncated" */ _ = uint32(0 /* ERROR "cannot convert" */ - 1) _ = uint32(0) _ = uint32(maxUint32) _ = uint32(maxUint32 /* ERROR "cannot convert" */ + 1) _ = uint32(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ uint64 = 0 /* ERROR "overflows" */ - 1 _ uint64 = 0 _ uint64 = maxUint64 _ uint64 = maxUint64 /* ERROR "overflows" */ + 1 _ uint64 = smallestFloat64 /* ERROR "truncated" */ _ = uint64(0 /* ERROR "cannot convert" */ - 1) _ = uint64(0) _ = uint64(maxUint64) _ = uint64(maxUint64 /* ERROR "cannot convert" */ + 1) _ = uint64(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ uint = 0 /* ERROR "overflows" */ - 1 _ uint = 0 _ uint = maxUint _ uint = maxUint /* ERROR "overflows" */ + 1 _ uint = smallestFloat64 /* ERROR "truncated" */ _ = uint(0 /* ERROR "cannot convert" */ - 1) _ = uint(0) _ = uint(maxUint) _ = uint(maxUint /* ERROR "cannot convert" */ + 1) _ = uint(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ uintptr = 0 /* ERROR "overflows" */ - 1 _ uintptr = 0 _ uintptr = maxUintptr _ uintptr = maxUintptr /* ERROR "overflows" */ + 1 _ uintptr = smallestFloat64 /* ERROR "truncated" */ _ = uintptr(0 /* ERROR "cannot convert" */ - 1) _ = uintptr(0) _ = uintptr(maxUintptr) _ = uintptr(maxUintptr /* ERROR "cannot convert" */ + 1) _ = uintptr(smallestFloat64 /* ERROR "cannot convert" */) ) const ( _ float32 = minInt64 _ float64 = minInt64 _ complex64 = minInt64 _ complex128 = minInt64 _ = float32(minInt64) _ = float64(minInt64) _ = complex64(minInt64) _ = complex128(minInt64) ) const ( _ float32 = maxUint64 _ float64 = maxUint64 _ complex64 = maxUint64 _ complex128 = maxUint64 _ = float32(maxUint64) _ = float64(maxUint64) _ = complex64(maxUint64) _ = complex128(maxUint64) ) // TODO(gri) find smaller deltas below const delta32 = maxFloat32/(1 << 23) const ( _ float32 = - /* ERROR "overflow" */ (maxFloat32 + delta32) _ float32 = -maxFloat32 _ float32 = maxFloat32 _ float32 = maxFloat32 /* ERROR "overflow" */ + delta32 _ = float32(- /* ERROR "cannot convert" */ (maxFloat32 + delta32)) _ = float32(-maxFloat32) _ = float32(maxFloat32) _ = float32(maxFloat32 /* ERROR "cannot convert" */ + delta32) _ = assert(float32(smallestFloat32) == smallestFloat32) _ = assert(float32(smallestFloat32/2) == 0) _ = assert(float32(smallestFloat64) == 0) _ = assert(float32(smallestFloat64/2) == 0) ) const delta64 = maxFloat64/(1 << 52) const ( _ float64 = - /* ERROR "overflow" */ (maxFloat64 + delta64) _ float64 = -maxFloat64 _ float64 = maxFloat64 _ float64 = maxFloat64 /* ERROR "overflow" */ + delta64 _ = float64(- /* ERROR "cannot convert" */ (maxFloat64 + delta64)) _ = float64(-maxFloat64) _ = float64(maxFloat64) _ = float64(maxFloat64 /* ERROR "cannot convert" */ + delta64) _ = assert(float64(smallestFloat32) == smallestFloat32) _ = assert(float64(smallestFloat32/2) == smallestFloat32/2) _ = assert(float64(smallestFloat64) == smallestFloat64) _ = assert(float64(smallestFloat64/2) == 0) ) const ( _ complex64 = - /* ERROR "overflow" */ (maxFloat32 + delta32) _ complex64 = -maxFloat32 _ complex64 = maxFloat32 _ complex64 = maxFloat32 /* ERROR "overflow" */ + delta32 _ = complex64(- /* ERROR "cannot convert" */ (maxFloat32 + delta32)) _ = complex64(-maxFloat32) _ = complex64(maxFloat32) _ = complex64(maxFloat32 /* ERROR "cannot convert" */ + delta32) ) const ( _ complex128 = - /* ERROR "overflow" */ (maxFloat64 + delta64) _ complex128 = -maxFloat64 _ complex128 = maxFloat64 _ complex128 = maxFloat64 /* ERROR "overflow" */ + delta64 _ = complex128(- /* ERROR "cannot convert" */ (maxFloat64 + delta64)) _ = complex128(-maxFloat64) _ = complex128(maxFloat64) _ = complex128(maxFloat64 /* ERROR "cannot convert" */ + delta64) ) // Initialization of typed constant and conversion are the same: const ( f32 = 1 + smallestFloat32 x32 float32 = f32 y32 = float32(f32) _ = assert(x32 - y32 == 0) ) const ( f64 = 1 + smallestFloat64 x64 float64 = f64 y64 = float64(f64) _ = assert(x64 - y64 == 0) ) ./go/types/testdata/cycles4.src0000644000014500017510000000240312246613010016133 0ustar michaelstaff// 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 p // Check that all methods of T are collected before // determining the result type of m (which embeds // all methods of T). type T interface { m() interface {T} E } var _ = T.m(nil).m().e() type E interface { e() int } // Check that unresolved forward chains are followed // (see also comment in resolver.go, checker.typeDecl). var _ = C.m(nil).m().e() type A B type B interface { m() interface{C} E } type C A // Check that interface type comparison for identity // does not recur endlessly. type T1 interface { m() interface{T1} } type T2 interface { m() interface{T2} } func _(x T1, y T2) { // Checking for assignability of interfaces must check // if all methods of x are present in y, and that they // have identical signatures. The signatures recur via // the result type, which is an interface that embeds // a single method m that refers to the very interface // that contains it. This requires cycle detection in // identity checks for interface types. x = y } type T3 interface { m() interface{T4} } type T4 interface { m() interface{T3} } func _(x T1, y T3) { x = y } ./go/types/testdata/vardecl.src0000644000014500017510000000662112246613010016213 0ustar michaelstaff// 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 vardecl // Prerequisites. func f() {} func g() (x, y int) { return } var m map[string]int // Var decls must have a type or an initializer. var _ int var _, _ int var _ /* ERROR "missing type or init expr" */ var _ /* ERROR "missing type or init expr" */, _ var _ /* ERROR "missing type or init expr" */, _, _ // The initializer must be an expression. var _ = int /* ERROR "not an expression" */ var _ = f /* ERROR "used as value" */ () // Identifier and expression arity must match. var _, _ = 1, 2 var _ = 1, 2 /* ERROR "extra init expr 2" */ var _, _ = 1 /* ERROR "assignment count mismatch" */ var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 var _ = g /* ERROR "2-valued expr" */ () var _, _ = g() var _, _, _ = g /* ERROR "assignment count mismatch" */ () var _ = m["foo"] var _, _ = m["foo"] var _, _, _ = m /* ERROR "assignment count mismatch" */ ["foo"] var _, _ int = 1, 2 var _ int = 1, 2 /* ERROR "extra init expr 2" */ var _, _ int = 1 /* ERROR "assignment count mismatch" */ var _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 var ( _, _ = 1, 2 _ = 1, 2 /* ERROR "extra init expr 2" */ _, _ = 1 /* ERROR "assignment count mismatch" */ _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 _ = g /* ERROR "2-valued expr" */ () _, _ = g() _, _, _ = g /* ERROR "assignment count mismatch" */ () _ = m["foo"] _, _ = m["foo"] _, _, _ = m /* ERROR "assignment count mismatch" */ ["foo"] _, _ int = 1, 2 _ int = 1, 2 /* ERROR "extra init expr 2" */ _, _ int = 1 /* ERROR "assignment count mismatch" */ _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 ) // Variables declared in function bodies must be 'used'. type T struct{} func (r T) _(a, b, c int) (u, v, w int) { var x1 /* ERROR "declared but not used" */ int var x2 /* ERROR "declared but not used" */ int x1 = 1 (x2) = 2 y1 /* ERROR "declared but not used" */ := 1 y2 /* ERROR "declared but not used" */ := 2 y1 = 1 (y1) = 2 { var x1 /* ERROR "declared but not used" */ int var x2 /* ERROR "declared but not used" */ int x1 = 1 (x2) = 2 y1 /* ERROR "declared but not used" */ := 1 y2 /* ERROR "declared but not used" */ := 2 y1 = 1 (y1) = 2 } if x /* ERROR "declared but not used" */ := 0; a < b {} switch x /* ERROR "declared but not used" */, y := 0, 1; a { case 0: _ = y case 1: x /* ERROR "declared but not used" */ := 0 } var t interface{} switch t /* ERROR "declared but not used" */ := t.(type) {} switch t /* ERROR "declared but not used" */ := t.(type) { case int: } switch t /* ERROR "declared but not used" */ := t.(type) { case int: case float32, complex64: t = nil } switch t := t.(type) { case int: case float32, complex64: _ = t } switch t := t.(type) { case int: case float32: case string: _ = func() string { return t } } switch t := t; t /* ERROR "declared but not used" */ := t.(type) {} var z1 /* ERROR "declared but not used" */ int var z2 int _ = func(a, b, c int) (u, v, w int) { z1 = a (z1) = b a = z2 return } var s []int var i /* ERROR "declared but not used" */ , j int for i, j = range s { _ = j } for i, j /* ERROR "declared but not used" */ := range s { _ = func() int { return i } } return } // TODO(gri) consolidate other var decl checks in this file./go/types/testdata/constdecl.src0000644000014500017510000000346012246613010016547 0ustar michaelstaff// 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 constdecl import "math" var v int // Const decls must be initialized by constants. const _ = v /* ERROR "not constant" */ const _ = math /* ERROR "not constant" */ .Sin(0) const _ = int /* ERROR "not an expression" */ func _() { const _ = v /* ERROR "not constant" */ const _ = math /* ERROR "not constant" */ .Sin(0) const _ = int /* ERROR "not an expression" */ } // Identifier and expression arity must match. const _ /* ERROR "missing init expr for _" */ const _ = 1, 2 /* ERROR "extra init expr 2" */ const _ /* ERROR "missing init expr for _" */ int const _ int = 1, 2 /* ERROR "extra init expr 2" */ const ( _ /* ERROR "missing init expr for _" */ _ = 1, 2 /* ERROR "extra init expr 2" */ _ /* ERROR "missing init expr for _" */ int _ int = 1, 2 /* ERROR "extra init expr 2" */ ) const ( _ = 1 _ _, _ /* ERROR "missing init expr for _" */ _ ) const ( _, _ = 1, 2 _, _ _ /* ERROR "extra init expr at" */ _, _ _, _, _ /* ERROR "missing init expr for _" */ _, _ ) func _() { const _ /* ERROR "missing init expr for _" */ const _ = 1, 2 /* ERROR "extra init expr 2" */ const _ /* ERROR "missing init expr for _" */ int const _ int = 1, 2 /* ERROR "extra init expr 2" */ const ( _ /* ERROR "missing init expr for _" */ _ = 1, 2 /* ERROR "extra init expr 2" */ _ /* ERROR "missing init expr for _" */ int _ int = 1, 2 /* ERROR "extra init expr 2" */ ) const ( _ = 1 _ _, _ /* ERROR "missing init expr for _" */ _ ) const ( _, _ = 1, 2 _, _ _ /* ERROR "extra init expr at" */ _, _ _, _, _ /* ERROR "missing init expr for _" */ _, _ ) } // TODO(gri) move extra tests from testdata/const0.src into here./go/types/testdata/expr3.src0000644000014500017510000002643712246613010015643 0ustar michaelstaff// Copyright 2012 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 expr3 func indexes() { _ = 1 /* ERROR "cannot index" */ [0] _ = indexes /* ERROR "cannot index" */ [0] _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2] var a [10]int _ = a[true /* ERROR "cannot convert" */ ] _ = a["foo" /* ERROR "cannot convert" */ ] _ = a[1.1 /* ERROR "truncated" */ ] _ = a[1.0] _ = a[- /* ERROR "negative" */ 1] _ = a[- /* ERROR "negative" */ 1 :] _ = a[: - /* ERROR "negative" */ 1] _ = a[::] /* ERROR "3-index slice" */ _ = a[0::] /* ERROR "3-index slice" */ _ = a[0::10] /* ERROR "3-index slice" */ _ = a[:10:10] var a0 int a0 = a[0] _ = a0 var a1 int32 a1 = a /* ERROR "cannot assign" */ [1] _ = a1 _ = a[9] _ = a[10 /* ERROR "index .* out of bounds" */ ] _ = a[1 /* ERROR "overflows" */ <<100] _ = a[10:] _ = a[:10] _ = a[10:10] _ = a[11 /* ERROR "index .* out of bounds" */ :] _ = a[: 11 /* ERROR "index .* out of bounds" */ ] _ = a[: 1 /* ERROR "overflows" */ <<100] _ = a[:10:10] _ = a[:11 /* ERROR "index .* out of bounds" */ :10] _ = a[:10:11 /* ERROR "index .* out of bounds" */ ] _ = a[10:0:10] /* ERROR "invalid slice indices" */ _ = a[0:10:0] /* ERROR "invalid slice indices" */ _ = a[10:0:0] /* ERROR "invalid slice indices" */ pa := &a _ = pa[9] _ = pa[10 /* ERROR "index .* out of bounds" */ ] _ = pa[1 /* ERROR "overflows" */ <<100] _ = pa[10:] _ = pa[:10] _ = pa[10:10] _ = pa[11 /* ERROR "index .* out of bounds" */ :] _ = pa[: 11 /* ERROR "index .* out of bounds" */ ] _ = pa[: 1 /* ERROR "overflows" */ <<100] _ = pa[:10:10] _ = pa[:11 /* ERROR "index .* out of bounds" */ :10] _ = pa[:10:11 /* ERROR "index .* out of bounds" */ ] _ = pa[10:0:10] /* ERROR "invalid slice indices" */ _ = pa[0:10:0] /* ERROR "invalid slice indices" */ _ = pa[10:0:0] /* ERROR "invalid slice indices" */ var b [0]int _ = b[0 /* ERROR "index .* out of bounds" */ ] _ = b[:] _ = b[0:] _ = b[:0] _ = b[0:0] _ = b[0:0:0] _ = b[1 /* ERROR "index .* out of bounds" */ :0:0] var s []int _ = s[- /* ERROR "negative" */ 1] _ = s[- /* ERROR "negative" */ 1 :] _ = s[: - /* ERROR "negative" */ 1] _ = s[0] _ = s[1:2] _ = s[2:1] /* ERROR "invalid slice indices" */ _ = s[2:] _ = s[: 1 /* ERROR "overflows" */ <<100] _ = s[1 /* ERROR "overflows" */ <<100 :] _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100] _ = s[::] /* ERROR "3-index slice" */ _ = s[:10:10] _ = s[10:0:10] /* ERROR "invalid slice indices" */ _ = s[0:10:0] /* ERROR "invalid slice indices" */ _ = s[10:0:0] /* ERROR "invalid slice indices" */ var t string _ = t[- /* ERROR "negative" */ 1] _ = t[- /* ERROR "negative" */ 1 :] _ = t[: - /* ERROR "negative" */ 1] _ = t /* ERROR "3-index slice of string" */ [1:2:3] _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3] var t0 byte t0 = t[0] _ = t0 var t1 rune t1 = t /* ERROR "cannot assign" */ [2] _ = t1 _ = ("foo" + "bar")[5] _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ] const c = "foo" _ = c[- /* ERROR "negative" */ 1] _ = c[- /* ERROR "negative" */ 1 :] _ = c[: - /* ERROR "negative" */ 1] var c0 byte c0 = c[0] _ = c0 var c2 float32 c2 = c /* ERROR "cannot assign" */ [2] _ = c[3 /* ERROR "index .* out of bounds" */ ] _ = ""[0 /* ERROR "index .* out of bounds" */ ] _ = c2 _ = s[1<<30] // no compile-time error here // issue 4913 type mystring string var ss string var ms mystring var i, j int ss = "foo"[1:2] ss = "foo"[i:j] ms = "foo" /* ERROR "cannot assign" */ [1:2] ms = "foo" /* ERROR "cannot assign" */ [i:j] _, _ = ss, ms } type T struct { x int y func() } func (*T) m() {} func method_expressions() { _ = T /* ERROR "no field or method" */ .a _ = T /* ERROR "has no method" */ .x _ = T /* ERROR "not in method set" */ .m _ = (*T).m var f func(*T) = T /* ERROR "not in method set" */ .m var g func(*T) = (*T).m _, _ = f, g _ = T /* ERROR "has no method" */ .y _ = ( /* ERROR "has no method" */ *T).y } func struct_literals() { type T0 struct { a, b, c int } type T1 struct { T0 a, b int u float64 s string } // keyed elements _ = T1{} _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ } _ = T1{aa /* ERROR "unknown field" */ : 0} _ = T1{1 /* ERROR "invalid field name" */ : 0} _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10} _ = T1{a: "foo" /* ERROR "cannot convert" */ } _ = T1{c /* ERROR "unknown field" */ : 0} _ = T1{T0: { /* ERROR "missing type" */ }} _ = T1{T0: T0{}} _ = T1{T0 /* ERROR "invalid field name" */ .a: 0} // unkeyed elements _ = T0{1, 2, 3} _ = T0{1, b /* ERROR "mixture" */ : 2, 3} _ = T0{1, 2} /* ERROR "too few values" */ _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ } _ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "truncated" */} // invalid type type P *struct{ x int } _ = P /* ERROR "invalid composite literal type" */ {} } func array_literals() { type A0 [0]int _ = A0{} _ = A0{0 /* ERROR "index .* out of bounds" */} _ = A0{0 /* ERROR "index .* out of bounds" */ : 0} type A1 [10]int _ = A1{} _ = A1{0, 1, 2} _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ } _ = A1{- /* ERROR "negative" */ 1: 0} _ = A1{8: 8, 9} _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ } _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} _ = A1{5: 5, 6, 7, 3: 3, 4} _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10} _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} _ = A1{2.0} _ = A1{2.1 /* ERROR "truncated" */ } _ = A1{"foo" /* ERROR "cannot convert" */ } // indices must be integer constants i := 1 const f = 2.1 const s = "foo" _ = A1{i /* ERROR "index i must be integer constant" */ : 0} _ = A1{f /* ERROR "truncated" */ : 0} _ = A1{s /* ERROR "cannot convert" */ : 0} a0 := [...]int{} assert(len(a0) == 0) a1 := [...]int{0, 1, 2} assert(len(a1) == 3) var a13 [3]int var a14 [4]int a13 = a1 a14 = a1 /* ERROR "cannot assign" */ _, _ = a13, a14 a2 := [...]int{- /* ERROR "negative" */ 1: 0} _ = a2 a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} assert(len(a3) == 5) // somewhat arbitrary a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14} assert(len(a4) == 1024) // from the spec type Point struct { x, y float32 } _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}} _ = [...]Point{{1.5, -3.5}, {0, 0}} _ = [][]int{[]int{1, 2, 3}, []int{4, 5}} _ = [][]int{{1, 2, 3}, {4, 5}} _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}} _ = [...]*Point{{1.5, -3.5}, {0, 0}} } func slice_literals() { type S0 []int _ = S0{} _ = S0{0, 1, 2} _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} _ = S0{- /* ERROR "negative" */ 1: 0} _ = S0{8: 8, 9} _ = S0{8: 8, 9, 10} _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} _ = S0{5: 5, 6, 7, 3: 3, 4} _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10} _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} _ = S0{2.0} _ = S0{2.1 /* ERROR "truncated" */ } _ = S0{"foo" /* ERROR "cannot convert" */ } // indices must be resolved correctly const index1 = 1 _ = S0{index1: 1} _ = S0{index2: 2} _ = S0{index3 /* ERROR "undeclared name" */ : 3} // indices must be integer constants i := 1 const f = 2.1 const s = "foo" _ = S0{i /* ERROR "index i must be integer constant" */ : 0} _ = S0{f /* ERROR "truncated" */ : 0} _ = S0{s /* ERROR "cannot convert" */ : 0} } const index2 int = 2 func map_literals() { type M0 map[string]int type M1 map[bool]int type M2 map[*int]int _ = M0{} _ = M0{1 /* ERROR "missing key" */ } _ = M0{1 /* ERROR "cannot convert" */ : 2} _ = M0{"foo": "bar" /* ERROR "cannot convert" */ } _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } // map keys must be resolved correctly key1 := "foo" _ = M0{key1: 1} _ = M0{key2: 2} _ = M0{key3 /* ERROR "undeclared name" */ : 2} var value int _ = M1{true: 1, false: 0} _ = M2{nil: 0, &value: 1} } var key2 string = "bar" type I interface { m() } type I2 interface { m(int) } type T1 struct{} type T2 struct{} func (T2) m(int) {} func type_asserts() { var x int _ = x /* ERROR "not an interface" */ .(int) var e interface{} var ok bool x, ok = e.(int) _ = ok var t I _ = t /* ERROR "use of .* outside type switch" */ .(type) _ = t /* ERROR "missing method m" */ .(T) _ = t.(*T) _ = t /* ERROR "missing method m" */ .(T1) _ = t /* ERROR "wrong type for method m" */ .(T2) _ = t /* ERROR "wrong type for method m" */ .(I2) // e doesn't statically have an m, but may have one dynamically. _ = e.(I2) } func f0() {} func f1(x int) {} func f2(u float32, s string) {} func fs(s []byte) {} func fv(x ...int) {} func fi(x ... interface{}) {} func (T) fm(x ...int) func g0() {} func g1() int { return 0} func g2() (u float32, s string) { return } func gs() []byte { return nil } func _calls() { var x int var y float32 var s []int f0() _ = f0 /* ERROR "used as value" */ () f0(g0 /* ERROR "too many arguments" */ ) f1(0) f1(x) f1(10.0) f1() /* ERROR "too few arguments" */ f1(x, y /* ERROR "too many arguments" */ ) f1(s /* ERROR "cannot pass" */ ) f1(x ... /* ERROR "cannot use ..." */ ) f1(g0 /* ERROR "used as value" */ ()) f1(g1()) // f1(g2()) // TODO(gri) missing position in error message f2() /* ERROR "too few arguments" */ f2(3.14) /* ERROR "too few arguments" */ f2(3.14, "foo") f2(x /* ERROR "cannot pass" */ , "foo") f2(g0 /* ERROR "used as value" */ ()) /* ERROR "too few arguments" */ f2(g1 /* ERROR "cannot pass" */ ()) /* ERROR "too few arguments" */ f2(g2()) fs() /* ERROR "too few arguments" */ fs(g0 /* ERROR "used as value" */ ()) fs(g1 /* ERROR "cannot pass" */ ()) fs(g2 /* ERROR "cannot pass" */ /* ERROR "too many arguments" */ ()) fs(gs()) fv() fv(1, 2.0, x) fv(s /* ERROR "cannot pass" */ ) fv(s...) fv(x /* ERROR "cannot use" */ ...) fv(1, s /* ERROR "can only use ... with matching parameter" */ ...) fv(gs /* ERROR "cannot pass" */ ()) fv(gs /* ERROR "cannot pass" */ ()...) var t T t.fm() t.fm(1, 2.0, x) t.fm(s /* ERROR "cannot pass" */ ) t.fm(g1()) t.fm(1, s /* ERROR "can only use ... with matching parameter" */ ...) t.fm(gs /* ERROR "cannot pass" */ ()) t.fm(gs /* ERROR "cannot pass" */ ()...) T.fm(t, ) T.fm(t, 1, 2.0, x) T.fm(t, s /* ERROR "cannot pass" */ ) T.fm(t, g1()) T.fm(t, 1, s /* ERROR "can only use ... with matching parameter" */ ...) T.fm(t, gs /* ERROR "cannot pass" */ ()) T.fm(t, gs /* ERROR "cannot pass" */ ()...) var i interface{ fm(x ...int) } = t i.fm() i.fm(1, 2.0, x) i.fm(s /* ERROR "cannot pass" */ ) i.fm(g1()) i.fm(1, s /* ERROR "can only use ... with matching parameter" */ ...) i.fm(gs /* ERROR "cannot pass" */ ()) i.fm(gs /* ERROR "cannot pass" */ ()...) fi() fi(1, 2.0, x, 3.14, "foo") fi(g2()) fi(0, g2) fi(0, g2 /* ERROR "2-valued expression" */ ()) } func issue6344() { type T []interface{} var x T fi(x...) // ... applies also to named slices } ./go/types/testdata/const0.src0000644000014500017510000001506712246613010016005 0ustar michaelstaff// Copyright 2012 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. // constant declarations package const0 // constants declarations must be initialized by constants var x = 0 const c0 = x /* ERROR "not constant" */ // typed constants must have constant types const _ interface /* ERROR invalid constant type */ {} = 0 func _ () { const _ interface /* ERROR invalid constant type */ {} = 0 for i := 0; i < 10; i++ {} // don't crash with non-nil iota here } // untyped constants const ( // boolean values ub0 = false ub1 = true ub2 = 2 < 1 ub3 = ui1 == uf1 ub4 = true /* ERROR "cannot convert" */ == 0 // integer values ui0 = 0 ui1 = 1 ui2 = 42 ui3 = 3141592653589793238462643383279502884197169399375105820974944592307816406286 ui4 = -10 ui5 = ui0 + ui1 ui6 = ui1 - ui1 ui7 = ui2 * ui1 ui8 = ui3 / ui3 ui9 = ui3 % ui3 ui10 = 1 / 0 /* ERROR "division by zero" */ ui11 = ui1 / 0 /* ERROR "division by zero" */ ui12 = ui3 / ui0 /* ERROR "division by zero" */ ui13 = 1 % 0 /* ERROR "division by zero" */ ui14 = ui1 % 0 /* ERROR "division by zero" */ ui15 = ui3 % ui0 /* ERROR "division by zero" */ ui16 = ui2 & ui3 ui17 = ui2 | ui3 ui18 = ui2 ^ ui3 ui19 = 1 /* ERROR "invalid operation" */ % 1.0 // floating point values uf0 = 0. uf1 = 1. uf2 = 4.2e1 uf3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286 uf4 = 1e-1 uf5 = uf0 + uf1 uf6 = uf1 - uf1 uf7 = uf2 * uf1 uf8 = uf3 / uf3 uf9 = uf3 /* ERROR "not defined" */ % uf3 uf10 = 1 / 0 /* ERROR "division by zero" */ uf11 = uf1 / 0 /* ERROR "division by zero" */ uf12 = uf3 / uf0 /* ERROR "division by zero" */ uf16 = uf2 /* ERROR "not defined" */ & uf3 uf17 = uf2 /* ERROR "not defined" */ | uf3 uf18 = uf2 /* ERROR "not defined" */ ^ uf3 // complex values uc0 = 0.i uc1 = 1.i uc2 = 4.2e1i uc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i uc4 = 1e-1i uc5 = uc0 + uc1 uc6 = uc1 - uc1 uc7 = uc2 * uc1 uc8 = uc3 / uc3 uc9 = uc3 /* ERROR "not defined" */ % uc3 uc10 = 1 / 0 /* ERROR "division by zero" */ uc11 = uc1 / 0 /* ERROR "division by zero" */ uc12 = uc3 / uc0 /* ERROR "division by zero" */ uc16 = uc2 /* ERROR "not defined" */ & uc3 uc17 = uc2 /* ERROR "not defined" */ | uc3 uc18 = uc2 /* ERROR "not defined" */ ^ uc3 ) type ( mybool bool myint int myfloat float64 mycomplex complex128 ) // typed constants const ( // boolean values tb0 bool = false tb1 bool = true tb2 mybool = 2 < 1 tb3 mybool = ti1 /* ERROR "mismatched types" */ == tf1 // integer values ti0 int8 = ui0 ti1 int32 = ui1 ti2 int64 = ui2 ti3 myint = ui3 /* ERROR "overflows" */ ti4 myint = ui4 ti5 = ti0 /* ERROR "mismatched types" */ + ti1 ti6 = ti1 - ti1 ti7 = ti2 /* ERROR "mismatched types" */ * ti1 ti8 = ti3 / ti3 ti9 = ti3 % ti3 ti10 = 1 / 0 /* ERROR "division by zero" */ ti11 = ti1 / 0 /* ERROR "division by zero" */ ti12 = ti3 /* ERROR "mismatched types" */ / ti0 ti13 = 1 % 0 /* ERROR "division by zero" */ ti14 = ti1 % 0 /* ERROR "division by zero" */ ti15 = ti3 /* ERROR "mismatched types" */ % ti0 ti16 = ti2 /* ERROR "mismatched types" */ & ti3 ti17 = ti2 /* ERROR "mismatched types" */ | ti4 ti18 = ti2 ^ ti5 // no mismatched types error because the type of ti5 is unknown // floating point values tf0 float32 = 0. tf1 float32 = 1. tf2 float64 = 4.2e1 tf3 myfloat = 3.141592653589793238462643383279502884197169399375105820974944592307816406286 tf4 myfloat = 1e-1 tf5 = tf0 + tf1 tf6 = tf1 - tf1 tf7 = tf2 /* ERROR "mismatched types" */ * tf1 tf8 = tf3 / tf3 tf9 = tf3 /* ERROR "not defined" */ % tf3 tf10 = 1 / 0 /* ERROR "division by zero" */ tf11 = tf1 / 0 /* ERROR "division by zero" */ tf12 = tf3 /* ERROR "mismatched types" */ / tf0 tf16 = tf2 /* ERROR "mismatched types" */ & tf3 tf17 = tf2 /* ERROR "mismatched types" */ | tf3 tf18 = tf2 /* ERROR "mismatched types" */ ^ tf3 // complex values tc0 = 0.i tc1 = 1.i tc2 = 4.2e1i tc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i tc4 = 1e-1i tc5 = tc0 + tc1 tc6 = tc1 - tc1 tc7 = tc2 * tc1 tc8 = tc3 / tc3 tc9 = tc3 /* ERROR "not defined" */ % tc3 tc10 = 1 / 0 /* ERROR "division by zero" */ tc11 = tc1 / 0 /* ERROR "division by zero" */ tc12 = tc3 / tc0 /* ERROR "division by zero" */ tc16 = tc2 /* ERROR "not defined" */ & tc3 tc17 = tc2 /* ERROR "not defined" */ | tc3 tc18 = tc2 /* ERROR "not defined" */ ^ tc3 ) // initialization cycles const ( a /* ERROR "cycle" */ = a b /* ERROR "cycle" */ , c /* ERROR "cycle" */, d, e = e, d, c, b // TODO(gri) should only have one cycle error f float64 = d ) // multiple initialization const ( a1, a2, a3 = 7, 3.1415926, "foo" b1, b2, b3 = b3, b1, 42 c1, c2, c3 /* ERROR "missing init expr for c3" */ = 1, 2 d1, d2, d3 = 1, 2, 3, 4 /* ERROR "extra init expr 4" */ _p0 = assert(a1 == 7) _p1 = assert(a2 == 3.1415926) _p2 = assert(a3 == "foo") _p3 = assert(b1 == 42) _p4 = assert(b2 == 42) _p5 = assert(b3 == 42) ) func _() { const ( a1, a2, a3 = 7, 3.1415926, "foo" b1, b2, b3 = b3, b1, 42 c1, c2, c3 /* ERROR "missing init expr for c3" */ = 1, 2 d1, d2, d3 = 1, 2, 3, 4 /* ERROR "extra init expr 4" */ _p0 = assert(a1 == 7) _p1 = assert(a2 == 3.1415926) _p2 = assert(a3 == "foo") _p3 = assert(b1 == 42) _p4 = assert(b2 == 42) _p5 = assert(b3 == 42) ) } // iota const ( iota0 = iota iota1 = iota iota2 = iota*2 _a0 = assert(iota0 == 0) _a1 = assert(iota1 == 1) _a2 = assert(iota2 == 4) iota6 = iota*3 iota7 iota8 _a3 = assert(iota7 == 21) _a4 = assert(iota8 == 24) ) const ( _b0 = iota _b1 = assert(iota + iota2 == 5) _b2 = len([iota]int{}) // iota may appear in a type! _b3 = assert(_b2 == 2) _b4 = len(A /* ERROR "invalid composite literal" */ {}) ) type A [iota /* ERROR "cannot use iota" */ ]int // constant expressions with operands accross different // constant declarations must use the right iota values const ( _c0 = iota _c1 _c2 _x = _c2 + _d1 + _e0 // 3 ) const ( _d0 = iota _d1 ) const ( _e0 = iota ) var _ = assert(_x == 3) // special cases const ( _n0 = nil /* ERROR "not constant" */ _n1 = [ /* ERROR "not constant" */ ]int{} ) // iotas must not be usable in expressions outside constant declarations type _ [iota /* ERROR "iota outside constant decl" */ ]byte var _ = iota /* ERROR "iota outside constant decl" */ func _() { _ = iota /* ERROR "iota outside constant decl" */ const _ = iota _ = iota /* ERROR "iota outside constant decl" */ } func _() { iota := 123 const x = iota /* ERROR "is not constant" */ var y = iota _ = y } ./go/types/testdata/decls3.src0000644000014500017510000001020112246613010015735 0ustar michaelstaff// Copyright 2012 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. // embedded types package decls3 import "unsafe" import "fmt" // fields with the same name at the same level cancel each other out func _() { type ( T1 struct { X int } T2 struct { X int } T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X ) var t T3 _ = t /* ERROR "ambiguous selector" */ .X } func _() { type ( T1 struct { X int } T2 struct { T1 } T3 struct { T1 } T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X ) var t T4 _ = t /* ERROR "ambiguous selector" */ .X } func issue4355() { type ( T1 struct {X int} T2 struct {T1} T3 struct {T2} T4 struct {T2} T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X ) var t T5 _ = t /* ERROR "ambiguous selector" */ .X } func _() { type State int type A struct{ State } type B struct{ fmt.State } type T struct{ A; B } var t T _ = t /* ERROR "ambiguous selector" */ .State } // Embedded fields can be predeclared types. func _() { type T0 struct{ int float32 f int } var x T0 _ = x.int _ = x.float32 _ = x.f type T1 struct{ T0 } var y T1 _ = y.int _ = y.float32 _ = y.f } // Restrictions on embedded field types. func _() { type I1 interface{} type I2 interface{} type P1 *int type P2 *int type UP unsafe.Pointer type T1 struct { I1 * /* ERROR "cannot be a pointer to an interface" */ I2 * /* ERROR "cannot be a pointer to an interface" */ error P1 /* ERROR "cannot be a pointer" */ * /* ERROR "cannot be a pointer" */ P2 } // unsafe.Pointers are treated like regular pointers when embedded type T2 struct { unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer */* ERROR "cannot be unsafe.Pointer" */ unsafe.Pointer UP /* ERROR "cannot be unsafe.Pointer" */ * /* ERROR "cannot be unsafe.Pointer" */ UP } } // Named types that are pointers. type S struct{ x int } func (*S) m() {} type P *S func _() { var s *S _ = s.x _ = s.m var p P _ = p.x _ = p /* ERROR "no field or method" */ .m _ = P /* ERROR "no field or method" */ .m } // Borrowed from the FieldByName test cases in reflect/all_test.go. type D1 struct { d int } type D2 struct { d int } type S0 struct { A, B, C int D1 D2 } type S1 struct { B int S0 } type S2 struct { A int *S1 } type S1x struct { S1 } type S1y struct { S1 } type S3 struct { S1x S2 D, E int *S1y } type S4 struct { *S4 A int } // The X in S6 and S7 annihilate, but they also block the X in S8.S9. type S5 struct { S6 S7 S8 } type S6 struct { X int } type S7 S6 type S8 struct { S9 } type S9 struct { X int Y int } // The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. type S10 struct { S11 S12 S13 } type S11 struct { S6 } type S12 struct { S6 } type S13 struct { S8 } func _() { _ = struct /* ERROR "no field or method" */ {}{}.Foo _ = S0{}.A _ = S0 /* ERROR "no field or method" */ {}.D _ = S1{}.A _ = S1{}.B _ = S1{}.S0 _ = S1{}.C _ = S2{}.A _ = S2{}.S1 _ = S2{}.B _ = S2{}.C _ = S2 /* ERROR "no field or method" */ {}.D _ = S3 /* ERROR "ambiguous selector" */ {}.S1 _ = S3{}.A _ = S3 /* ERROR "ambiguous selector" */ {}.B _ = S3{}.D _ = S3{}.E _ = S4{}.A _ = S4 /* ERROR "no field or method" */ {}.B _ = S5 /* ERROR "ambiguous selector" */ {}.X _ = S5{}.Y _ = S10 /* ERROR "ambiguous selector" */ {}.X _ = S10{}.Y } // Borrowed from the FieldByName benchmark in reflect/all_test.go. type R0 struct { *R1 *R2 *R3 *R4 } type R1 struct { *R5 *R6 *R7 *R8 } type R2 R1 type R3 R1 type R4 R1 type R5 struct { *R9 *R10 *R11 *R12 } type R6 R5 type R7 R5 type R8 R5 type R9 struct { *R13 *R14 *R15 *R16 } type R10 R9 type R11 R9 type R12 R9 type R13 struct { *R17 *R18 *R19 *R20 } type R14 R13 type R15 R13 type R16 R13 type R17 struct { *R21 *R22 *R23 *R24 } type R18 R17 type R19 R17 type R20 R17 type R21 struct { X int } type R22 R21 type R23 R21 type R24 R21 var _ = R0 /* ERROR "ambiguous selector" */ {}.X./go/types/testdata/expr1.src0000644000014500017510000000030512246613010015623 0ustar michaelstaff// Copyright 2012 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. // binary expressions package expr1 ./go/types/testdata/decls1.src0000644000014500017510000000705612246613010015751 0ustar michaelstaff// Copyright 2012 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. // variable declarations package decls1 import ( "math" ) // Global variables without initialization var ( a, b bool c byte d uint8 r rune i int j, k, l int x, y float32 xx, yy float64 u, v complex64 uu, vv complex128 s, t string array []byte iface interface{} blank _ /* ERROR "cannot use _" */ ) // Global variables with initialization var ( s1 = i + j s2 = i /* ERROR "mismatched types" */ + x s3 = c + d s4 = s + t s5 = s /* ERROR "invalid operation" */ / t s6 = array[t1] s7 = array[x /* ERROR "integer" */] s8 = &a s10 = &42 /* ERROR "cannot take address" */ s11 = &v s12 = -(u + *t11) / *&v s13 = a /* ERROR "shifted operand" */ << d s14 = i << j /* ERROR "must be unsigned" */ s18 = math.Pi * 10.0 s19 = s1 /* ERROR "cannot call" */ () s20 = f0 /* ERROR "no value" */ () s21 = f6(1, s1, i) s22 = f6(1, s1, uu /* ERROR "cannot pass argument" */ ) t1 int = i + j t2 int = i /* ERROR "mismatched types" */ + x t3 int = c /* ERROR "cannot initialize" */ + d t4 string = s + t t5 string = s /* ERROR "invalid operation" */ / t t6 byte = array[t1] t7 byte = array[x /* ERROR "must be integer" */] t8 *int = & /* ERROR "cannot initialize" */ a t10 *int = &42 /* ERROR "cannot take address" */ t11 *complex64 = &v t12 complex64 = -(u + *t11) / *&v t13 int = a /* ERROR "shifted operand" */ << d t14 int = i << j /* ERROR "must be unsigned" */ t15 math /* ERROR "not in selector" */ t16 math /* ERROR "not declared" */ .xxx t17 math /* ERROR "not a type" */ .Pi t18 float64 = math.Pi * 10.0 t19 int = t1 /* ERROR "cannot call" */ () t20 int = f0 /* ERROR "no value" */ () ) // Various more complex expressions var ( u1 = x /* ERROR "not an interface" */ .(int) u2 = iface.([]int) u3 = iface.(a /* ERROR "not a type" */ ) u4, ok = iface.(int) u5, ok2, ok3 = iface /* ERROR "assignment count mismatch" */ .(int) ) // Constant expression initializations var ( v1 = 1 /* ERROR "cannot convert" */ + "foo" v2 = c + 255 v3 = c + 256 /* ERROR "overflows" */ v4 = r + 2147483647 v5 = r + 2147483648 /* ERROR "overflows" */ v6 = 42 v7 = v6 + 9223372036854775807 v8 = v6 + 9223372036854775808 /* ERROR "overflows" */ v9 = i + 1 << 10 v10 byte = 1024 /* ERROR "overflows" */ v11 = xx/yy*yy - xx v12 = true && false v13 = nil /* ERROR "use of untyped nil" */ ) // Multiple assignment expressions var ( m1a, m1b = 1, 2 m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2 m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */ ) func _() { var ( m1a, m1b = 1, 2 m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2 m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */ ) _, _ = m1a, m1b _, _, _ = m2a, m2b, m2c _, _ = m3a, m3b } // Declaration of parameters and results func f0() {} func f1(a /* ERROR "not a type" */) {} func f2(a, b, c d /* ERROR "not a type" */) {} func f3() int { return 0 } func f4() a /* ERROR "not a type" */ { return 0 } func f5() (a, b, c d /* ERROR "not a type" */) { return } func f6(a, b, c int) complex128 { return 0 } // Declaration of receivers type T struct{} func (T) m0() {} func (*T) m1() {} func (x T) m2() {} func (x *T) m3() {} // Initialization functions func init() {} func /* ERROR "no arguments and no return values" */ init(int) {} func /* ERROR "no arguments and no return values" */ init() int { return 0 } func /* ERROR "no arguments and no return values" */ init(int) int { return 0 } func (T) init(int) int { return 0 } ./go/types/testdata/cycles2.src0000644000014500017510000000144312246613010016134 0ustar michaelstaff// 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 p // Test case for issue 5090 type t interface { f(u) } type u interface { t } func _() { var t t var u u t.f(t) t.f(u) u.f(t) u.f(u) } // Test case for issue 6589. type A interface { a() interface { AB } } type B interface { a() interface { AB } } type AB interface { a() interface { A B /* ERROR a redeclared */ } b() interface { A B /* ERROR a redeclared */ } } var x AB var y interface { A B /* ERROR a redeclared */ } var _ = x /* ERROR cannot compare */ == y // Test case for issue 6638. type T /* ERROR cycle */ interface { m() [T /* ERROR no field or method */ (nil).m()[0]]int } ./go/types/testdata/shifts.src0000644000014500017510000002373012246613010016073 0ustar michaelstaff// 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 shifts func shifts0() { // basic constant shifts const ( s = 10 _ = 0<<0 _ = 1<> s) _, _, _ = u, v, x } func shifts4() { // shifts in comparisons w/ untyped operands var s uint _ = 1< */ /* ERROR previous case */ : case int: case nil /* ERROR duplicate case */ , nil /* ERROR duplicate case */ : } type F func(int) switch x.(type) { case nil: case int, func /* ERROR previous case */ (int): case float32, func /* ERROR duplicate case */ (x int): case F: } } func rangeloops1() { var ( x int a [10]float32 b []string p *[10]complex128 pp **[10]complex128 s string m map[int]bool c chan int sc chan<- int rc <-chan int ) for _ = range x /* ERROR "cannot range over" */ {} for i := range x /* ERROR "cannot range over" */ {} for i := range a { var ii int ii = i _ = ii } for i, x := range a { var ii int ii = i _ = ii var xx float64 xx = x /* ERROR "cannot assign" */ _ = xx } var ii int var xx float32 for ii, xx = range a {} _, _ = ii, xx for i := range b { var ii int ii = i _ = ii } for i, x := range b { var ii int ii = i _ = ii var xx string xx = x _ = xx } for i := range s { var ii int ii = i _ = ii } for i, x := range s { var ii int ii = i _ = ii var xx rune xx = x _ = xx } for _, x := range p { var xx complex128 xx = x _ = xx } for _, x := range pp /* ERROR "cannot range over" */ {} for k := range m { var kk int32 kk = k /* ERROR "cannot assign" */ _ = kk } for k, v := range m { var kk int kk = k _ = kk if v {} } for _, _ /* ERROR "only one iteration variable" */ = range c {} for e := range c { var ee int ee = e _ = ee } for _ = range sc /* ERROR "cannot range over send-only channel" */ {} for _ = range rc {} // constant strings const cs = "foo" for i, x := range cs { _, _ = i, x } for i, x := range "" { var ii int ii = i _ = ii var xx rune xx = x _ = xx } } func rangeloops2() { type I int type R rune var a [10]int var i I _ = i for i /* ERROR cannot assign */ = range a {} for i /* ERROR cannot assign */ = range &a {} for i /* ERROR cannot assign */ = range a[:] {} var s string var r R _ = r for i /* ERROR cannot assign */ = range s {} for i /* ERROR cannot assign */ = range "foo" {} for _, r /* ERROR cannot assign */ = range s {} for _, r /* ERROR cannot assign */ = range "foo" {} } func issue6766b() { for _ := /* ERROR no new variables */ range "" {} for a, a /* ERROR redeclared */ := range "" { _ = a } var a int _ = a for a, a /* ERROR redeclared */ := range []int{1, 2, 3} { _ = a } } func labels0() { goto L0 goto L1 L0: L1: L1 /* ERROR "already declared" */ : if true { goto L2 L2: L0 /* ERROR "already declared" */ : } _ = func() { goto L0 goto L1 goto L2 L0: L1: L2: } } func expression_statements(ch chan int) { expression_statements(ch) <-ch println() 0 /* ERROR "not used" */ 1 /* ERROR "not used" */ +2 cap /* ERROR "not used" */ (ch) println /* ERROR "must be called" */ } ./go/types/builtins_test.go0000644000014500017510000001666012246613010015474 0ustar michaelstaff// 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 types_test import ( "fmt" "go/ast" "go/parser" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) var builtinCalls = []struct { name, src, sig string }{ {"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`}, {"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`}, {"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`}, {"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`}, // Note that ...uint8 (instead of ..byte) appears below because that is the type // that corresponds to Typ[byte] (an alias) - in the other cases, the type name // is chosen by the source. Either way, byte and uint8 denote identical types. {"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, ...uint8) []byte`}, {"append", `type T []byte; var s T; _ = append(s, "foo"...)`, `func(p.T, ...uint8) p.T`}, {"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`}, {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`}, {"len", `_ = len("foo")`, `invalid type`}, // constant {"len", `var s string; _ = len(s)`, `func(string) int`}, {"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant {"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant {"len", `var s []int64; _ = len(s)`, `func([]int64) int`}, {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`}, {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`}, {"close", `var c chan int; close(c)`, `func(chan int)`}, {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`}, {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`}, {"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`}, {"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`}, {"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`}, {"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`}, {"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func([][]int, [][]int) int`}, {"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`}, {"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`}, {"imag", `_ = imag(1i)`, `invalid type`}, // constant {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`}, {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`}, {"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`}, {"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`}, {"real", `_ = real(1i)`, `invalid type`}, // constant {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`}, {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`}, {"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`}, {"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`}, {"make", `_ = make([]int, 10)`, `func([]int, int) []int`}, {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`}, {"new", `_ = new(int)`, `func(int) *int`}, {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`}, {"panic", `panic(0)`, `func(interface{})`}, {"panic", `panic("foo")`, `func(interface{})`}, {"print", `print()`, `func()`}, {"print", `print(0)`, `func(int)`}, {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, {"println", `println()`, `func()`}, {"println", `println(0)`, `func(int)`}, {"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, {"recover", `recover()`, `func() interface{}`}, {"recover", `_ = recover()`, `func() interface{}`}, {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant {"assert", `assert(true)`, `invalid type`}, // constant {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant // no tests for trace since it produces output as a side-effect } func TestBuiltinSignatures(t *testing.T) { DefPredeclaredTestFuncs() seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually for _, call := range builtinCalls { testBuiltinSignature(t, call.name, call.src, call.sig) seen[call.name] = true } // make sure we didn't miss one for _, name := range Universe.Names() { if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] { t.Errorf("missing test for %s", name) } } for _, name := range Unsafe.Scope().Names() { if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] { t.Errorf("missing test for unsafe.%s", name) } } } func testBuiltinSignature(t *testing.T, name, src0, want string) { src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0) f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Errorf("%s: %s", src0, err) return } var conf Config objects := make(map[*ast.Ident]Object) types := make(map[ast.Expr]Type) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Objects: objects, Types: types}) if err != nil { t.Errorf("%s: %s", src0, err) return } // find called function n := 0 var fun ast.Expr for x, _ := range types { if call, _ := x.(*ast.CallExpr); call != nil { fun = call.Fun n++ } } if n != 1 { t.Errorf("%s: got %d CallExprs; want 1", src0, n) return } // check recorded types for fun and descendents (may be parenthesized) for { // the recorded type for the built-in must match the wanted signature typ := types[fun] if typ == nil { t.Errorf("%s: no type recorded for %s", src0, ExprString(fun)) return } if got := typ.String(); got != want { t.Errorf("%s: got type %s; want %s", src0, got, want) return } // called function must be a (possibly parenthesized, qualified) // identifier denoting the expected built-in switch p := fun.(type) { case *ast.Ident: obj := objects[p] if obj == nil { t.Errorf("%s: no object found for %s", src0, p) return } bin, _ := obj.(*Builtin) if bin == nil { t.Errorf("%s: %s does not denote a built-in", src0, p) return } if bin.Name() != name { t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name) return } return // we're done case *ast.ParenExpr: fun = p.X // unpack case *ast.SelectorExpr: // built-in from package unsafe - ignore details return // we're done default: t.Errorf("%s: invalid function call", src0) return } } } ./go/types/universe.go0000644000014500017510000001347212246613010014442 0ustar michaelstaff// 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 implements the universe and unsafe package scopes. package types import ( "go/token" "strings" "code.google.com/p/go.tools/go/exact" ) var ( Universe *Scope Unsafe *Package universeIota *Const ) var Typ = [...]*Basic{ Invalid: {Invalid, 0, 0, "invalid type"}, Bool: {Bool, IsBoolean, 1, "bool"}, Int: {Int, IsInteger, 0, "int"}, Int8: {Int8, IsInteger, 1, "int8"}, Int16: {Int16, IsInteger, 2, "int16"}, Int32: {Int32, IsInteger, 4, "int32"}, Int64: {Int64, IsInteger, 8, "int64"}, Uint: {Uint, IsInteger | IsUnsigned, 0, "uint"}, Uint8: {Uint8, IsInteger | IsUnsigned, 1, "uint8"}, Uint16: {Uint16, IsInteger | IsUnsigned, 2, "uint16"}, Uint32: {Uint32, IsInteger | IsUnsigned, 4, "uint32"}, Uint64: {Uint64, IsInteger | IsUnsigned, 8, "uint64"}, Uintptr: {Uintptr, IsInteger | IsUnsigned, 0, "uintptr"}, Float32: {Float32, IsFloat, 4, "float32"}, Float64: {Float64, IsFloat, 8, "float64"}, Complex64: {Complex64, IsComplex, 8, "complex64"}, Complex128: {Complex128, IsComplex, 16, "complex128"}, String: {String, IsString, 0, "string"}, UnsafePointer: {UnsafePointer, 0, 0, "Pointer"}, UntypedBool: {UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"}, UntypedInt: {UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"}, UntypedRune: {UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"}, UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"}, UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"}, UntypedString: {UntypedString, IsString | IsUntyped, 0, "untyped string"}, UntypedNil: {UntypedNil, IsUntyped, 0, "untyped nil"}, } var aliases = [...]*Basic{ {Byte, IsInteger | IsUnsigned, 1, "byte"}, {Rune, IsInteger, 4, "rune"}, } func defPredeclaredTypes() { for _, t := range Typ { def(NewTypeName(token.NoPos, nil, t.name, t)) } for _, t := range aliases { def(NewTypeName(token.NoPos, nil, t.name, t)) } // Error has a nil package in its qualified name since it is in no package res := NewVar(token.NoPos, nil, "", Typ[String]) sig := &Signature{results: NewTuple(res)} err := NewFunc(token.NoPos, nil, "Error", sig) typ := &Named{underlying: NewInterface([]*Func{err}, nil), complete: true} sig.recv = NewVar(token.NoPos, nil, "", typ) def(NewTypeName(token.NoPos, nil, "error", typ)) } var predeclaredConsts = [...]struct { name string kind BasicKind val exact.Value }{ {"true", UntypedBool, exact.MakeBool(true)}, {"false", UntypedBool, exact.MakeBool(false)}, {"iota", UntypedInt, exact.MakeInt64(0)}, } func defPredeclaredConsts() { for _, c := range predeclaredConsts { def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val)) } } func defPredeclaredNil() { def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}}) } // A builtinId is the id of a builtin function. type builtinId int const ( // universe scope _Append builtinId = iota _Cap _Close _Complex _Copy _Delete _Imag _Len _Make _New _Panic _Print _Println _Real _Recover // package unsafe _Alignof _Offsetof _Sizeof // testing support _Assert _Trace ) var predeclaredFuncs = [...]struct { name string nargs int variadic bool kind exprKind }{ _Append: {"append", 1, true, expression}, _Cap: {"cap", 1, false, expression}, _Close: {"close", 1, false, statement}, _Complex: {"complex", 2, false, expression}, _Copy: {"copy", 2, false, statement}, _Delete: {"delete", 2, false, statement}, _Imag: {"imag", 1, false, expression}, _Len: {"len", 1, false, expression}, _Make: {"make", 1, true, expression}, _New: {"new", 1, false, expression}, _Panic: {"panic", 1, false, statement}, _Print: {"print", 0, true, statement}, _Println: {"println", 0, true, statement}, _Real: {"real", 1, false, expression}, _Recover: {"recover", 0, false, statement}, _Alignof: {"Alignof", 1, false, expression}, _Offsetof: {"Offsetof", 1, false, expression}, _Sizeof: {"Sizeof", 1, false, expression}, _Assert: {"assert", 1, false, statement}, _Trace: {"trace", 0, true, statement}, } func defPredeclaredFuncs() { for i := range predeclaredFuncs { id := builtinId(i) if id == _Assert || id == _Trace { continue // only define these in testing environment } def(newBuiltin(id)) } } // DefPredeclaredTestFuncs defines the assert and trace built-ins. // These built-ins are intended for debugging and testing of this // package only. func DefPredeclaredTestFuncs() { if Universe.Lookup("assert") != nil { return // already defined } def(newBuiltin(_Assert)) def(newBuiltin(_Trace)) } func init() { Universe = NewScope(nil) Unsafe = NewPackage("unsafe", "unsafe", NewScope(Universe)) Unsafe.complete = true defPredeclaredTypes() defPredeclaredConsts() defPredeclaredNil() defPredeclaredFuncs() universeIota = Universe.Lookup("iota").(*Const) } // Objects with names containing blanks are internal and not entered into // a scope. Objects with exported names are inserted in the unsafe package // scope; other objects are inserted in the universe scope. // func def(obj Object) { name := obj.Name() if strings.Index(name, " ") >= 0 { return // nothing to do } // fix Obj link for named types if typ, ok := obj.Type().(*Named); ok { typ.obj = obj.(*TypeName) } // exported identifiers go into package unsafe scope := Universe if obj.IsExported() { scope = Unsafe.scope // set Pkg field switch obj := obj.(type) { case *TypeName: obj.pkg = Unsafe case *Builtin: obj.pkg = Unsafe default: unreachable() } } if scope.Insert(obj) != nil { panic("internal error: double declaration") } } ./go/types/resolver_test.go0000644000014500017510000000614212246613010015476 0ustar michaelstaff// 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. package types_test import ( "fmt" "go/ast" "go/parser" "go/token" "testing" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) var sources = []string{ ` package p import "fmt" import "math" const pi = math.Pi func sin(x float64) float64 { return math.Sin(x) } var Println = fmt.Println `, ` package p import "fmt" func f() string { _ = "foo" return fmt.Sprintf("%d", g()) } func g() (x int) { return } `, ` package p import . "go/parser" import "sync" func h() Mode { return ImportsOnly } var _, x int = 1, 2 func init() {} type T struct{ sync.Mutex; a, b, c int} type I interface{ m() } var _ = T{a: 1, b: 2, c: 3} func (_ T) m() {} func (T) _() {} var i I var _ = i.m func _(s []int) { for i, x := range s { _, _ = i, x } } func _(x interface{}) { switch x := x.(type) { case int: _ = x } } `, ` package p type S struct{} func (T) _() {} func (T) _() {} `, ` package p func _() { L0: L1: goto L0 for { goto L1 } if true { goto L2 } L2: } `, } var pkgnames = []string{ "fmt", "math", } func TestResolveIdents(t *testing.T) { // parse package files fset := token.NewFileSet() var files []*ast.File for i, src := range sources { f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors) if err != nil { t.Fatal(err) } files = append(files, f) } // resolve and type-check package AST var conf Config idents := make(map[*ast.Ident]Object) _, err := conf.Check("testResolveIdents", fset, files, &Info{Objects: idents}) if err != nil { t.Fatal(err) } // check that all packages were imported for _, name := range pkgnames { if conf.Packages[name] == nil { t.Errorf("package %s not imported", name) } } // check that qualified identifiers are resolved for _, f := range files { ast.Inspect(f, func(n ast.Node) bool { if s, ok := n.(*ast.SelectorExpr); ok { if x, ok := s.X.(*ast.Ident); ok { obj := idents[x] if obj == nil { t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name) return false } if _, ok := obj.(*PkgName); ok && idents[s.Sel] == nil { t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name) return false } return false } return false } return true }) } // check that each identifier in the source is found in the idents map for _, f := range files { ast.Inspect(f, func(n ast.Node) bool { if x, ok := n.(*ast.Ident); ok { if _, found := idents[x]; found { delete(idents, x) } else { t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name) } return false } return true }) } // any left-over identifiers didn't exist in the source for x := range idents { t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) } // TODO(gri) add tests to check ImplicitObj callbacks } ./go/types/check.go0000644000014500017510000001625312246613010013657 0ustar michaelstaff// 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 implements the Check function, which drives type-checking. package types import ( "go/ast" "go/token" "path" "code.google.com/p/go.tools/go/exact" ) // debugging/development support const ( debug = true // leave on during development trace = false // turn on for detailed type resolution traces ) // exprInfo stores type and constant value for an untyped expression. type exprInfo struct { isLhs bool // expression is lhs operand of a shift with delayed type-check typ *Basic val exact.Value // constant value; or nil (if not a constant) } // A checker is an instance of the type-checker. type checker struct { conf *Config fset *token.FileSet pkg *Package methods map[string][]*Func // maps type names to associated methods conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls) untyped map[ast.Expr]exprInfo // map of expressions without final type lhsVarsList [][]*Var // type switch lhs variable sets, for 'declared but not used' errors delayed []func() // delayed checks that require fully setup types firstErr error // first error encountered Info // collected type info objMap map[Object]*declInfo // if set we are in the package-level declaration phase (otherwise all objects seen must be declared) initMap map[Object]*declInfo // map of variables/functions with init expressions/bodies topScope *Scope // current topScope for lookups iota exact.Value // current value of iota in a constant declaration; nil otherwise decl *declInfo // current package-level declaration whose init expression/body is type-checked // functions funcList []funcInfo // list of functions/methods with correct signatures and non-empty bodies funcSig *Signature // signature of currently type-checked function hasLabel bool // set if a function makes use of labels (only ~1% of functions) // debugging indent int // indentation for tracing } func newChecker(conf *Config, fset *token.FileSet, pkg *Package) *checker { return &checker{ conf: conf, fset: fset, pkg: pkg, methods: make(map[string][]*Func), conversions: make(map[*ast.CallExpr]bool), untyped: make(map[ast.Expr]exprInfo), } } // addDeclDep adds the dependency edge (check.decl -> to) // if check.decl exists and to has an init expression. func (check *checker) addDeclDep(to Object) { from := check.decl if from == nil { return // not in a package-level init expression } init := check.initMap[to] if init == nil { return // to does not have a package-level init expression } m := from.deps if m == nil { m = make(map[Object]*declInfo) from.deps = m } m[to] = init } func (check *checker) delay(f func()) { check.delayed = append(check.delayed, f) } func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) { assert(x != nil && typ != nil) if m := check.Types; m != nil { m[x] = typ } if val != nil { if m := check.Values; m != nil { m[x] = val } } } func (check *checker) recordBuiltinType(f ast.Expr, sig *Signature) { // f must be a (possibly parenthesized) identifier denoting a built-in // (built-ins in package unsafe always produce a constant result and // we don't record their signatures, so we don't see qualified idents // here): record the signature for f and possible children. for { check.recordTypeAndValue(f, sig, nil) switch p := f.(type) { case *ast.Ident: return // we're done case *ast.ParenExpr: f = p.X default: unreachable() } } } func (check *checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { assert(x != nil) if a[0] == nil || a[1] == nil { return } assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1])) if m := check.Types; m != nil { for { assert(m[x] != nil) // should have been recorded already pos := x.Pos() m[x] = NewTuple( NewVar(pos, check.pkg, "", a[0]), NewVar(pos, check.pkg, "", a[1]), ) // if x is a parenthesized expression (p.X), update p.X p, _ := x.(*ast.ParenExpr) if p == nil { break } x = p.X } } } func (check *checker) recordObject(id *ast.Ident, obj Object) { assert(id != nil) if m := check.Objects; m != nil { m[id] = obj } } func (check *checker) recordImplicit(node ast.Node, obj Object) { assert(node != nil && obj != nil) if m := check.Implicits; m != nil { m[node] = obj } } func (check *checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { assert(obj != nil && (recv == nil || len(index) > 0)) check.recordObject(x.Sel, obj) // TODO(gri) Should we also call recordTypeAndValue? if m := check.Selections; m != nil { m[x] = &Selection{kind, recv, obj, index, indirect} } } func (check *checker) recordScope(node ast.Node, scope *Scope) { assert(node != nil && scope != nil) if m := check.Scopes; m != nil { m[node] = scope } } // A bailout panic is raised to indicate early termination. type bailout struct{} func (check *checker) handleBailout(err *error) { switch p := recover().(type) { case nil, bailout: // normal return or early exit *err = check.firstErr default: // re-panic panic(p) } } func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File, info *Info) (pkg *Package, err error) { // make sure we have a package canonicalization map if conf.Packages == nil { conf.Packages = make(map[string]*Package) } pkg = NewPackage(pkgPath, "", NewScope(Universe)) // package name is set below check := newChecker(conf, fset, pkg) defer check.handleBailout(&err) // we need a reasonable package path to continue if path.Clean(pkgPath) == "." { check.errorf(token.NoPos, "invalid package path provided: %q", pkgPath) return } // determine package name and files i := 0 for _, file := range files { switch name := file.Name.Name; pkg.name { case "": pkg.name = name fallthrough case name: files[i] = file i++ default: check.errorf(file.Package, "package %s; expected %s", name, pkg.name) // ignore this file } } // install optional info if info != nil { check.Info = *info } check.resolveFiles(files[:i]) // perform delayed checks for _, f := range check.delayed { f() } check.delayed = nil // not needed anymore // remaining untyped expressions must indeed be untyped if debug { for x, info := range check.untyped { if isTyped(info.typ) { check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ) panic(0) } } } // notify client of any untyped types left // TODO(gri) Consider doing this before and // after function body checking for smaller // map size and more immediate feedback. if check.Types != nil || check.Values != nil { for x, info := range check.untyped { check.recordTypeAndValue(x, info.typ, info.val) } } // copy check.InitOrder back to incoming *info if necessary // (In case of early (error) bailout, this is not done, but we don't care in that case.) if info != nil { info.InitOrder = check.InitOrder } return } ./go/types/resolver.go0000644000014500017510000006506612246613010014451 0ustar michaelstaff// 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 types import ( "errors" "fmt" "go/ast" "go/token" "strconv" "strings" "unicode" "code.google.com/p/go.tools/go/exact" ) func (check *checker) reportAltDecl(obj Object) { if pos := obj.Pos(); pos.IsValid() { // We use "other" rather than "previous" here because // the first declaration seen may not be textually // earlier in the source. check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented } } func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) { if alt := scope.Insert(obj); alt != nil { check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) check.reportAltDecl(alt) return } if id != nil { check.recordObject(id, obj) } } // A declInfo describes a package-level const, type, var, or func declaration. type declInfo struct { file *Scope // scope of file containing this declaration lhs []*Var // lhs of n:1 variable declarations, or nil typ ast.Expr // type, or nil init ast.Expr // init expression, or nil fdecl *ast.FuncDecl // func declaration, or nil deps map[Object]*declInfo // init dependencies; lazily allocated mark int // see check.dependencies } type funcInfo struct { obj *Func // for debugging/tracing only info *declInfo // for cycle detection sig *Signature body *ast.BlockStmt } // later appends a function with non-empty body to check.funcList. func (check *checker) later(f *Func, info *declInfo, sig *Signature, body *ast.BlockStmt) { // functions implemented elsewhere (say in assembly) have no body if !check.conf.IgnoreFuncBodies && body != nil { check.funcList = append(check.funcList, funcInfo{f, info, sig, body}) } } // arityMatch checks that the lhs and rhs of a const or var decl // have the appropriate number of names and init exprs. For const // decls, init is the value spec providing the init exprs; for // var decls, init is nil (the init exprs are in s in this case). func (check *checker) arityMatch(s, init *ast.ValueSpec) { l := len(s.Names) r := len(s.Values) if init != nil { r = len(init.Values) } switch { case init == nil && r == 0: // var decl w/o init expr if s.Type == nil { check.errorf(s.Pos(), "missing type or init expr") } case l < r: if l < len(s.Values) { // init exprs from s n := s.Values[l] check.errorf(n.Pos(), "extra init expr %s", n) } else { // init exprs "inherited" check.errorf(s.Pos(), "extra init expr at %s", init.Pos()) } case l > r && (init != nil || r != 1): n := s.Names[r] check.errorf(n.Pos(), "missing init expr for %s", n) } } func (check *checker) validatedImportPath(path string) (string, error) { s, err := strconv.Unquote(path) if err != nil { return "", err } if s == "" { return "", fmt.Errorf("empty string") } const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" for _, r := range s { if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { return s, fmt.Errorf("invalid character %#U", r) } } return s, nil } // TODO(gri) Split resolveFiles into smaller components. func (check *checker) resolveFiles(files []*ast.File) { pkg := check.pkg // Phase 1: Pre-declare all package-level objects so that they can be found // independent of source order. Associate methods with receiver // base type names. var ( objList []Object // list of package-level objects, in source order objMap = make(map[Object]*declInfo) // corresponding declaration info initMap = make(map[Object]*declInfo) // declaration info for variables with initializers ) // declare declares obj in the package scope, records its ident -> obj mapping, // and updates objList and objMap. The object must not be a function or method. declare := func(ident *ast.Ident, obj Object, d *declInfo) { assert(ident.Name == obj.Name()) // spec: "A package-scope or file-scope identifier with name init // may only be declared to be a function with this (func()) signature." if ident.Name == "init" { check.errorf(ident.Pos(), "cannot declare init - must be func") return } check.declare(pkg.scope, ident, obj) objList = append(objList, obj) objMap[obj] = d } importer := check.conf.Import if importer == nil { if DefaultImport == nil { panic(`no Config.Import or DefaultImport (missing import _ "code.google.com/p/go.tools/go/gcimporter"?)`) } importer = DefaultImport } var ( seenPkgs = make(map[*Package]bool) // imported packages that have been seen already fileScopes []*Scope // file scope for each file dotImports []map[*Package]token.Pos // positions of dot-imports for each file ) for _, file := range files { // The package identifier denotes the current package, // but there is no corresponding package object. check.recordObject(file.Name, nil) fileScope := NewScope(pkg.scope) check.recordScope(file, fileScope) fileScopes = append(fileScopes, fileScope) dotImports = append(dotImports, nil) // element (map) is lazily allocated for _, decl := range file.Decls { switch d := decl.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: var last *ast.ValueSpec // last ValueSpec with type or init exprs seen for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: // import package var imp *Package path, err := check.validatedImportPath(s.Path.Value) if err != nil { check.errorf(s.Path.Pos(), "invalid import path (%s)", err) continue } if path == "C" && check.conf.FakeImportC { // TODO(gri) shouldn't create a new one each time imp = NewPackage("C", "C", NewScope(nil)) imp.fake = true } else { var err error imp, err = importer(check.conf.Packages, path) if imp == nil && err == nil { err = errors.New("Config.Import returned nil but no error") } if err != nil { check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) continue } } // add package to list of explicit imports // (this functionality is provided as a convenience // for clients; it is not needed for type-checking) if !seenPkgs[imp] { seenPkgs[imp] = true if imp != Unsafe { pkg.imports = append(pkg.imports, imp) } } // local name overrides imported package name name := imp.name if s.Name != nil { name = s.Name.Name if name == "init" { check.errorf(s.Name.Pos(), "cannot declare init - must be func") continue } } obj := NewPkgName(s.Pos(), imp, name) if s.Name != nil { // in a dot-import, the dot represents the package check.recordObject(s.Name, obj) } else { check.recordImplicit(s, obj) } // add import to file scope if name == "." { // merge imported scope with file scope for _, obj := range imp.scope.elems { // A package scope may contain non-exported objects, // do not import them! if obj.IsExported() { // Note: This will change each imported object's scope! // May be an issue for type aliases. check.declare(fileScope, nil, obj) check.recordImplicit(s, obj) } } // add position to set of dot-import positions for this file // (this is only needed for "imported but not used" errors) posSet := dotImports[len(dotImports)-1] if posSet == nil { posSet = make(map[*Package]token.Pos) dotImports[len(dotImports)-1] = posSet } posSet[imp] = s.Pos() } else { // declare imported package object in file scope check.declare(fileScope, nil, obj) } case *ast.ValueSpec: switch d.Tok { case token.CONST: // determine which initialization expressions to use switch { case s.Type != nil || len(s.Values) > 0: last = s case last == nil: last = new(ast.ValueSpec) // make sure last exists } // declare all constants for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) var init ast.Expr if i < len(last.Values) { init = last.Values[i] } declare(name, obj, &declInfo{file: fileScope, typ: last.Type, init: init}) } check.arityMatch(s, last) case token.VAR: lhs := make([]*Var, len(s.Names)) // If there's exactly one rhs initializer, use // the same declInfo d1 for all lhs variables // so that each lhs variable depends on the same // rhs initializer (n:1 var declaration). var d1 *declInfo if len(s.Values) == 1 { // The lhs elements are only set up after the foor loop below, // but that's ok because declareVar only collects the declInfo // for a later phase. d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]} } // declare all variables for i, name := range s.Names { obj := NewVar(name.Pos(), pkg, name.Name, nil) lhs[i] = obj d := d1 var init ast.Expr if d == nil { // individual assignments if i < len(s.Values) { init = s.Values[i] } d = &declInfo{file: fileScope, typ: s.Type, init: init} } declare(name, obj, d) // remember obj if it has an initializer if d1 != nil || init != nil { initMap[obj] = d } } check.arityMatch(s, nil) default: check.invalidAST(s.Pos(), "invalid token %s", d.Tok) } case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) declare(s.Name, obj, &declInfo{file: fileScope, typ: s.Type}) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) } } case *ast.FuncDecl: name := d.Name.Name obj := NewFunc(d.Name.Pos(), pkg, name, nil) if d.Recv == nil { // regular function if name == "init" { // don't declare init functions in the package scope - they are invisible obj.parent = pkg.scope check.recordObject(d.Name, obj) // init functions must have a body if d.Body == nil { check.errorf(obj.pos, "missing function body") // ok to continue } } else { check.declare(pkg.scope, d.Name, obj) } } else { // Associate method with receiver base type name, if possible. // Ignore methods that have an invalid receiver, or a blank _ // receiver name. They will be type-checked later, with regular // functions. if list := d.Recv.List; len(list) > 0 { typ := list[0].Type if ptr, _ := typ.(*ast.StarExpr); ptr != nil { typ = ptr.X } if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" { check.methods[base.Name] = append(check.methods[base.Name], obj) } } } objList = append(objList, obj) info := &declInfo{file: fileScope, fdecl: d} objMap[obj] = info // remember obj if it has a body (= initializer) if d.Body != nil { initMap[obj] = info } default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } } } seenPkgs = nil // not needed anymore // Phase 2: Verify that objects in package and file scopes have different names. for _, scope := range fileScopes { for _, obj := range scope.elems { if alt := pkg.scope.Lookup(obj.Name()); alt != nil { check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name()) } } } // Phase 3: Typecheck all objects in objList, but not function bodies. check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet) check.initMap = initMap for _, obj := range objList { if obj.Type() == nil { check.objDecl(obj, nil, false) } } check.objMap = nil // done with package-level declarations // At this point we may have a non-empty check.methods map; this means that not all // entries were deleted at the end of typeDecl because the respective receiver base // types were not declared. In that case, an error was reported when declaring those // methods. We can now safely discard this map. check.methods = nil // Phase 4: Typecheck all functions bodies. // Note: funcList may grow while iterating through it - cannot use range clause. for i := 0; i < len(check.funcList); i++ { // TODO(gri) Factor out this code into a dedicated function // with its own context so that it can be run concurrently // eventually. f := check.funcList[i] if trace { s := "" if f.obj != nil { s = f.obj.name } fmt.Println("---", s) } check.topScope = f.sig.scope // open function scope check.funcSig = f.sig check.hasLabel = false check.decl = f.info check.stmtList(0, f.body.List) if check.hasLabel { check.labels(f.body) } if f.sig.results.Len() > 0 && !check.isTerminating(f.body, "") { check.errorf(f.body.Rbrace, "missing return") } } // Phase 5: Check initialization dependencies. // Note: must happen after checking all functions because function bodies // may introduce dependencies // For source order and reproducible error messages, // iterate through objList rather than initMap. for _, obj := range objList { if obj, _ := obj.(*Var); obj != nil { if init := initMap[obj]; init != nil { // provide non-nil path for re-use of underlying array check.dependencies(obj, init, make([]Object, 0, 8)) } } } objList = nil // not needed anymore check.initMap = nil // not needed anymore // Phase 6: Check for declared but not used packages and variables. // Note: must happen after checking all functions because closures may affect outer scopes // spec: "It is illegal (...) to directly import a package without referring to // any of its exported identifiers. To import a package solely for its side-effects // (initialization), use the blank identifier as explicit package name." for i, scope := range fileScopes { var usedDotImports map[*Package]bool // lazily allocated for _, obj := range scope.elems { switch obj := obj.(type) { case *PkgName: // Unused "blank imports" are automatically ignored // since _ identifiers are not entered into scopes. if !obj.used { check.errorf(obj.pos, "%q imported but not used", obj.pkg.path) } default: // All other objects in the file scope must be dot- // imported. If an object was used, mark its package // as used. if obj.isUsed() { if usedDotImports == nil { usedDotImports = make(map[*Package]bool) } usedDotImports[obj.Pkg()] = true } } } // Iterate through all dot-imports for this file and // check if the corresponding package was used. for pkg, pos := range dotImports[i] { if !usedDotImports[pkg] { check.errorf(pos, "%q imported but not used", pkg.path) } } } // Each set of implicitly declared lhs variables in a type switch acts collectively // as a single lhs variable. If any one was 'used', all of them are 'used'. Handle // them before the general analysis. for _, vars := range check.lhsVarsList { // len(vars) > 0 var used bool for _, v := range vars { if v.used { used = true } v.used = true // avoid later error } if !used { v := vars[0] check.errorf(v.pos, "%s declared but not used", v.name) } } // spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." for _, f := range check.funcList { check.usage(f.sig.scope) } } // dependencies recursively traverses the initialization dependency graph in a depth-first // manner and appends the encountered variables in postorder to the Info.InitOrder list. // As a result, that list ends up being sorted topologically in the order of dependencies. // // The current node is represented by the pair (obj, init); and path contains all nodes // on the path to the current node (excluding the current node). // // To detect cyles, the nodes are marked as follows: Initially, all nodes are unmarked // (declInfo.mark == 0). On the way down, a node is appended to the path, and the node // is marked with a value > 0 ("in progress"). On the way up, a node is marked with a // value < 0 ("finished"). A cycle is detected if a node is reached that is marked as // "in progress". // // A cycle must contain at least one variable to be invalid (cycles containing only // functions are permitted). To detect such a cycle, and in order to print it, the // mark value indicating "in progress" is the path length up to (and including) the // current node; i.e. the length of the path after appending the node. Naturally, // that value is > 0 as required for "in progress" marks. In other words, each node's // "in progress" mark value corresponds to the node's path index plus 1. Accordingly, // when the first node of a cycle is reached, that node's mark value indicates the // start of the cycle in the path. The tail of the path (path[mark-1:]) contains all // nodes of the cycle. // func (check *checker) dependencies(obj Object, init *declInfo, path []Object) { if init.mark < 0 { return // finished } if init.mark > 0 { // cycle detected - find index of first variable in cycle, if any first := -1 cycle := path[init.mark-1:] for i, obj := range cycle { if _, ok := obj.(*Var); ok { first = i break } } // only report an error if there's at least one variable if first >= 0 { obj := cycle[first] check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name()) // print cycle i := first for _ = range cycle { check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented i++ if i >= len(cycle) { i = 0 } obj = cycle[i] } check.errorf(obj.Pos(), "\t%s (cycle start)", obj.Name()) } init.mark = -1 // avoid further errors return } // init.mark == 0 path = append(path, obj) // len(path) > 0 init.mark = len(path) // init.mark > 0 for obj, dep := range init.deps { check.dependencies(obj, dep, path) } init.mark = -1 // init.mark < 0 // record the init order for variables if this, _ := obj.(*Var); this != nil { initLhs := init.lhs // possibly nil (see declInfo.lhs field comment) if initLhs == nil { initLhs = []*Var{this} } check.Info.InitOrder = append(check.Info.InitOrder, &Initializer{initLhs, init.init}) } } func (check *checker) usage(scope *Scope) { for _, obj := range scope.elems { if v, _ := obj.(*Var); v != nil && !v.used { check.errorf(v.pos, "%s declared but not used", v.name) } } for _, scope := range scope.children { check.usage(scope) } } // objDecl type-checks the declaration of obj in its respective file scope. // See typeDecl for the details on def and cycleOk. func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) { d := check.objMap[obj] // adjust file scope for current object oldScope := check.topScope check.topScope = d.file // for lookup // save current iota oldIota := check.iota check.iota = nil // save current decl oldDecl := check.decl check.decl = nil switch obj := obj.(type) { case *Const: check.constDecl(obj, d.typ, d.init) case *Var: check.decl = d // new package-level var decl check.varDecl(obj, d.lhs, d.typ, d.init) case *TypeName: check.typeDecl(obj, d.typ, def, cycleOk) case *Func: check.funcDecl(obj, d) default: unreachable() } check.decl = oldDecl check.iota = oldIota check.topScope = oldScope } func (check *checker) constDecl(obj *Const, typ, init ast.Expr) { if obj.visited { check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name) obj.typ = Typ[Invalid] return } obj.visited = true // use the correct value of iota assert(check.iota == nil) check.iota = obj.val // determine type, if any if typ != nil { t := check.typ(typ, nil, false) if !isConstType(t) { check.errorf(typ.Pos(), "invalid constant type %s", t) obj.typ = Typ[Invalid] check.iota = nil return } obj.typ = t } // check initialization var x operand if init != nil { check.expr(&x, init) } check.initConst(obj, &x) check.iota = nil } // TODO(gri) document arguments func (check *checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { if obj.visited { check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.name) obj.typ = Typ[Invalid] return } obj.visited = true // var declarations cannot use iota assert(check.iota == nil) // determine type, if any if typ != nil { obj.typ = check.typ(typ, nil, false) } // check initialization if init == nil { if typ == nil { // error reported before by arityMatch obj.typ = Typ[Invalid] } return } if lhs == nil || len(lhs) == 1 { assert(lhs == nil || lhs[0] == obj) var x operand check.expr(&x, init) check.initVar(obj, &x) return } if debug { // obj must be one of lhs found := false for _, lhs := range lhs { if obj == lhs { found = true break } } if !found { panic("inconsistent lhs") } } check.initVars(lhs, []ast.Expr{init}, token.NoPos) } func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) { assert(obj.Type() == nil) // type declarations cannot use iota assert(check.iota == nil) named := &Named{obj: obj} obj.typ = named // make sure recursive type declarations terminate // If this type (named) defines the type of another (def) type declaration, // set def's underlying type to this type so that we can resolve the true // underlying of def later. if def != nil { def.underlying = named } // Typecheck typ - it may be a named type that is not yet complete. // For instance, consider: // // type ( // A B // B *C // C A // ) // // When we declare object C, typ is the identifier A which is incomplete. u := check.typ(typ, named, cycleOk) // Determine the unnamed underlying type. // In the above example, the underlying type of A was (temporarily) set // to B whose underlying type was set to *C. Such "forward chains" always // end in an unnamed type (cycles are terminated with an invalid type). for { n, _ := u.(*Named) if n == nil { break } u = n.underlying } named.underlying = u // the underlying type has been determined named.complete = true // type-check signatures of associated methods methods := check.methods[obj.name] if len(methods) == 0 { return // no methods } // spec: "For a base type, the non-blank names of methods bound // to it must be unique." // => use an objset to determine redeclarations var mset objset // spec: "If the base type is a struct type, the non-blank method // and field names must be distinct." // => pre-populate the objset to find conflicts // TODO(gri) consider keeping the objset with the struct instead if t, _ := named.underlying.(*Struct); t != nil { for _, fld := range t.fields { if fld.name != "_" { assert(mset.insert(fld) == nil) } } } // check each method for _, m := range methods { if m.name != "_" { if alt := mset.insert(m); alt != nil { switch alt.(type) { case *Var: check.errorf(m.pos, "field and method with the same name %s", m.name) case *Func: check.errorf(m.pos, "method %s already declared for %s", m.name, named) default: unreachable() } check.reportAltDecl(alt) continue } } check.recordObject(check.objMap[m].fdecl.Name, m) check.objDecl(m, nil, true) // Methods with blank _ names cannot be found. // Don't add them to the method list. if m.name != "_" { named.methods = append(named.methods, m) } } delete(check.methods, obj.name) // we don't need them anymore } func (check *checker) funcDecl(obj *Func, info *declInfo) { // func declarations cannot use iota assert(check.iota == nil) obj.typ = Typ[Invalid] // guard against cycles fdecl := info.fdecl sig := check.funcType(fdecl.Recv, fdecl.Type, nil) if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) { check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") // ok to continue } obj.typ = sig check.later(obj, info, sig, fdecl.Body) } func (check *checker) declStmt(decl ast.Decl) { pkg := check.pkg switch d := decl.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: var last *ast.ValueSpec // last ValueSpec with type or init exprs seen for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ValueSpec: switch d.Tok { case token.CONST: // determine which init exprs to use switch { case s.Type != nil || len(s.Values) > 0: last = s case last == nil: last = new(ast.ValueSpec) // make sure last exists } // declare all constants lhs := make([]*Const, len(s.Names)) for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) lhs[i] = obj var init ast.Expr if i < len(last.Values) { init = last.Values[i] } check.constDecl(obj, last.Type, init) } check.arityMatch(s, last) for i, name := range s.Names { check.declare(check.topScope, name, lhs[i]) } case token.VAR: lhs0 := make([]*Var, len(s.Names)) for i, name := range s.Names { lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil) } // initialize all variables for i, obj := range lhs0 { var lhs []*Var var init ast.Expr switch len(s.Values) { case len(s.Names): // lhs and rhs match init = s.Values[i] case 1: // rhs is expected to be a multi-valued expression lhs = lhs0 init = s.Values[0] default: if i < len(s.Values) { init = s.Values[i] } } check.varDecl(obj, lhs, s.Type, init) } check.arityMatch(s, nil) // declare all variables // (only at this point are the variable scopes (parents) set) for i, name := range s.Names { check.declare(check.topScope, name, lhs0[i]) } default: check.invalidAST(s.Pos(), "invalid token %s", d.Tok) } case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) check.declare(check.topScope, s.Name, obj) check.typeDecl(obj, s.Type, nil, false) default: check.invalidAST(s.Pos(), "const, type, or var declaration expected") } } default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } } ./go/vcs/0000755000014500017510000000000012246613010011673 5ustar michaelstaff./go/vcs/env.go0000644000014500017510000000204512246613010013013 0ustar michaelstaff// 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 vcs import ( "os" "strings" ) // envForDir returns a copy of the environment // suitable for running in the given directory. // The environment is the current process's environment // but with an updated $PWD, so that an os.Getwd in the // child will be faster. func envForDir(dir string) []string { env := os.Environ() // Internally we only use rooted paths, so dir is rooted. // Even if dir is not rooted, no harm done. return mergeEnvLists([]string{"PWD=" + dir}, env) } // mergeEnvLists merges the two environment lists such that // variables with the same name in "in" replace those in "out". func mergeEnvLists(in, out []string) []string { NextVar: for _, inkv := range in { k := strings.SplitAfterN(inkv, "=", 2)[0] for i, outkv := range out { if strings.HasPrefix(outkv, k) { out[i] = inkv continue NextVar } } out = append(out, inkv) } return out } ./go/vcs/vcs_test.go0000644000014500017510000000361712246613010014063 0ustar michaelstaff// 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 vcs import ( "io/ioutil" "os" "path/filepath" "testing" ) // Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath. // TODO(cmang): Add tests for SVN and BZR. func TestRepoRootForImportPath(t *testing.T) { tests := []struct { path string want *RepoRoot }{ { "code.google.com/p/go", &RepoRoot{ VCS: vcsHg, Repo: "https://code.google.com/p/go", }, }, { "code.google.com/r/go", &RepoRoot{ VCS: vcsHg, Repo: "https://code.google.com/r/go", }, }, { "github.com/golang/groupcache", &RepoRoot{ VCS: vcsGit, Repo: "https://github.com/golang/groupcache", }, }, } for _, test := range tests { got, err := RepoRootForImportPath(test.path, false) if err != nil { t.Errorf("RepoRootForImport(%q): %v", test.path, err) continue } want := test.want if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo { t.Errorf("RepoRootForImport(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo) } } } // Test that FromDir correctly inspects a given directory and returns the right VCS. func TestFromDir(t *testing.T) { type testStruct struct { path string want *Cmd } tests := make([]testStruct, len(vcsList)) tempDir, err := ioutil.TempDir("", "vcstest") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) for i, vcs := range vcsList { tests[i] = testStruct{ filepath.Join(tempDir, vcs.Name, "."+vcs.Cmd), vcs, } } 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) } os.RemoveAll(test.path) } } ./go/vcs/vcs.go0000644000014500017510000005263512246613010013030 0ustar michaelstaff// Copyright 2012 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 vcs import ( "bytes" "encoding/json" "errors" "fmt" "log" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" ) // Verbose enables verbose operation logging. var Verbose bool // ShowCmd controls whether VCS commands are printed. var ShowCmd bool // A Cmd describes how to use a version control system // like Mercurial, Git, or Subversion. type Cmd struct { Name string Cmd string // name of binary to invoke command CreateCmd string // command to download a fresh copy of a repository DownloadCmd string // command to download updates into an existing repository TagCmd []TagCmd // commands to list tags TagLookupCmd []TagCmd // commands to lookup tags before running tagSyncCmd TagSyncCmd string // command to sync to specific tag TagSyncDefault string // command to sync to default tag LogCmd string // command to list repository changelogs in an XML format Scheme []string PingCmd string } // A TagCmd describes a command to list available tags // that can be passed to Cmd.TagSyncCmd. type TagCmd struct { Cmd string // command to list tags Pattern string // regexp to extract tags from list } // vcsList lists the known version control systems var vcsList = []*Cmd{ vcsHg, vcsGit, vcsSvn, vcsBzr, } // ByCmd returns the version control system for the given // command name (hg, git, svn, bzr). func ByCmd(cmd string) *Cmd { for _, vcs := range vcsList { if vcs.Cmd == cmd { return vcs } } return nil } // vcsHg describes how to use Mercurial. var vcsHg = &Cmd{ Name: "Mercurial", Cmd: "hg", CreateCmd: "clone -U {repo} {dir}", DownloadCmd: "pull", // We allow both tag and branch names as 'tags' // for selecting a version. This lets people have // a go.release.r60 branch and a go1 branch // and make changes in both, without constantly // editing .hgtags. TagCmd: []TagCmd{ {"tags", `^(\S+)`}, {"branches", `^(\S+)`}, }, TagSyncCmd: "update -r {tag}", TagSyncDefault: "update default", LogCmd: "log --encoding=utf-8 --limit={limit} --template={template}", Scheme: []string{"https", "http", "ssh"}, PingCmd: "identify {scheme}://{repo}", } // vcsGit describes how to use Git. var vcsGit = &Cmd{ Name: "Git", Cmd: "git", CreateCmd: "clone {repo} {dir}", DownloadCmd: "pull --ff-only", TagCmd: []TagCmd{ // tags/xxx matches a git tag named xxx // origin/xxx matches a git branch named xxx on the default remote repository {"show-ref", `(?:tags|origin)/(\S+)$`}, }, TagLookupCmd: []TagCmd{ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, }, TagSyncCmd: "checkout {tag}", TagSyncDefault: "checkout master", Scheme: []string{"git", "https", "http", "git+ssh"}, PingCmd: "ls-remote {scheme}://{repo}", } // vcsBzr describes how to use Bazaar. var vcsBzr = &Cmd{ Name: "Bazaar", Cmd: "bzr", CreateCmd: "branch {repo} {dir}", // Without --overwrite bzr will not pull tags that changed. // Replace by --overwrite-tags after http://pad.lv/681792 goes in. DownloadCmd: "pull --overwrite", TagCmd: []TagCmd{{"tags", `^(\S+)`}}, TagSyncCmd: "update -r {tag}", TagSyncDefault: "update -r revno:-1", Scheme: []string{"https", "http", "bzr", "bzr+ssh"}, PingCmd: "info {scheme}://{repo}", } // vcsSvn describes how to use Subversion. var vcsSvn = &Cmd{ Name: "Subversion", Cmd: "svn", CreateCmd: "checkout {repo} {dir}", DownloadCmd: "update", // There is no tag command in subversion. // The branch information is all in the path names. LogCmd: "log --xml --limit={limit}", Scheme: []string{"https", "http", "svn", "svn+ssh"}, PingCmd: "info {scheme}://{repo}", } func (v *Cmd) String() string { return v.Name } // run runs the command line cmd in the given directory. // keyval is a list of key, value pairs. run expands // instances of {key} in cmd into value, but only after // splitting cmd into individual arguments. // If an error occurs, run prints the command line and the // command's combined stdout+stderr to standard error. // Otherwise run discards the command's output. func (v *Cmd) run(dir string, cmd string, keyval ...string) error { _, err := v.run1(dir, cmd, keyval, true) return err } // runVerboseOnly is like run but only generates error output to standard error in verbose mode. func (v *Cmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { _, err := v.run1(dir, cmd, keyval, false) return err } // runOutput is like run but returns the output of the command. func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) { return v.run1(dir, cmd, keyval, true) } // run1 is the generalized implementation of run and runOutput. func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { m := make(map[string]string) for i := 0; i < len(keyval); i += 2 { m[keyval[i]] = keyval[i+1] } args := strings.Fields(cmdline) for i, arg := range args { args[i] = expand(m, arg) } _, err := exec.LookPath(v.Cmd) if err != nil { fmt.Fprintf(os.Stderr, "go: missing %s command. See http://golang.org/s/gogetcmd\n", v.Name) return nil, err } cmd := exec.Command(v.Cmd, args...) cmd.Dir = dir cmd.Env = envForDir(cmd.Dir) if ShowCmd { fmt.Printf("cd %s\n", dir) fmt.Printf("%s %s\n", v.Cmd, strings.Join(args, " ")) } var buf bytes.Buffer cmd.Stdout = &buf cmd.Stderr = &buf err = cmd.Run() out := buf.Bytes() if err != nil { if verbose || Verbose { fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.Cmd, strings.Join(args, " ")) os.Stderr.Write(out) } return nil, err } return out, nil } // Ping pings the repo to determine if scheme used is valid. // This repo must be pingable with this scheme and VCS. func (v *Cmd) Ping(scheme, repo string) error { return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo) } // Create creates a new copy of repo in dir. // The parent of dir must exist; dir must not. func (v *Cmd) Create(dir, repo string) error { return v.run(".", v.CreateCmd, "dir", dir, "repo", repo) } // CreateAtRev creates a new copy of repo in dir at revision rev. // The parent of dir must exist; dir must not. // rev must be a valid revision in repo. func (v *Cmd) CreateAtRev(dir, repo, rev string) error { // Append revision flag to CreateCmd createAtRevCmd := v.CreateCmd + " --rev=" + rev return v.run(".", createAtRevCmd, "dir", dir, "repo", repo) } // Download downloads any new changes for the repo in dir. // dir must be a valid VCS repo compatible with v. func (v *Cmd) Download(dir string) error { return v.run(dir, v.DownloadCmd) } // Tags returns the list of available tags for the repo in dir. // dir must be a valid VCS repo compatible with v. func (v *Cmd) Tags(dir string) ([]string, error) { var tags []string for _, tc := range v.TagCmd { out, err := v.runOutput(dir, tc.Cmd) if err != nil { return nil, err } re := regexp.MustCompile(`(?m-s)` + tc.Pattern) for _, m := range re.FindAllStringSubmatch(string(out), -1) { tags = append(tags, m[1]) } } return tags, nil } // TagSync syncs the repo in dir to the named tag, // which either is a tag returned by tags or is v.TagDefault. // dir must be a valid VCS repo compatible with v and the tag must exist. func (v *Cmd) TagSync(dir, tag string) error { if v.TagSyncCmd == "" { return nil } if tag != "" { for _, tc := range v.TagLookupCmd { out, err := v.runOutput(dir, tc.Cmd, "tag", tag) if err != nil { return err } re := regexp.MustCompile(`(?m-s)` + tc.Pattern) m := re.FindStringSubmatch(string(out)) if len(m) > 1 { tag = m[1] break } } } if tag == "" && v.TagSyncDefault != "" { return v.run(dir, v.TagSyncDefault) } return v.run(dir, v.TagSyncCmd, "tag", tag) } // Log logs the changes for the repo in dir. // dir must be a valid VCS repo compatible with v. func (v *Cmd) Log(dir, logTemplate string) ([]byte, error) { if err := v.Download(dir); err != nil { return []byte{}, err } const N = 50 // how many revisions to grab return v.runOutput(dir, v.LogCmd, "limit", strconv.Itoa(N), "template", logTemplate) } // LogAtRev logs the change for repo in dir at the rev revision. // dir must be a valid VCS repo compatible with v. // rev must be a valid revision for the repo in dir. func (v *Cmd) LogAtRev(dir, rev, logTemplate string) ([]byte, error) { if err := v.Download(dir); err != nil { return []byte{}, err } // Append revision flag to LogCmd. logAtRevCmd := v.LogCmd + " --rev=" + rev return v.runOutput(dir, logAtRevCmd, "limit", strconv.Itoa(1), "template", logTemplate) } // A vcsPath describes how to convert an import path into a // version control system and repository name. type vcsPath struct { prefix string // prefix this description applies to re string // pattern for import path repo string // repository to use (expand with match of re) vcs string // version control system to use (expand with match of re) check func(match map[string]string) error // additional checks ping bool // ping for scheme to use to download repo regexp *regexp.Regexp // cached compiled form of re } // 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). 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) srcRoot = filepath.Clean(srcRoot) if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) } 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 } } // Move to parent. ndir := filepath.Dir(dir) if len(ndir) >= len(dir) { // Shouldn't happen, but just in case, stop. break } dir = ndir } return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir) } // RepoRoot represents a version control system, a repo, and a root of // where to put it on disk. type RepoRoot struct { VCS *Cmd // repo is the repository URL, including scheme Repo string // root is the import path corresponding to the root of the // repository Root string } // RepoRootForImportPath analyzes importPath to determine the // version control system, and code repository to use. func RepoRootForImportPath(importPath string, verbose bool) (*RepoRoot, error) { rr, err := RepoRootForImportPathStatic(importPath, "") if err == errUnknownSite { rr, err = RepoRootForImportDynamic(importPath, verbose) // RepoRootForImportDynamic returns error detail // that is irrelevant if the user didn't intend to use a // dynamic import in the first place. // Squelch it. if err != nil { if Verbose { log.Printf("import %q: %v", importPath, err) } err = fmt.Errorf("unrecognized import path %q", importPath) } } if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { // Do not allow wildcards in the repo root. rr = nil err = fmt.Errorf("cannot expand ... in %q", importPath) } return rr, err } var errUnknownSite = errors.New("dynamic lookup required to find mapping") // RepoRootForImportPathStatic attempts to map importPath to a // RepoRoot using the commonly-used VCS hosting sites in vcsPaths // (github.com/user/dir), or from a fully-qualified importPath already // containing its VCS type (foo.com/repo.git/dir) // // If scheme is non-empty, that scheme is forced. func RepoRootForImportPathStatic(importPath, scheme string) (*RepoRoot, error) { if strings.Contains(importPath, "://") { return nil, fmt.Errorf("invalid import path %q", importPath) } for _, srv := range vcsPaths { if !strings.HasPrefix(importPath, srv.prefix) { continue } m := srv.regexp.FindStringSubmatch(importPath) if m == nil { if srv.prefix != "" { return nil, fmt.Errorf("invalid %s import path %q", srv.prefix, importPath) } continue } // Build map of named subexpression matches for expand. match := map[string]string{ "prefix": srv.prefix, "import": importPath, } for i, name := range srv.regexp.SubexpNames() { if name != "" && match[name] == "" { match[name] = m[i] } } if srv.vcs != "" { match["vcs"] = expand(match, srv.vcs) } if srv.repo != "" { match["repo"] = expand(match, srv.repo) } if srv.check != nil { if err := srv.check(match); err != nil { return nil, err } } vcs := ByCmd(match["vcs"]) if vcs == nil { return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) } if srv.ping { if scheme != "" { match["repo"] = scheme + "://" + match["repo"] } else { for _, scheme := range vcs.Scheme { if vcs.Ping(scheme, match["repo"]) == nil { match["repo"] = scheme + "://" + match["repo"] break } } } } rr := &RepoRoot{ VCS: vcs, Repo: match["repo"], Root: match["root"], } return rr, nil } return nil, errUnknownSite } // 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". 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") } host := importPath[:slash] if !strings.Contains(host, ".") { return nil, errors.New("import path doesn't contain a hostname") } urlStr, body, err := httpsOrHTTP(importPath) if err != nil { return nil, fmt.Errorf("http/https fetch: %v", err) } defer body.Close() imports, err := parseMetaGoImports(body) if err != nil { return nil, fmt.Errorf("parsing %s: %v", importPath, err) } metaImport, err := matchGoImport(imports, importPath) if err != nil { if err != errNoMatch { return nil, fmt.Errorf("parse %s: %v", urlStr, err) } return nil, fmt.Errorf("parse %s: no go-import meta tags", urlStr) } if verbose { log.Printf("get %q: found meta tag %#v at %s", importPath, metaImport, urlStr) } // If the import was "uni.edu/bob/project", which said the // prefix was "uni.edu" and the RepoRoot was "evilroot.com", // make sure we don't trust Bob and check out evilroot.com to // "uni.edu" yet (possibly overwriting/preempting another // non-evil student). Instead, first verify the root and see // if it matches Bob's claim. if metaImport.Prefix != importPath { if verbose { log.Printf("get %q: verifying non-authoritative meta tag", importPath) } urlStr0 := urlStr urlStr, body, err = httpsOrHTTP(metaImport.Prefix) if err != nil { return nil, fmt.Errorf("fetch %s: %v", urlStr, err) } imports, err := parseMetaGoImports(body) if err != nil { return nil, fmt.Errorf("parsing %s: %v", importPath, err) } if len(imports) == 0 { return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr) } metaImport2, err := matchGoImport(imports, importPath) if err != nil || metaImport != metaImport2 { return nil, fmt.Errorf("%s and %s disagree about go-import for %s", urlStr0, urlStr, metaImport.Prefix) } } if !strings.Contains(metaImport.RepoRoot, "://") { return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, metaImport.RepoRoot) } rr := &RepoRoot{ VCS: ByCmd(metaImport.VCS), Repo: metaImport.RepoRoot, Root: metaImport.Prefix, } if rr.VCS == nil { return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, metaImport.VCS) } return rr, nil } // metaImport represents the parsed tags from HTML files. type metaImport struct { Prefix, VCS, RepoRoot string } // errNoMatch is returned from matchGoImport when there's no applicable match. var errNoMatch = errors.New("no import match") // matchGoImport returns the metaImport from imports matching importPath. // An error is returned if there are multiple matches. // errNoMatch is returned if none match. func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) { match := -1 for i, im := range imports { if !strings.HasPrefix(importPath, im.Prefix) { continue } if match != -1 { err = fmt.Errorf("multiple meta tags match import path %q", importPath) return } match = i } if match == -1 { err = errNoMatch return } return imports[match], nil } // expand rewrites s to replace {k} with match[k] for each key k in match. func expand(match map[string]string, s string) string { for k, v := range match { s = strings.Replace(s, "{"+k+"}", v, -1) } return s } // vcsPaths lists the known vcs paths. var vcsPaths = []*vcsPath{ // Google Code - new syntax { prefix: "code.google.com/", re: `^(?Pcode\.google\.com/[pr]/(?P[a-z0-9\-]+)(\.(?P[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`, repo: "https://{root}", check: googleCodeVCS, }, // Google Code - old syntax { re: `^(?P[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P/.*)?$`, check: oldGoogleCode, }, // Github { prefix: "github.com/", re: `^(?Pgithub\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, vcs: "git", repo: "https://{root}", check: noVCSSuffix, }, // Bitbucket { prefix: "bitbucket.org/", re: `^(?Pbitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, repo: "https://{root}", check: bitbucketVCS, }, // Launchpad { prefix: "launchpad.net/", re: `^(?Plaunchpad\.net/((?P[A-Za-z0-9_.\-]+)(?P/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, vcs: "bzr", repo: "https://{root}", check: launchpadVCS, }, // General syntax for any server. { re: `^(?P(?P([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?Pbzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`, ping: true, }, } func init() { // fill in cached regexps. // Doing this eagerly discovers invalid regexp syntax // without having to run a command that needs that regexp. for _, srv := range vcsPaths { srv.regexp = regexp.MustCompile(srv.re) } } // noVCSSuffix checks that the repository name does not // end in .foo for any version control system foo. // The usual culprit is ".git". func noVCSSuffix(match map[string]string) error { repo := match["repo"] for _, vcs := range vcsList { if strings.HasSuffix(repo, "."+vcs.Cmd) { return fmt.Errorf("invalid version control suffix in %s path", match["prefix"]) } } 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 { if err := noVCSSuffix(match); err != nil { return err } var resp struct { SCM string `json:"scm"` } url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}") data, err := httpGET(url) if err != nil { return err } if err := json.Unmarshal(data, &resp); err != nil { return fmt.Errorf("decoding %s: %v", url, err) } if ByCmd(resp.SCM) != nil { match["vcs"] = resp.SCM if resp.SCM == "git" { match["repo"] += ".git" } return nil } return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") } // launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, // "foo" could be a series name registered in Launchpad with its own branch, // and it could also be the name of a directory within the main project // branch one level up. func launchpadVCS(match map[string]string) error { if match["project"] == "" || match["series"] == "" { return nil } _, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format")) if err != nil { match["root"] = expand(match, "launchpad.net/{project}") match["repo"] = expand(match, "https://{root}") } return nil } ./go/vcs/discovery.go0000644000014500017510000000403712246613010014235 0ustar michaelstaff// Copyright 2012 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 vcs import ( "encoding/xml" "fmt" "io" "strings" ) // charsetReader returns a reader for the given charset. Currently // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful // error which is printed by go get, so the user can find why the package // wasn't downloaded if the encoding is not supported. Note that, in // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters // greater than 0x7f are not rejected). func charsetReader(charset string, input io.Reader) (io.Reader, error) { switch strings.ToLower(charset) { case "ascii": return input, nil default: return nil, fmt.Errorf("can't decode XML document using charset %q", charset) } } // parseMetaGoImports returns meta imports from the HTML in r. // Parsing ends at the end of the section or the beginning of the . func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { d := xml.NewDecoder(r) d.CharsetReader = charsetReader d.Strict = false var t xml.Token for { t, err = d.Token() if err != nil { return } if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { return } if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { return } e, ok := t.(xml.StartElement) if !ok || !strings.EqualFold(e.Name.Local, "meta") { continue } if attrValue(e.Attr, "name") != "go-import" { continue } if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { imports = append(imports, metaImport{ Prefix: f[0], VCS: f[1], RepoRoot: f[2], }) } } return } // attrValue returns the attribute value for the case-insensitive key // `name', or the empty string if nothing is found. func attrValue(attrs []xml.Attr, name string) string { for _, a := range attrs { if strings.EqualFold(a.Name.Local, name) { return a.Value } } return "" } ./go/vcs/http.go0000644000014500017510000000376012246613010013207 0ustar michaelstaff// Copyright 2012 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 vcs import ( "fmt" "io" "io/ioutil" "log" "net/http" "net/url" ) // httpClient is the default HTTP client, but a variable so it can be // changed by tests, without modifying http.DefaultClient. var httpClient = http.DefaultClient // httpGET returns the data from an HTTP GET request for the given URL. func httpGET(url string) ([]byte, error) { resp, err := httpClient.Get(url) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("%s: %s", url, resp.Status) } b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("%s: %v", url, err) } return b, nil } // httpsOrHTTP returns the body of either the importPath's // https resource or, if unavailable, the http resource. func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { fetch := func(scheme string) (urlStr string, res *http.Response, err error) { u, err := url.Parse(scheme + "://" + importPath) if err != nil { return "", nil, err } u.RawQuery = "go-get=1" urlStr = u.String() if Verbose { log.Printf("Fetching %s", urlStr) } res, err = httpClient.Get(urlStr) return } closeBody := func(res *http.Response) { if res != nil { res.Body.Close() } } urlStr, res, err := fetch("https") if err != nil || res.StatusCode != 200 { if Verbose { if err != nil { log.Printf("https fetch failed.") } else { log.Printf("ignoring https fetch with status code %d", res.StatusCode) } } closeBody(res) urlStr, res, err = fetch("http") } if err != nil { closeBody(res) return "", nil, err } // Note: accepting a non-200 OK here, so people can serve a // meta import in their http 404 page. if Verbose { log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode) } return urlStr, res.Body, nil } ./go/gcimporter/0000755000014500017510000000000012246613010013253 5ustar michaelstaff./go/gcimporter/gcimporter.go0000644000014500017510000005562512246613010015772 0ustar michaelstaff// 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. // Package gcimporter implements Import for gc-generated object files. // Importing this package installs Import as go/types.DefaultImport. package gcimporter import ( "bufio" "errors" "fmt" "go/ast" "go/build" "go/token" "io" "os" "path/filepath" "strconv" "strings" "text/scanner" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" ) // debugging/development support const debug = false func init() { types.DefaultImport = Import } var pkgExts = [...]string{".a", ".5", ".6", ".8"} // 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 len(path) == 0 { return } id = path 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") 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 } // 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 imports map indexed by id, // and returns the object. // // The imports 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 imports[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(imports 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, imports) pkg = p.parseExport() return } // Import imports a gc-generated package given its import path, adds the // corresponding package object to the imports map, and returns the object. // Local import paths are interpreted relative to the current working directory. // The imports map must contains all packages already imported. // func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { if path == "unsafe" { return types.Unsafe, nil } srcDir := "." if build.IsLocalImport(path) { srcDir, err = os.Getwd() if err != nil { return } } filename, id := FindPkg(path, srcDir) if filename == "" { err = fmt.Errorf("can't find import: %s", id) return } // no need to re-import if the package was imported completely before if pkg = imports[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) } }() buf := bufio.NewReader(f) if err = FindExportData(buf); err != nil { return } pkg, err = ImportData(imports, filename, id, buf) 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 imports map[string]*types.Package // package id -> package object } func (p *parser) init(filename, id string, src io.Reader, imports 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.imports = imports if debug { // check consistency of imports map for _, pkg := range imports { 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 but we have a package name, create the package and // add it to the p.imports map. // func (p *parser) getPkg(id, name string) *types.Package { // package unsafe is not in the imports map - handle explicitly if id == "unsafe" { return types.Unsafe } pkg := p.imports[id] if pkg == nil && name != "" { pkg = types.NewPackage(id, name, types.NewScope(nil)) p.imports[id] = pkg } 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, "") if pkg == nil { p.errorf("%s package not found", 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() types.Type { // "[" already consumed and lookahead known not to be "]" lit := p.expect(scanner.Int) p.expect(']') elem := p.parseType() 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() types.Type { p.expectKeyword("map") p.expect('[') key := p.parseType() p.expect(']') elem := p.parseType() return types.NewMap(key, elem) } // Name = identifier | "?" | QualifiedName . // // If materializePkg is set, the returned package is guaranteed to be set. // For fully qualified names, the returned package may be a fake package // (without name, scope, and not in the p.imports map), created for the // sole purpose of providing a package path. Fake packages are created // when the package id is not found in the p.imports map; in that case // we cannot create a real package because we don't have a package name. // For non-qualified names, the returned package is the imported package. // func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) { switch p.tok { case scanner.Ident: pkg = p.imports[p.id] name = p.lit p.next() case '?': // anonymous pkg = p.imports[p.id] p.next() case '@': // exported name prefixed with package path var id string id, name = p.parseQualifiedName() if materializePkg { // we don't have a package name - if the package // doesn't exist yet, create a fake package instead pkg = p.getPkg(id, "") if pkg == nil { pkg = types.NewPackage(id, "", nil) } } 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() (*types.Var, string) { pkg, name := p.parseName(true) typ := p.parseType() 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 name = typ.Name() case *types.Named: obj := typ.Obj() pkg = obj.Pkg() // TODO(gri) is this still correct? name = obj.Name() default: p.errorf("anonymous field expected") } anonymous = true } tag := "" if p.tok == scanner.String { tag = p.expect(scanner.String) } return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag } // StructType = "struct" "{" [ FieldList ] "}" . // FieldList = Field { ";" Field } . // func (p *parser) parseStructType() types.Type { var fields []*types.Var var tags []string p.expectKeyword("struct") p.expect('{') for i := 0; p.tok != '}'; i++ { if i > 0 { p.expect(';') } fld, tag := p.parseField() 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(false) if name == "" { name = "_" // cannot access unnamed identifiers } if p.tok == '.' { p.expectSpecial("...") isVariadic = true } typ := p.parseType() 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 != ')' { 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(nil, 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() types.Type { var methods []*types.Func p.expectKeyword("interface") p.expect('{') for i := 0; p.tok != '}'; i++ { if i > 0 { p.expect(';') } pkg, name := p.parseName(true) sig := p.parseSignature(nil) methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) } p.expect('}') return types.NewInterface(methods, nil) } // ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . // func (p *parser) parseChanType() types.Type { dir := ast.SEND | ast.RECV if p.tok == scanner.Ident { p.expectKeyword("chan") if p.tok == '<' { p.expectSpecial("<-") dir = ast.SEND } } else { p.expectSpecial("<-") p.expectKeyword("chan") dir = ast.RECV } elem := p.parseType() 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() types.Type { switch p.tok { case scanner.Ident: switch p.lit { default: return p.parseBasicType() case "struct": return p.parseStructType() case "func": // FuncType p.next() return p.parseSignature(nil) case "interface": return p.parseInterfaceType() case "map": return p.parseMapType() case "chan": return p.parseChanType() } 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()) } return p.parseArrayType() case '*': // PointerType p.next() return types.NewPointer(p.parseType()) case '<': return p.parseChanType() case '(': // "(" Type ")" p.next() typ := p.parseType() 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) 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 != '=' { typ0 = p.parseType() } 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) p.next() case scanner.String: // string_lit typ = types.Typ[types.UntypedString] val = exact.MakeFromLiteral(p.lit, token.STRING) 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() 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.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 pkg, name := p.parseName(true) sig := p.parseFunc(recv) // 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) } // package was imported completely and without errors pkg.MarkComplete() return pkg } ./go/gcimporter/exportdata.go0000644000014500017510000000523412246613010015761 0ustar michaelstaff// 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 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. // func FindExportData(r *bufio.Reader) (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) == "!\n" { // Archive file. Scan to __.PKGDEF, which should // be second archive entry. var name string var size int // First entry should be __.GOSYMDEF. // Older archives used __.SYMDEF, so allow that too. // Read and discard. if name, size, err = readGopackHeader(r); err != nil { return } if name != "__.SYMDEF" && name != "__.GOSYMDEF" { err = errors.New("go archive does not begin with __.SYMDEF or __.GOSYMDEF") return } 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 } // Second entry should be __.PKGDEF. if name, size, err = readGopackHeader(r); err != nil { return } 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 with $$. for line[0] != '$' { if line, err = r.ReadSlice('\n'); err != nil { return } } return } ./go/gcimporter/gcimporter_test.go0000644000014500017510000001072312246613010017017 0ustar michaelstaff// 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. package gcimporter import ( "go/build" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" "time" "code.google.com/p/go.tools/go/types" ) var gcPath string // Go compiler path func init() { // determine compiler var gc string switch runtime.GOARCH { case "386": gc = "8g" case "amd64": gc = "6g" case "arm": gc = "5g" default: gcPath = "unknown-GOARCH-compiler" return } gcPath = filepath.Join(build.ToolDir, gc) } func compile(t *testing.T, dirname, filename string) string { cmd := exec.Command(gcPath, filename) cmd.Dir = dirname out, err := cmd.CombinedOutput() if err != nil { t.Logf("%s", out) t.Fatalf("%s %s failed: %s", gcPath, filename, err) } archCh, _ := build.ArchChar(runtime.GOARCH) // filename should end with ".go" return filepath.Join(dirname, filename[:len(filename)-2]+archCh) } // Use the same global imports map for all tests. The effect is // as if all tested packages were imported into a single package. var imports = make(map[string]*types.Package) func testPath(t *testing.T, path string) bool { t0 := time.Now() _, err := Import(imports, path) if err != nil { t.Errorf("testPath(%s): %s", path, err) return false } t.Logf("testPath(%s): %v", path, time.Since(t0)) return true } 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)) { nimports++ } } } case f.IsDir(): nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) } } return } func TestImport(t *testing.T) { // This package does not handle gccgo export data. if runtime.Compiler == "gccgo" { return } // On cross-compile builds, the path will not exist. // Need to use GOHOSTOS, which is not available. if _, err := os.Stat(gcPath); err != nil { t.Skipf("skipping test: %v", err) } if outFn := compile(t, "testdata", "exports.go"); outFn != "" { defer os.Remove(outFn) } nimports := 0 if testPath(t, "./testdata/exports") { nimports++ } nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages t.Logf("tested %d imports", nimports) } var importedObjectTests = []struct { name string want string }{ {"unsafe.Pointer", "type Pointer unsafe.Pointer"}, {"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·2 float64) (_ float64)"}, // TODO(gri) add more tests } func TestImportedTypes(t *testing.T) { // This package does not handle gccgo export data. if runtime.Compiler == "gccgo" { 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(imports, 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(pkg, obj) if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } } } func TestIssue5815(t *testing.T) { // This package does not handle gccgo export data. if runtime.Compiler == "gccgo" { 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) } } } } } ./go/gcimporter/testdata/0000755000014500017510000000000012246613010015064 5ustar michaelstaff./go/gcimporter/testdata/exports.go0000644000014500017510000000267512246613010017131 0ustar michaelstaff// 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() ./ssa/0000755000014500017510000000000012246613010011261 5ustar michaelstaff./ssa/blockopt.go0000644000014500017510000001021312246613010013422 0ustar michaelstaff// 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 ssa // Simple block optimizations to simplify the control flow graph. // TODO(adonovan): opt: instead of creating several "unreachable" blocks // per function in the Builder, reuse a single one (e.g. at Blocks[1]) // to reduce garbage. import ( "fmt" "os" ) // If true, perform sanity checking and show progress at each // successive iteration of optimizeBlocks. Very verbose. const debugBlockOpt = false // markReachable sets Index=-1 for all blocks reachable from b. func markReachable(b *BasicBlock) { b.Index = -1 for _, succ := range b.Succs { if succ.Index == 0 { markReachable(succ) } } } // deleteUnreachableBlocks marks all reachable blocks of f and // eliminates (nils) all others, including possibly cyclic subgraphs. // func deleteUnreachableBlocks(f *Function) { const white, black = 0, -1 // We borrow b.Index temporarily as the mark bit. for _, b := range f.Blocks { b.Index = white } markReachable(f.Blocks[0]) if f.Recover != nil { markReachable(f.Recover) } for i, b := range f.Blocks { if b.Index == white { for _, c := range b.Succs { if c.Index == black { c.removePred(b) // delete white->black edge } } if debugBlockOpt { fmt.Fprintln(os.Stderr, "unreachable", b) } f.Blocks[i] = nil // delete b } } f.removeNilBlocks() } // jumpThreading attempts to apply simple jump-threading to block b, // in which a->b->c become a->c if b is just a Jump. // The result is true if the optimization was applied. // func jumpThreading(f *Function, b *BasicBlock) bool { if b.Index == 0 { return false // don't apply to entry block } if b.Instrs == nil { return false } if _, ok := b.Instrs[0].(*Jump); !ok { return false // not just a jump } c := b.Succs[0] if c == b { return false // don't apply to degenerate jump-to-self. } if c.hasPhi() { return false // not sound without more effort } for j, a := range b.Preds { a.replaceSucc(b, c) // If a now has two edges to c, replace its degenerate If by Jump. if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c { jump := new(Jump) jump.setBlock(a) a.Instrs[len(a.Instrs)-1] = jump a.Succs = a.Succs[:1] c.removePred(b) } else { if j == 0 { c.replacePred(b, a) } else { c.Preds = append(c.Preds, a) } } if debugBlockOpt { fmt.Fprintln(os.Stderr, "jumpThreading", a, b, c) } } f.Blocks[b.Index] = nil // delete b return true } // fuseBlocks attempts to apply the block fusion optimization to block // a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1. // The result is true if the optimization was applied. // func fuseBlocks(f *Function, a *BasicBlock) bool { if len(a.Succs) != 1 { return false } b := a.Succs[0] if len(b.Preds) != 1 { return false } // Eliminate jump at end of A, then copy all of B across. a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...) for _, instr := range b.Instrs { instr.setBlock(a) } // A inherits B's successors a.Succs = append(a.succs2[:0], b.Succs...) // Fix up Preds links of all successors of B. for _, c := range b.Succs { c.replacePred(b, a) } if debugBlockOpt { fmt.Fprintln(os.Stderr, "fuseBlocks", a, b) } f.Blocks[b.Index] = nil // delete b return true } // optimizeBlocks() performs some simple block optimizations on a // completed function: dead block elimination, block fusion, jump // threading. // func optimizeBlocks(f *Function) { deleteUnreachableBlocks(f) // Loop until no further progress. changed := true for changed { changed = false if debugBlockOpt { f.DumpTo(os.Stderr) mustSanityCheck(f, nil) } for _, b := range f.Blocks { // f.Blocks will temporarily contain nils to indicate // deleted blocks; we remove them at the end. if b == nil { continue } // Fuse blocks. b->c becomes bc. if fuseBlocks(f, b) { changed = true } // a->b->c becomes a->c if b contains only a Jump. if jumpThreading(f, b) { changed = true continue // (b was disconnected) } } } f.removeNilBlocks() } ./ssa/source_test.go0000644000014500017510000001567612246613010014166 0ustar michaelstaff// 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 ssa_test // This file defines tests of source-level debugging utilities. import ( "fmt" "go/ast" "go/parser" "go/token" "os" "regexp" "strings" "testing" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" ) func TestObjValueLookup(t *testing.T) { imp := importer.New(new(importer.Config)) // (uses GCImporter) f, err := parser.ParseFile(imp.Fset, "testdata/objlookup.go", nil, parser.ParseComments) if err != nil { t.Error(err) return } // 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 := imp.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 } } mainInfo := imp.CreatePackage("main", f) prog := ssa.NewProgram(imp.Fset, 0 /*|ssa.LogFunctions*/) if err := prog.CreatePackages(imp); err != nil { t.Error(err) return } mainPkg := prog.Package(mainInfo.Pkg) mainPkg.SetDebugMode(true) mainPkg.Build() // Gather all idents and objects in file. objs := make(map[types.Object]bool) var ids []*ast.Ident ast.Inspect(f, func(n ast.Node) bool { if id, ok := n.(*ast.Ident); ok { ids = append(ids, id) if obj := mainInfo.ObjectOf(id); obj != nil { objs[obj] = true } } return true }) // Check invariants for func and const objects. for obj := range objs { switch obj := obj.(type) { case *types.Func: checkFuncValue(t, prog, obj) case *types.Const: checkConstValue(t, prog, obj) } } // Check invariants for var objects. // The result varies based on the specific Ident. for _, id := range ids { if id.Name == "_" { continue } if obj, ok := mainInfo.ObjectOf(id).(*types.Var); ok { ref, _ := importer.PathEnclosingInterval(f, id.Pos(), id.Pos()) pos := imp.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 { 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.IsIdentical(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.IsIdentical(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.IsIdentical(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) { imp := importer.New(new(importer.Config)) // (uses GCImporter) f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil, parser.ParseComments) if err != nil { t.Error(err) return } mainInfo := imp.CreatePackage("main", f) prog := ssa.NewProgram(imp.Fset, 0) if err := prog.CreatePackages(imp); err != nil { t.Error(err) return } 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.DumpTo(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 := imp.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, _ := importer.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.IsIdentical(T, mainInfo.TypeOf(e)) { t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T) } } } } ./ssa/promote.go0000644000014500017510000002721612246613010013305 0ustar michaelstaff// 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 ssa // This file defines utilities for population of method sets and // synthesis of wrapper methods. // // Wrappers include: // - indirection/promotion wrappers for methods of embedded fields. // - interface method wrappers for expressions I.f. // - bound method wrappers, for uncalled obj.Method closures. // TODO(adonovan): split and rename to {methodset,wrappers}.go. import ( "fmt" "go/token" "code.google.com/p/go.tools/go/types" ) // Method returns the Function implementing method meth, building // wrapper methods on demand. // // Thread-safe. // // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) // func (prog *Program) Method(meth *types.Selection) *Function { if meth == nil { panic("Method(nil)") } T := meth.Recv() if prog.mode&LogSource != 0 { defer logStack("Method %s %v", T, meth)() } prog.methodsMu.Lock() defer prog.methodsMu.Unlock() return prog.addMethod(prog.createMethodSet(T), meth) } // makeMethods ensures that all wrappers in the complete method set of // T are generated. It is equivalent to calling prog.Method() on all // members of T.methodSet(), but acquires fewer locks. // // It reports whether the type's method set is non-empty. // // Thread-safe. // // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) // func (prog *Program) makeMethods(T types.Type) bool { tmset := T.MethodSet() n := tmset.Len() if n == 0 { return false // empty (common case) } if prog.mode&LogSource != 0 { defer logStack("makeMethods %s", T)() } prog.methodsMu.Lock() defer prog.methodsMu.Unlock() mset := prog.createMethodSet(T) if !mset.complete { mset.complete = true for i := 0; i < n; i++ { prog.addMethod(mset, tmset.At(i)) } } return true } type methodSet struct { mapping map[string]*Function // populated lazily complete bool // mapping contains all methods } // 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, meth *types.Selection) *Function { id := meth.Obj().Id() fn := mset.mapping[id] if fn == nil { fn = findMethod(prog, meth) mset.mapping[id] = fn } return fn } // TypesWithMethodSets returns a new unordered slice containing all // types in the program for which a complete (non-empty) method set is // required at run-time. // // It is the union of pkg.TypesWithMethodSets() for all pkg in // prog.AllPackages(). // // Thread-safe. // // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) // func (prog *Program) TypesWithMethodSets() []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 } // TypesWithMethodSets returns a new unordered slice containing the // set of all types referenced within package pkg and not belonging to // some other package, for which a complete (non-empty) method set is // required at run-time. // // A type belongs to a package if it is a named type or a pointer to a // named type, and the name was defined in that package. All other // types belong to no package. // // A type may appear in the TypesWithMethodSets() set of multiple // distinct packages if that type belongs to no package. Typical // compilers emit method sets for such types multiple times (using // weak symbols) into each package that references them, with the // linker performing duplicate elimination. // // This set includes the types of all operands of some MakeInterface // instruction, the types of all exported members of some package, and // all types that are subcomponents, since even types that aren't used // directly may be derived via reflection. // // Callers must not mutate the result. // func (pkg *Package) TypesWithMethodSets() []types.Type { return pkg.methodSets } // ------------------------------------------------------------------------ // 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()) } // recvType returns the receiver type of method obj. func recvType(obj *types.Func) types.Type { return obj.Type().(*types.Signature).Recv().Type() } // findMethod returns the concrete Function for the method meth, // synthesizing wrappers as needed. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func findMethod(prog *Program, meth *types.Selection) *Function { needsPromotion := len(meth.Index()) > 1 obj := meth.Obj().(*types.Func) needsIndirection := !isPointer(recvType(obj)) && isPointer(meth.Recv()) if needsPromotion || needsIndirection { return makeWrapper(prog, meth.Recv(), meth) } if _, ok := meth.Recv().Underlying().(*types.Interface); ok { return interfaceMethodWrapper(prog, meth.Recv(), obj) } return prog.declaredFunc(obj) } // makeWrapper returns a synthetic wrapper Function that optionally // performs receiver indirection, implicit field selections and then a // tailcall of a "promoted" method. For example, given these decls: // // type A struct {B} // type B struct {*C} // type C ... // func (*C) f() // // then makeWrapper(typ=A, obj={Func:(*C).f, Indices=[B,C,f]}) // synthesize this wrapper method: // // func (a A) f() { return a.B.C->f() } // // prog is the program to which the synthesized method will belong. // typ is the receiver type of the wrapper method. obj is the // type-checker's object for the promoted method; its Func may be a // concrete or an interface method. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func makeWrapper(prog *Program, typ types.Type, meth *types.Selection) *Function { obj := meth.Obj().(*types.Func) oldsig := obj.Type().(*types.Signature) recv := types.NewVar(token.NoPos, nil, "recv", typ) description := fmt.Sprintf("wrapper for %s", obj) if prog.mode&LogSource != 0 { defer logStack("make %s to (%s)", description, typ)() } fn := &Function{ name: obj.Name(), method: meth, Signature: changeRecv(oldsig, recv), Synthetic: description, Prog: prog, pos: obj.Pos(), } fn.startBody() fn.addSpilledParam(recv) createParams(fn) var v Value = fn.Locals[0] // spilled receiver if isPointer(typ) { // TODO(adonovan): consider emitting a nil-pointer check here // with a nice error message, like gc does. v = emitLoad(fn, v) } // 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). indices := meth.Index() 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 _, ok := oldsig.Recv().Type().Underlying().(*types.Interface); !ok { // concrete method if !isPointer(oldsig.Recv().Type()) { 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. // func createParams(fn *Function) { var last *Parameter tparams := fn.Signature.Params() for i, n := 0, tparams.Len(); i < n; i++ { last = fn.addParamObj(tparams.At(i)) } if fn.Signature.IsVariadic() { last.typ = types.NewSlice(last.typ) } } // Wrappers for standalone interface methods ---------------------------------- // interfaceMethodWrapper returns a synthetic wrapper function // permitting an abstract method obj to be called like a standalone // function, e.g.: // // type I interface { f(x int) R } // m := I.f // wrapper // var i I // m(i, 0) // // The wrapper is defined as if by: // // func (i I) f(x int, ...) R { // return i.f(x, ...) // } // // typ is the type of the receiver (I here). It isn't necessarily // equal to the recvType(obj) because one interface may embed another. // TODO(adonovan): more tests. // // TODO(adonovan): opt: currently the stub is created even when used // in call position: I.f(i, 0). Clearly this is suboptimal. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func interfaceMethodWrapper(prog *Program, typ types.Type, obj *types.Func) *Function { // If one interface embeds another they'll share the same // wrappers for common methods. This is safe, but it might // confuse some tools because of the implicit interface // conversion applied to the first argument. If this becomes // a problem, we should include 'typ' in the memoization key. fn, ok := prog.ifaceMethodWrappers[obj] if !ok { description := "interface method wrapper" if prog.mode&LogSource != 0 { defer logStack("(%s).%s, %s", typ, obj.Name(), description)() } fn = &Function{ name: obj.Name(), object: obj, Signature: obj.Type().(*types.Signature), Synthetic: description, pos: obj.Pos(), Prog: prog, } fn.startBody() fn.addParam("recv", typ, token.NoPos) createParams(fn) var c Call c.Call.Method = obj c.Call.Value = fn.Params[0] for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.ifaceMethodWrappers[obj] = fn } return fn } // Wrappers for bound methods ------------------------------------------------- // boundMethodWrapper returns a synthetic wrapper function that // delegates to a concrete or interface method. // The wrapper has one free variable, the method's receiver. // 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() } // // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) // func boundMethodWrapper(prog *Program, obj *types.Func) *Function { prog.methodsMu.Lock() defer prog.methodsMu.Unlock() fn, ok := prog.boundMethodWrappers[obj] if !ok { description := fmt.Sprintf("bound method wrapper for %s", obj) if prog.mode&LogSource != 0 { defer logStack("%s", description)() } fn = &Function{ name: "bound$" + obj.FullName(), Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver Synthetic: description, Prog: prog, pos: obj.Pos(), } cap := &Capture{name: "recv", typ: recvType(obj), parent: fn} fn.FreeVars = []*Capture{cap} fn.startBody() createParams(fn) var c Call if _, ok := recvType(obj).Underlying().(*types.Interface); !ok { // concrete c.Call.Value = prog.declaredFunc(obj) c.Call.Args = []Value{cap} } else { c.Call.Value = cap c.Call.Method = obj } for _, arg := range fn.Params { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.boundMethodWrappers[obj] = fn } return fn } func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { return types.NewSignature(nil, recv, s.Params(), s.Results(), s.IsVariadic()) } ./ssa/dom.go0000644000014500017510000001771112246613010012376 0ustar michaelstaff// 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 ssa // This file defines algorithms related to dominance. // Dominator tree construction ---------------------------------------- // // We use the algorithm described in Lengauer & Tarjan. 1979. A fast // algorithm for finding dominators in a flowgraph. // http://doi.acm.org/10.1145/357062.357071 // // We also apply the optimizations to SLT described in Georgiadis et // al, Finding Dominators in Practice, JGAA 2006, // http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf // to avoid the need for buckets of size > 1. import ( "fmt" "io" "math/big" "os" ) // domNode represents a node in the dominator tree. // // TODO(adonovan): export this, when ready. type domNode struct { Block *BasicBlock // the basic block; n.Block.dom == n Idom *domNode // immediate dominator (parent in dominator tree) Children []*domNode // nodes dominated by this one Level int // level number of node within tree; zero for root Pre, Post int // pre- and post-order numbering within dominator tree // Working state for Lengauer-Tarjan algorithm // (during which Pre is repurposed for CFG DFS preorder number). // TODO(adonovan): opt: measure allocating these as temps. semi *domNode // semidominator parent *domNode // parent in DFS traversal of CFG ancestor *domNode // ancestor with least sdom } // ltDfs implements the depth-first search part of the LT algorithm. func ltDfs(v *domNode, i int, preorder []*domNode) int { preorder[i] = v v.Pre = i // For now: DFS preorder of spanning tree of CFG i++ v.semi = v v.ancestor = nil for _, succ := range v.Block.Succs { if w := succ.dom; w.semi == nil { w.parent = v i = ltDfs(w, i, preorder) } } return i } // ltEval implements the EVAL part of the LT algorithm. func ltEval(v *domNode) *domNode { // TODO(adonovan): opt: do path compression per simple LT. u := v for ; v.ancestor != nil; v = v.ancestor { if v.semi.Pre < u.semi.Pre { u = v } } return u } // ltLink implements the LINK part of the LT algorithm. func ltLink(v, w *domNode) { w.ancestor = v } // buildDomTree computes the dominator tree of f using the LT algorithm. // Precondition: all blocks are reachable (e.g. optimizeBlocks has been run). // func buildDomTree(f *Function) { // The step numbers refer to the original LT paper; the // reodering is due to Georgiadis. // Initialize domNode nodes. for _, b := range f.Blocks { dom := b.dom if dom == nil { dom = &domNode{Block: b} b.dom = dom } else { dom.Block = b // reuse } } // Step 1. Number vertices by depth-first preorder. n := len(f.Blocks) preorder := make([]*domNode, n) root := f.Blocks[0].dom prenum := ltDfs(root, 0, preorder) var recover *domNode if f.Recover != nil { recover = f.Recover.dom ltDfs(recover, prenum, preorder) } buckets := make([]*domNode, n) copy(buckets, preorder) // In reverse preorder... for i := n - 1; i > 0; i-- { w := preorder[i] // Step 3. Implicitly define the immediate dominator of each node. for v := buckets[i]; v != w; v = buckets[v.Pre] { u := ltEval(v) if u.semi.Pre < i { v.Idom = u } else { v.Idom = w } } // Step 2. Compute the semidominators of all nodes. w.semi = w.parent for _, pred := range w.Block.Preds { v := pred.dom u := ltEval(v) if u.semi.Pre < w.semi.Pre { w.semi = u.semi } } ltLink(w.parent, w) if w.parent == w.semi { w.Idom = w.parent } else { buckets[i] = buckets[w.semi.Pre] buckets[w.semi.Pre] = w } } // The final 'Step 3' is now outside the loop. for v := buckets[0]; v != root; v = buckets[v.Pre] { v.Idom = root } // Step 4. Explicitly define the immediate dominator of each // node, in preorder. for _, w := range preorder[1:] { if w == root || w == recover { w.Idom = nil } else { if w.Idom != w.semi { w.Idom = w.Idom.Idom } // Calculate Children relation as inverse of Idom. w.Idom.Children = append(w.Idom.Children, w) } // Clear working state. w.semi = nil w.parent = nil w.ancestor = nil } numberDomTree(root, 0, 0, 0) // printDomTreeDot(os.Stderr, f) // debugging // printDomTreeText(os.Stderr, root, 0) // debugging if f.Prog.mode&SanityCheckFunctions != 0 { sanityCheckDomTree(f) } } // numberDomTree sets the pre- and post-order numbers of a depth-first // traversal of the dominator tree rooted at v. These are used to // answer dominance queries in constant time. Also, it sets the level // numbers (zero for the root) used for frontier computation. // func numberDomTree(v *domNode, pre, post, level int) (int, int) { v.Level = level level++ v.Pre = pre pre++ for _, child := range v.Children { pre, post = numberDomTree(child, pre, post, level) } v.Post = post post++ return pre, post } // dominates returns true if b dominates c. // Requires that dominance information is up-to-date. // func dominates(b, c *BasicBlock) bool { return b.dom.Pre <= c.dom.Pre && c.dom.Post <= b.dom.Post } // Testing utilities ---------------------------------------- // sanityCheckDomTree checks the correctness of the dominator tree // computed by the LT algorithm by comparing against the dominance // relation computed by a naive Kildall-style forward dataflow // analysis (Algorithm 10.16 from the "Dragon" book). // func sanityCheckDomTree(f *Function) { n := len(f.Blocks) // D[i] is the set of blocks that dominate f.Blocks[i], // represented as a bit-set of block indices. D := make([]big.Int, n) one := big.NewInt(1) // all is the set of all blocks; constant. var all big.Int all.Set(one).Lsh(&all, uint(n)).Sub(&all, one) // Initialization. for i, b := range f.Blocks { if i == 0 || b == f.Recover { // The root is dominated only by itself. D[i].SetBit(&D[0], 0, 1) } else { // All other blocks are (initially) dominated // by every block. D[i].Set(&all) } } // Iteration until fixed point. for changed := true; changed; { changed = false for i, b := range f.Blocks { if i == 0 || b == f.Recover { continue } // Compute intersection across predecessors. var x big.Int x.Set(&all) for _, pred := range b.Preds { x.And(&x, &D[pred.Index]) } x.SetBit(&x, i, 1) // a block always dominates itself. if D[i].Cmp(&x) != 0 { D[i].Set(&x) changed = true } } } // Check the entire relation. O(n^2). // The Recover block (if any) must be treated specially so we skip it. ok := true for i := 0; i < n; i++ { for j := 0; j < n; j++ { b, c := f.Blocks[i], f.Blocks[j] if c == f.Recover { continue } actual := dominates(b, c) expected := D[j].Bit(i) == 1 if actual != expected { fmt.Fprintf(os.Stderr, "dominates(%s, %s)==%t, want %t\n", b, c, actual, expected) ok = false } } } if !ok { panic("sanityCheckDomTree failed for " + f.String()) } } // Printing functions ---------------------------------------- // printDomTree prints the dominator tree as text, using indentation. func printDomTreeText(w io.Writer, v *domNode, indent int) { fmt.Fprintf(w, "%*s%s\n", 4*indent, "", v.Block) for _, child := range v.Children { printDomTreeText(w, child, indent+1) } } // printDomTreeDot prints the dominator tree of f in AT&T GraphViz // (.dot) format. func printDomTreeDot(w io.Writer, f *Function) { fmt.Fprintln(w, "//", f) fmt.Fprintln(w, "digraph domtree {") for i, b := range f.Blocks { v := b.dom fmt.Fprintf(w, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.Pre, b, v.Pre, v.Post) // TODO(adonovan): improve appearance of edges // belonging to both dominator tree and CFG. // Dominator tree edge. if i != 0 { fmt.Fprintf(w, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.Idom.Pre, v.Pre) } // CFG edges. for _, pred := range b.Preds { fmt.Fprintf(w, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.Pre, v.Pre) } } fmt.Fprintln(w, "}") } ./ssa/example_test.go0000644000014500017510000000637612246613010014316 0ustar michaelstaff// 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 ssa_test import ( "fmt" "go/build" "go/parser" "os" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" ) // This program demonstrates how to run the SSA builder on a "Hello, // World!" program and 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 in this package if you want a // standalone tool with similar functionality. // func Example() { const hello = ` package main import "fmt" const message = "Hello, World!" func main() { fmt.Println(message) } ` // Construct an importer. Imports will be loaded as if by 'go build'. imp := importer.New(&importer.Config{Build: &build.Default}) // Parse the input file. file, err := parser.ParseFile(imp.Fset, "hello.go", hello, 0) if err != nil { fmt.Print(err) // parse error return } // Create single-file main package and import its dependencies. mainInfo := imp.CreatePackage("main", file) // Create SSA-form program representation. var mode ssa.BuilderMode prog := ssa.NewProgram(imp.Fset, mode) if err := prog.CreatePackages(imp); err != nil { fmt.Print(err) // type error in some package return } mainPkg := prog.Package(mainInfo.Pkg) // Print out the package. mainPkg.DumpTo(os.Stdout) // Build SSA code for bodies of functions in mainPkg. mainPkg.Build() // Print out the package-level functions. mainPkg.Func("init").DumpTo(os.Stdout) mainPkg.Func("main").DumpTo(os.Stdout) // Output: // // package main: // func init func() // var init$guard bool // func main func() // const message message = "Hello, World!":untyped string // // # Name: main.init // # Package: main // # Synthetic: package initializer // func init(): // .0.entry: P:0 S:2 // t0 = *init$guard bool // if t0 goto 2.init.done else 1.init.start // .1.init.start: P:1 S:1 // *init$guard = true:bool // t1 = fmt.init() () // jump 2.init.done // .2.init.done: P:2 S:0 // return // // # Name: main.main // # Package: main // # Location: hello.go:8:6 // func main(): // .0.entry: P:0 S:0 // t0 = new [1]interface{} (varargs) *[1]interface{} // t1 = &t0[0:untyped integer] *interface{} // t2 = make interface{} <- string ("Hello, World!":string) interface{} // *t1 = t2 // t3 = slice t0[:] []interface{} // t4 = fmt.Println(t3) (n int, err error) // return } ./ssa/func.go0000644000014500017510000004413612246613010012553 0ustar michaelstaff// 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 ssa // This file implements the Function and BasicBlock types. import ( "fmt" "go/ast" "go/token" "io" "os" "strings" "code.google.com/p/go.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.%s", b.Index, b.Comment) } // 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.objectOf(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.objectOf(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 any f.Locals that are now heap-allocated. 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) if f.Prog.mode&NaiveForm == 0 { // For debugging pre-state of lifting pass: // numberRegisters(f) // f.DumpTo(os.Stderr) lift(f) } f.namedResults = nil // (used by lifting) numberRegisters(f) if f.Prog.mode&LogFunctions != 0 { f.DumpTo(os.Stderr) } 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.objectOf(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.Enclosing == nil { panic("no Value for type.Object " + obj.Name()) } outer := f.Enclosing.lookup(obj, true) // escaping v := &Capture{ 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, updating the // control-flow graph if required. // 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 // "IsNaN" // intra-package reference to same // "(*sync.WaitGroup).Add" // a declared method // "(*Return).Block" // a promotion wrapper method (intra-package ref) // "(Instruction).Block" // an interface method wrapper (intra-package ref) // "func@5.32" // an anonymous function // "bound$(*T).f" // a bound method wrapper // // If from==f.Pkg, suppress package qualification. func (f *Function) RelString(from *types.Package) string { // TODO(adonovan): expose less fragile case discrimination // using f.method. // Anonymous? if f.Enclosing != nil { return f.name } // Declared method, or promotion/indirection wrapper? if recv := f.Signature.Recv(); recv != nil { return fmt.Sprintf("(%s).%s", relType(recv.Type(), from), f.name) } // Other synthetic wrapper? if f.Synthetic != "" { // Bound method wrapper? if strings.HasPrefix(f.name, "bound$") { return f.name } // Interface method wrapper? if strings.HasPrefix(f.Synthetic, "interface ") { return fmt.Sprintf("(%s).%s", relType(f.Params[0].Type(), from), f.name) } // "package initializer" or "loaded from GC object file": fall through. } // Package-level function. // Prefix with package name for cross-package references only. if p := f.pkgobj(); p != from { return fmt.Sprintf("%s.%s", p.Path(), f.name) } return f.name } // writeSignature writes to w the signature sig in declaration syntax. // Derived from types.Signature.String(). // func writeSignature(w io.Writer, pkg *types.Package, name string, sig *types.Signature, params []*Parameter) { io.WriteString(w, "func ") if recv := sig.Recv(); recv != nil { io.WriteString(w, "(") if n := params[0].Name(); n != "" { io.WriteString(w, n) io.WriteString(w, " ") } io.WriteString(w, relType(params[0].Type(), pkg)) io.WriteString(w, ") ") params = params[1:] } io.WriteString(w, name) io.WriteString(w, "(") for i, v := range params { if i > 0 { io.WriteString(w, ", ") } io.WriteString(w, v.Name()) io.WriteString(w, " ") if sig.IsVariadic() && i == len(params)-1 { io.WriteString(w, "...") io.WriteString(w, relType(v.Type().Underlying().(*types.Slice).Elem(), pkg)) } else { io.WriteString(w, relType(v.Type(), pkg)) } } io.WriteString(w, ")") if n := sig.Results().Len(); n > 0 { io.WriteString(w, " ") r := sig.Results() if n == 1 && r.At(0).Name() == "" { io.WriteString(w, relType(r.At(0).Type(), pkg)) } else { io.WriteString(w, relType(r, pkg)) } } } func (f *Function) pkgobj() *types.Package { if f.Pkg != nil { return f.Pkg.Object } return nil } // DumpTo prints to w a human readable "disassembly" of the SSA code of // all basic blocks of function f. // func (f *Function) DumpTo(w io.Writer) { fmt.Fprintf(w, "# Name: %s\n", f.String()) if f.Pkg != nil { fmt.Fprintf(w, "# Package: %s\n", f.Pkg.Object.Path()) } if syn := f.Synthetic; syn != "" { fmt.Fprintln(w, "# Synthetic:", syn) } if pos := f.Pos(); pos.IsValid() { fmt.Fprintf(w, "# Location: %s\n", f.Prog.Fset.Position(pos)) } if f.Enclosing != nil { fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name()) } if f.Recover != nil { fmt.Fprintf(w, "# Recover: %s\n", f.Recover) } pkgobj := f.pkgobj() if f.FreeVars != nil { io.WriteString(w, "# Free variables:\n") for i, fv := range f.FreeVars { fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), pkgobj)) } } if len(f.Locals) > 0 { io.WriteString(w, "# Locals:\n") for i, l := range f.Locals { fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), pkgobj)) } } writeSignature(w, pkgobj, f.Name(), f.Signature, f.Params) io.WriteString(w, ":\n") if f.Blocks == nil { io.WriteString(w, "\t(external)\n") } // NB. column calculations are confused by non-ASCII characters. const punchcard = 80 // for old time's sake. for _, b := range f.Blocks { if b == nil { // Corrupt CFG. fmt.Fprintf(w, ".nil:\n") continue } n, _ := fmt.Fprintf(w, ".%s:", b) fmt.Fprintf(w, "%*sP:%d S:%d\n", punchcard-1-n-len("P:n S:n"), "", len(b.Preds), len(b.Succs)) if false { // CFG debugging fmt.Fprintf(w, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs) } for _, instr := range b.Instrs { io.WriteString(w, "\t") switch v := instr.(type) { case Value: l := punchcard // Left-align the instruction. if name := v.Name(); name != "" { n, _ := fmt.Fprintf(w, "%s = ", name) l -= n } // TODO(adonovan): append instructions directly to w. n, _ := io.WriteString(w, instr.String()) l -= n // Right-align the type. if t := v.Type(); t != nil { fmt.Fprintf(w, " %*s", l-10, relType(t, pkgobj)) } case nil: // Be robust against bad transforms. io.WriteString(w, "") default: io.WriteString(w, instr.String()) } io.WriteString(w, "\n") } } fmt.Fprintf(w, "\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 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, Prog, 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 NewFunction(name string, sig *types.Signature, provenance string) *Function { return &Function{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 } ./ssa/lvalue.go0000644000014500017510000000572312246613010013107 0ustar michaelstaff// 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 ssa // lvalues are the union of addressable expressions and map-index // expressions. import ( "go/ast" "go/token" "code.google.com/p/go.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 starPos token.Pos // source position, if from explicit *addr expr ast.Expr // source syntax [debug mode] } func (a *address) load(fn *Function) Value { load := emitLoad(fn, a.addr) load.pos = a.starPos return load } func (a *address) store(fn *Function, v Value) { store := emitStore(fn, a.addr, v) store.pos = a.starPos 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") } ./ssa/source.go0000644000014500017510000002202512246613010013111 0ustar michaelstaff// 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 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" "code.google.com/p/go.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 := types.NewPointer(mem.Type()).MethodSet() 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 // importer.PackageInfo.ValueOf(e) instead.) // - e is a reference to nil or a built-in function. // - the value was optimised away. // // If e is an addressable expression used an 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 Function denoted by the source-level named // function obj. // func (prog *Program) FuncValue(obj *types.Func) *Function { // Package-level function or declared method? if v := prog.packageLevelValue(obj); v != nil { return v.(*Function) } // Interface method wrapper? meth := recvType(obj).MethodSet().Lookup(obj.Pkg(), obj.Name()) return prog.Method(meth) } // ConstValue returns the SSA Value denoted by the source-level named // constant obj. The result may be a *Const, or nil if not found. // 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.) // // The Value of a defining (as opposed to referring) identifier is the // value assigned to it in its definition. Similarly, the Value of an // identifier that is the LHS of an assignment is the value assigned // to it in that statement. In all these examples, VarValue(x) returns // the value of x and isAddr==false. // // var x X // var x = X{} // x := X{} // x = X{} // // When an identifier appears in an lvalue context other than as the // LHS of an assignment, the resulting Value is the var's address, not // its value. This situation is reported by isAddr, the second // component of the result. In these examples, VarValue(x) returns // the address of x and isAddr==true. // // x.y = 0 // x[0] = 0 // _ = x[:] (where x is an array) // _ = &x // x.method() (iff method is on &x) // 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 } ./ssa/stdlib_test.go0000644000014500017510000000603412246613010014133 0ustar michaelstaff// 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 ssa_test // This file runs the SSA builder in sanity-checking mode on all // packages beneath $GOROOT and prints some summary information. // // Run test with GOMAXPROCS=8. import ( "go/build" "go/token" "os" "path/filepath" "runtime" "strings" "testing" "time" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" ) const debugMode = false func allPackages() []string { var pkgs []string root := filepath.Join(runtime.GOROOT(), "src/pkg") + string(os.PathSeparator) filepath.Walk(root, func(path string, info os.FileInfo, err error) error { // Prune the search if we encounter any of these names: switch filepath.Base(path) { case "testdata", ".hg": return filepath.SkipDir } if info.IsDir() { pkg := strings.TrimPrefix(path, root) switch pkg { case "builtin", "pkg", "code.google.com": return filepath.SkipDir // skip these subtrees case "": return nil // ignore root of tree } pkgs = append(pkgs, pkg) } return nil }) return pkgs } func TestStdlib(t *testing.T) { impctx := importer.Config{Build: &build.Default} // Load, parse and type-check the program. t0 := time.Now() imp := importer.New(&impctx) if _, _, err := imp.LoadInitialPackages(allPackages()); err != nil { t.Errorf("LoadInitialPackages failed: %s", err) return } t1 := time.Now() runtime.GC() var memstats runtime.MemStats runtime.ReadMemStats(&memstats) alloc := memstats.Alloc // Create SSA packages. prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) if err := prog.CreatePackages(imp); err != nil { t.Errorf("CreatePackages failed: %s", err) return } // Enable debug mode globally. for _, info := range imp.AllPackages() { prog.Package(info.Pkg).SetDebugMode(debugMode) } t2 := time.Now() // Build SSA IR... if it's safe. prog.BuildAll() t3 := time.Now() runtime.GC() runtime.ReadMemStats(&memstats) numPkgs := len(prog.AllPackages()) if want := 140; numPkgs < want { t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) } // Dump some statistics. allFuncs := ssa.AllFunctions(prog) var numInstrs int for fn := range allFuncs { for _, b := range fn.Blocks { numInstrs += len(b.Instrs) } } // determine line count var lineCount int imp.Fset.Iterate(func(f *token.File) bool { lineCount += f.LineCount() return true }) // NB: when benchmarking, don't forget to clear the debug + // sanity builder flags for better performance. t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0)) t.Log("#Source lines: ", lineCount) t.Log("Load/parse/typecheck: ", t1.Sub(t0)) t.Log("SSA create: ", t2.Sub(t1)) t.Log("SSA build: ", t3.Sub(t2)) // SSA stats: t.Log("#Packages: ", numPkgs) t.Log("#Functions: ", len(allFuncs)) t.Log("#Instructions: ", numInstrs) t.Log("#MB: ", (memstats.Alloc-alloc)/1000000) } ./ssa/ssa.go0000644000014500017510000015215012246613010012402 0ustar michaelstaff// 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 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" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types/typemap" "code.google.com/p/go.tools/importer" ) // 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 builtins map[*types.Builtin]*Builtin // all built-in functions, keyed by typechecker objects. mode BuilderMode // set of mode bits for SSA construction methodsMu sync.Mutex // guards the following maps: methodSets typemap.M // maps type to its concrete methodSet boundMethodWrappers map[*types.Func]*Function // wrappers for curried x.Method closures ifaceMethodWrappers map[*types.Func]*Function // wrappers for curried I.Method functions } // 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. // type Package struct { Prog *Program // the owning program Object *types.Package // the type checker's package object for this package Members map[string]Member // all package members keyed by name methodSets []types.Type // types whose method sets are included in this package 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. started int32 // atomically tested and set at start of build phase ninit int32 // number of init functions info *importer.PackageInfo // package ASTs and type information needRTTI typemap.M // types for which runtime type info is needed } // 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 // returns the containing package. (TODO: rename Pkg) } // 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 Package representing a package-level // named constant value. // // 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 } // 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, Captures, 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 // 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 for the function-local // values Capture, 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. e.g. // // Examples of Instructions that define a Value: // e.g. "x + y" (BinOp) // "len([])" (Call) // Note that the name of the Value is not printed. // // Examples of Instructions that do define (are) Values: // e.g. "return x" (Return) // "*y = x" (Store) // // (This separation 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; 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 } // 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, Captures and Locals // will be nil too. Clients performing whole-program analysis must // handle external functions specially. // // Functions are immutable values; they do not have addresses. // // Blocks[0] is the function entry point; block order is not otherwise // semantically significant, though it may affect the readability of // the disassembly. // // Recover is an optional second entry point to which control resumes // after a recovered panic. The Recover block may contain only a load // of the function's named return parameters followed by a return of // the loaded values. // // A nested function that refers to one or more lexically enclosing // local variables ("free variables") has Capture parameters. 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. // // 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. // // Type() returns the function's Signature. // type Function struct { name string object types.Object // a declared *types.Func; nil for init, wrappers, etc. method *types.Selection // info about provenance of synthetic methods [currently unused] 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 Enclosing *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 []*Capture // free variables whose values must be supplied by closure Locals []*Alloc 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 Enclosing != 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 } // 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. It is illegal for // multiple edges to exist between the same pair of blocks. // // The order of Preds and Succs are significant (to Phi and If // instructions, respectively). // type BasicBlock struct { Index int // index of this block within Func.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 *domNode // node in dominator tree; optional. gaps int // number of nil Instrs (transient). rundefers int // number of rundefers (transient) } // Pure values ---------------------------------------- // A Capture represents a free variable of the function to which it // belongs. // // Captures are used to implement anonymous functions, whose free // variables are lexically captured in a closure formed by // MakeClosure. The referent of such a capture is an Alloc or another // Capture and is considered a potentially escaping heap address, with // pointer type. // // Captures are also used to implement bound method closures. Such a // capture 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 Capture 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. // // It may have a nil, boolean, string or numeric (integer, fraction or // complex) value, or a []byte or []rune conversion of a string // constant. // // Consts may be of named types. A constant's underlying type can be // a basic type, possibly one of the "untyped" types, or a slice type // whose elements' underlying type is byte or rune. A nil constant can // have 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 equal 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 built-in function, e.g. len. // // Builtins are immutable values. Builtins do not have addresses. // Builtins can only appear in CallCommon.Func. // // Object() returns a *types.Builtin. // // Type() returns types.Typ[types.Invalid], since built-in functions // may have polymorphic or variadic types that are not expressible in // Go's type system. // type Builtin struct { object *types.Builtin // canonical types.Universe object for this built-in } // Value-defining instructions ---------------------------------------- // The Alloc instruction reserves space for a value 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 space is actually indirect(Type()). // // 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, and returns; 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.Lparen 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.start: t0, 1.if.then: 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, or a tuple (empty or len>1) whose components 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. // // 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 or ast.RangeStmt.TokPos (for // ranging over a channel), if explicit in the source. // // 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. // - between f(T) functions and (T) func f() methods. // - 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 X.Type().MethodSet() 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 statically known 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 Value // either 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 ast.ChanDir // direction of case 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 or more // 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 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 ast.StarExpr.Star, if explicit in the source. // // 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(). // // 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'. // // In the common case in which Value is a *Function, this indicates a // statically dispatched call to a package-level function, an // anonymous function, or a method of a named type. Also statically // dispatched, but less common, Value may be a *MakeClosure, indicating // an immediately applied function literal with free variables. Any // other value of Value indicates a dynamically dispatched function // call. The StaticCallee method returns the callee in these cases. // // 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) // // In both modes, HasEllipsis is true iff the last element of Args is // a slice value containing zero or more arguments to a variadic // function. (This is not semantically significant since the type of // the called function is sufficient to determine this, but it aids // readability of the printed form.) // 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) HasEllipsis bool // true iff last Args is a slice of '...' args (needed?) 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). // // Signature returns nil for a call to a built-in function. // func (c *CallCommon) Signature() *types.Signature { if c.Method != nil { return c.Method.Type().(*types.Signature) } sig, _ := c.Value.Type().Underlying().(*types.Signature) // nil for *Builtin return sig } // StaticCallee returns the called function if this is a trivially // static "call"-mode call. 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.object.Type() } func (v *Builtin) Name() string { return v.object.Name() } func (*Builtin) Referrers() *[]Instruction { return nil } func (v *Builtin) Pos() token.Pos { return token.NoPos } func (v *Builtin) Object() types.Object { return v.object } func (v *Capture) Type() types.Type { return v.typ } func (v *Capture) Name() string { return v.name } func (v *Capture) Referrers() *[]Instruction { return &v.referrers } func (v *Capture) Pos() token.Pos { return v.pos } func (v *Capture) 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) 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) Referrers() *[]Instruction { if v.Enclosing != 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 (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) } 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) } ./ssa/doc.go0000644000014500017510000001366712246613010012372 0ustar michaelstaff// 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 ssa defines a representation of the elements of Go programs // (packages, types, functions, variables and constants) using a // static single-assignment (SSA) form intermediate representation // (IR) for the bodies of functions. // // THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE. // // For an introduction to SSA form, see // http://en.wikipedia.org/wiki/Static_single_assignment_form. // This page provides a broader reading list: // http://www.dcs.gla.ac.uk/~jsinger/ssa.html. // // The level of abstraction of the SSA form is intentionally close to // the source language to facilitate construction of source analysis // tools. It is not intended for machine code generation. // // All looping, branching and switching constructs are replaced with // unstructured control flow. We may add higher-level control flow // primitives in the future to facilitate constant-time dispatch of // switch statements, for example. // // Builder encapsulates the tasks of type-checking (using go/types) // abstract syntax trees (as defined by go/ast) for the source files // comprising a Go program, and the conversion of each function from // Go ASTs to the SSA representation. // // By supplying an instance of the SourceLocator function prototype, // clients may control how the builder locates, loads and parses Go // sources files for imported packages. This package provides // MakeGoBuildLoader, which creates a loader that uses go/build to // locate packages in the Go source distribution, and go/parser to // parse them. // // The builder initially builds a naive SSA form in which all local // variables are addresses of stack locations with explicit loads and // stores. Registerisation of eligible locals and φ-node insertion // using dominance and dataflow are then performed as a second pass // called "lifting" to improve the accuracy and performance of // subsequent analyses; this pass can be skipped by setting the // NaiveForm builder flag. // // The primary interfaces of this package are: // // - Member: a named member of a Go package. // - Value: an expression that yields a value. // - Instruction: a statement that consumes values and performs computation. // // A computation that yields a result implements both the Value and // Instruction interfaces. The following table shows for each // concrete type which of these interfaces it implements. // // Value? Instruction? Member? // *Alloc ✔ ✔ // *BinOp ✔ ✔ // *Builtin ✔ ✔ // *Call ✔ ✔ // *Capture ✔ // *ChangeInterface ✔ ✔ // *ChangeType ✔ ✔ // *Const ✔ // *Convert ✔ ✔ // *DebugRef ✔ // *Defer ✔ // *Extract ✔ ✔ // *Field ✔ ✔ // *FieldAddr ✔ ✔ // *Function ✔ ✔ (func) // *Global ✔ ✔ (var) // *Go ✔ // *If ✔ // *Index ✔ ✔ // *IndexAddr ✔ ✔ // *Jump ✔ // *Lookup ✔ ✔ // *MakeChan ✔ ✔ // *MakeClosure ✔ ✔ // *MakeInterface ✔ ✔ // *MakeMap ✔ ✔ // *MakeSlice ✔ ✔ // *MapUpdate ✔ // *NamedConst ✔ (const) // *Next ✔ ✔ // *Panic ✔ // *Parameter ✔ // *Phi ✔ ✔ // *Range ✔ ✔ // *Return ✔ // *RunDefers ✔ // *Select ✔ ✔ // *Send ✔ // *Slice ✔ ✔ // *Store ✔ // *Type ✔ (type) // *TypeAssert ✔ ✔ // *UnOp ✔ ✔ // // Other key types in this package include: Program, Package, Function // and BasicBlock. // // The program representation constructed by this package is fully // resolved internally, i.e. it does not rely on the names of Values, // Packages, Functions, Types or BasicBlocks for the correct // interpretation of the program. Only the identities of objects and // the topology of the SSA and type graphs are semantically // significant. (There is one exception: Ids, used to identify field // and method names, contain strings.) Avoidance of name-based // operations simplifies the implementation of subsequent passes and // can make them very efficient. Many objects are nonetheless named // to aid in debugging, but it is not essential that the names be // either accurate or unambiguous. The public API exposes a number of // name-based maps for client convenience. // // TODO(adonovan): Consider the exceptional control-flow implications // of defer and recover(). // // TODO(adonovan): write an example showing how to visit all functions // in a Program, including package init functions, methods of named // and anon types, and functions used as values but never called // directly. See AllFunctions(). // // TODO(adonovan): write a how-to document for all the various cases // of trying to determine corresponding elements across the four // domains of source locations, ast.Nodes, types.Objects, // ssa.Values/Instructions. // package ssa ./ssa/interp/0000755000014500017510000000000012246613010012562 5ustar michaelstaff./ssa/interp/map.go0000644000014500017510000000516612246613010013676 0ustar michaelstaff// 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 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 ( "code.google.com/p/go.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 } ./ssa/interp/interp_test.go0000644000014500017510000002217212246613010015455 0ustar michaelstaff// 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 !windows,!plan9 package interp_test import ( "bytes" "fmt" "go/build" "os" "path/filepath" "strings" "testing" "time" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa/interp" ) // 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", "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/ssa/interp/testdata/. var testdataTests = []string{ "boundmeth.go", "coverage.go", "fieldprom.go", "ifaceconv.go", "ifaceprom.go", "initorder.go", "methprom.go", "mrvchain.go", "recover.go", } // These are files in $GOROOT/src/pkg/. // These tests exercise the "testing" package. var gorootSrcPkgTests = []string{ "unicode/script_test.go", "unicode/digit_test.go", "hash/crc32/crc32.go hash/crc32/crc32_generic.go hash/crc32/crc32_test.go", "path/path.go path/path_test.go", // TODO(adonovan): figure out the package loading error here: // "strings.go strings/search.go strings/search_test.go", } 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, " ") { inputs = append(inputs, dir+i) } imp := importer.New(&importer.Config{Build: &build.Default}) // TODO(adonovan): use LoadInitialPackages, then un-export ParseFiles. // Then add the following packages' tests, which pass: // "flag", "unicode", "unicode/utf8", "testing", "log", "path". files, err := importer.ParseFiles(imp.Fset, ".", inputs...) if err != nil { t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error()) return false } // 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 code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input) mainInfo := imp.CreatePackage(files[0].Name.Name, files...) if _, err := imp.LoadPackage("runtime"); err != nil { t.Errorf("LoadPackage(runtime) failed: %s", err) } prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) if err := prog.CreatePackages(imp); err != nil { t.Errorf("CreatePackages failed: %s", err) return false } prog.BuildAll() mainPkg := prog.Package(mainInfo.Pkg) if mainPkg.Func("main") == nil { testmainPkg := prog.CreateTestMainPackage(mainPkg) 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 code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input) exitCode := interp.Interpret(mainPkg, 0, 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) } } } // The "normal" success predicate. func exitsZero(exitcode int, _ string) error { if exitcode != 0 { return fmt.Errorf("exit code was %d", exitcode) } return nil } // TestTestdataFiles runs the interpreter on testdata/*.go. func TestTestdataFiles(t *testing.T) { var failures []string for _, input := range testdataTests { if !run(t, "testdata"+slash, input, exitsZero) { failures = append(failures, input) } } printFailures(failures) } // TestGorootTest runs the interpreter on $GOROOT/test/*.go. func TestGorootTest(t *testing.T) { if testing.Short() { return // too slow (~30s) } var failures []string // $GOROOT/tests are also considered a failure if they print "BUG". success := func(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 } for _, input := range gorootTestTests { if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) { failures = append(failures, input) } } for _, input := range gorootSrcPkgTests { if !run(t, filepath.Join(build.Default.GOROOT, "src/pkg")+slash, input, success) { failures = append(failures, input) } } printFailures(failures) } // TestTestmainPackage runs the interpreter on a synthetic "testmain" package. func TestTestmainPackage(t *testing.T) { 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) { imp := importer.New(&importer.Config{Build: &build.Default}) files, err := importer.ParseFiles(imp.Fset, ".", "testdata/b_test.go") if err != nil { t.Fatalf("ParseFiles failed: %s", err) } mainInfo := imp.CreatePackage("b", files...) prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) if err := prog.CreatePackages(imp); err != nil { t.Fatalf("CreatePackages failed: %s", err) } mainPkg := prog.Package(mainInfo.Pkg) if mainPkg.Func("main") != nil { t.Fatalf("unexpected main function") } if prog.CreateTestMainPackage(mainPkg) != nil { t.Fatalf("CreateTestMainPackage returned non-nil") } } ./ssa/interp/external.go0000644000014500017510000004377312246613010014751 0ustar michaelstaff// 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 interp // Emulated functions that we cannot interpret because they are // external or because they use "unsafe" or "reflect" operations. import ( "math" "os" "runtime" "syscall" "time" "code.google.com/p/go.tools/ssa" ) type externalFn func(fn *ssa.Function, 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.FullName(). // That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd]. var externals = map[string]externalFn{ "(*runtime.Func).Entry": ext۰runtime۰Func۰Entry, "(*runtime.Func).FileLine": ext۰runtime۰Func۰FileLine, "(*runtime.Func).Name": ext۰runtime۰Func۰Name, "(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).Kind": ext۰reflect۰rtype۰Kind, "(reflect.rtype).NumField": ext۰reflect۰rtype۰NumField, "(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.Min": ext۰math۰Min, "reflect.New": ext۰reflect۰New, "reflect.TypeOf": ext۰reflect۰TypeOf, "reflect.ValueOf": ext۰reflect۰ValueOf, "reflect.init": ext۰reflect۰Init, "reflect.valueInterface": ext۰reflect۰valueInterface, "runtime.Breakpoint": ext۰runtime۰Breakpoint, "runtime.Caller": ext۰runtime۰Caller, "runtime.FuncForPC": ext۰runtime۰FuncForPC, "runtime.GC": ext۰runtime۰GC, "runtime.GOMAXPROCS": ext۰runtime۰GOMAXPROCS, "runtime.Gosched": ext۰runtime۰Gosched, "runtime.NumCPU": ext۰runtime۰NumCPU, "runtime.ReadMemStats": ext۰runtime۰ReadMemStats, "runtime.SetFinalizer": ext۰runtime۰SetFinalizer, "runtime.getgoroot": ext۰runtime۰getgoroot, "strings.IndexByte": ext۰strings۰IndexByte, "sync.runtime_Syncsemcheck": ext۰sync۰runtime_Syncsemcheck, "sync/atomic.AddInt32": ext۰atomic۰AddInt32, "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, "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۰runtime۰Func۰Entry(fn *ssa.Function, args []value) value { return 0 } func ext۰runtime۰Func۰FileLine(fn *ssa.Function, args []value) value { return tuple{"unknown.go", -1} } func ext۰runtime۰Func۰Name(fn *ssa.Function, args []value) value { return "unknown" } func ext۰bytes۰Equal(fn *ssa.Function, 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(fn *ssa.Function, 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(fn *ssa.Function, args []value) value { return false } func ext۰math۰Float64frombits(fn *ssa.Function, args []value) value { return math.Float64frombits(args[0].(uint64)) } func ext۰math۰Float64bits(fn *ssa.Function, args []value) value { return math.Float64bits(args[0].(float64)) } func ext۰math۰Float32frombits(fn *ssa.Function, args []value) value { return math.Float32frombits(args[0].(uint32)) } func ext۰math۰Abs(fn *ssa.Function, args []value) value { return math.Abs(args[0].(float64)) } func ext۰math۰Exp(fn *ssa.Function, args []value) value { return math.Exp(args[0].(float64)) } func ext۰math۰Float32bits(fn *ssa.Function, args []value) value { return math.Float32bits(args[0].(float32)) } func ext۰math۰Min(fn *ssa.Function, args []value) value { return math.Min(args[0].(float64), args[1].(float64)) } func ext۰runtime۰Breakpoint(fn *ssa.Function, args []value) value { runtime.Breakpoint() return nil } func ext۰runtime۰Caller(fn *ssa.Function, args []value) value { // TODO(adonovan): actually inspect the stack. return tuple{0, "somefile.go", 42, true} } func ext۰runtime۰FuncForPC(fn *ssa.Function, args []value) value { // TODO(adonovan): actually inspect the stack. return (*value)(nil) //tuple{0, "somefile.go", 42, true} // //func FuncForPC(pc uintptr) *Func } func ext۰runtime۰getgoroot(fn *ssa.Function, args []value) value { return os.Getenv("GOROOT") } func ext۰strings۰IndexByte(fn *ssa.Function, 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۰sync۰runtime_Syncsemcheck(fn *ssa.Function, args []value) value { return nil } func ext۰runtime۰GOMAXPROCS(fn *ssa.Function, args []value) value { return runtime.GOMAXPROCS(args[0].(int)) } func ext۰runtime۰GC(fn *ssa.Function, args []value) value { runtime.GC() return nil } func ext۰runtime۰Gosched(fn *ssa.Function, args []value) value { runtime.Gosched() return nil } func ext۰runtime۰NumCPU(fn *ssa.Function, args []value) value { return runtime.NumCPU() } func ext۰runtime۰ReadMemStats(fn *ssa.Function, args []value) value { // TODO(adonovan): populate args[0].(Struct) return nil } func ext۰atomic۰LoadUint32(fn *ssa.Function, args []value) value { // TODO(adonovan): fix: not atomic! return (*args[0].(*value)).(uint32) } func ext۰atomic۰StoreUint32(fn *ssa.Function, args []value) value { // TODO(adonovan): fix: not atomic! *args[0].(*value) = args[1].(uint32) return nil } func ext۰atomic۰LoadInt32(fn *ssa.Function, args []value) value { // TODO(adonovan): fix: not atomic! return (*args[0].(*value)).(int32) } func ext۰atomic۰StoreInt32(fn *ssa.Function, args []value) value { // TODO(adonovan): fix: not atomic! *args[0].(*value) = args[1].(int32) return nil } func ext۰atomic۰CompareAndSwapInt32(fn *ssa.Function, 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(fn *ssa.Function, args []value) value { // TODO(adonovan): fix: not atomic! p := args[0].(*value) newv := (*p).(int32) + args[1].(int32) *p = newv return newv } func ext۰runtime۰SetFinalizer(fn *ssa.Function, args []value) value { return nil // ignore } func ext۰runtime۰funcname_go(fn *ssa.Function, args []value) value { // TODO(adonovan): actually inspect the stack. return (*value)(nil) //tuple{0, "somefile.go", 42, true} // //func FuncForPC(pc uintptr) *Func } func ext۰time۰now(fn *ssa.Function, args []value) value { nano := time.Now().UnixNano() return tuple{int64(nano / 1e9), int32(nano % 1e9)} } func ext۰time۰Sleep(fn *ssa.Function, args []value) value { time.Sleep(time.Duration(args[0].(int64))) return nil } func ext۰syscall۰Exit(fn *ssa.Function, args []value) value { panic(exitPanic(args[0].(int))) } func ext۰syscall۰Getwd(fn *ssa.Function, args []value) value { s, err := syscall.Getwd() return tuple{s, wrapError(err)} } func ext۰syscall۰Getpid(fn *ssa.Function, args []value) value { return syscall.Getpid() } func ext۰syscall۰RawSyscall(fn *ssa.Function, args []value) value { return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)} } func valueToBytes(v value) []byte { in := v.([]value) b := make([]byte, len(in)) for i := range in { b[i] = in[i].(byte) } return b } // The set of remaining native functions we need to implement (as needed): // crypto/aes/cipher_asm.go:10:func hasAsm() bool // crypto/aes/cipher_asm.go:11:func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) // crypto/aes/cipher_asm.go:12:func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) // crypto/aes/cipher_asm.go:13:func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32) // hash/crc32/crc32_amd64.go:12:func haveSSE42() bool // hash/crc32/crc32_amd64.go:16:func castagnoliSSE42(crc uint32, p []byte) uint32 // math/abs.go:12:func Abs(x float64) float64 // math/asin.go:19:func Asin(x float64) float64 // math/asin.go:51:func Acos(x float64) float64 // math/atan.go:95:func Atan(x float64) float64 // math/atan2.go:29:func Atan2(y, x float64) float64 // math/big/arith_decl.go:8:func mulWW(x, y Word) (z1, z0 Word) // math/big/arith_decl.go:9:func divWW(x1, x0, y Word) (q, r Word) // math/big/arith_decl.go:10:func addVV(z, x, y []Word) (c Word) // math/big/arith_decl.go:11:func subVV(z, x, y []Word) (c Word) // math/big/arith_decl.go:12:func addVW(z, x []Word, y Word) (c Word) // math/big/arith_decl.go:13:func subVW(z, x []Word, y Word) (c Word) // math/big/arith_decl.go:14:func shlVU(z, x []Word, s uint) (c Word) // math/big/arith_decl.go:15:func shrVU(z, x []Word, s uint) (c Word) // math/big/arith_decl.go:16:func mulAddVWW(z, x []Word, y, r Word) (c Word) // math/big/arith_decl.go:17:func addMulVVW(z, x []Word, y Word) (c Word) // math/big/arith_decl.go:18:func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) // math/big/arith_decl.go:19:func bitLen(x Word) (n int) // math/dim.go:13:func Dim(x, y float64) float64 // math/dim.go:26:func Max(x, y float64) float64 // math/exp.go:14:func Exp(x float64) float64 // math/exp.go:135:func Exp2(x float64) float64 // math/expm1.go:124:func Expm1(x float64) float64 // math/floor.go:13:func Floor(x float64) float64 // math/floor.go:36:func Ceil(x float64) float64 // math/floor.go:48:func Trunc(x float64) float64 // math/frexp.go:16:func Frexp(f float64) (frac float64, exp int) // math/hypot.go:17:func Hypot(p, q float64) float64 // math/ldexp.go:14:func Ldexp(frac float64, exp int) float64 // math/log.go:80:func Log(x float64) float64 // math/log10.go:9:func Log10(x float64) float64 // math/log10.go:17:func Log2(x float64) float64 // math/log1p.go:95:func Log1p(x float64) float64 // math/mod.go:21:func Mod(x, y float64) float64 // math/modf.go:13:func Modf(f float64) (int float64, frac float64) // math/remainder.go:37:func Remainder(x, y float64) float64 // math/sin.go:117:func Cos(x float64) float64 // math/sin.go:174:func Sin(x float64) float64 // math/sincos.go:15:func Sincos(x float64) (sin, cos float64) // math/sqrt.go:14:func Sqrt(x float64) float64 // math/tan.go:82:func Tan(x float64) float64 // os/file_posix.go:14:func sigpipe() // implemented in package runtime // os/signal/signal_unix.go:15:func signal_enable(uint32) // os/signal/signal_unix.go:16:func signal_recv() uint32 // runtime/debug.go:13:func LockOSThread() // runtime/debug.go:17:func UnlockOSThread() // runtime/debug.go:30:func NumCgoCall() int64 // runtime/debug.go:33:func NumGoroutine() int // runtime/debug.go:90:func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) // runtime/debug.go:114:func ThreadCreateProfile(p []StackRecord) (n int, ok bool) // runtime/debug.go:122:func GoroutineProfile(p []StackRecord) (n int, ok bool) // runtime/debug.go:132:func CPUProfile() []byte // runtime/debug.go:141:func SetCPUProfileRate(hz int) // runtime/debug.go:149:func SetBlockProfileRate(rate int) // runtime/debug.go:166:func BlockProfile(p []BlockProfileRecord) (n int, ok bool) // runtime/debug.go:172:func Stack(buf []byte, all bool) int // runtime/error.go:81:func typestring(interface{}) string // runtime/extern.go:19:func Goexit() // runtime/extern.go:27:func Caller(skip int) (pc uintptr, file string, line int, ok bool) // runtime/extern.go:34:func Callers(skip int, pc []uintptr) int // runtime/extern.go:51:func FuncForPC(pc uintptr) *Func // runtime/extern.go:68:func funcline_go(*Func, uintptr) (string, int) // runtime/extern.go:71:func mid() uint32 // runtime/pprof/pprof.go:667:func runtime_cyclesPerSecond() int64 // runtime/race.go:16:func RaceDisable() // runtime/race.go:19:func RaceEnable() // runtime/race.go:21:func RaceAcquire(addr unsafe.Pointer) // runtime/race.go:22:func RaceRelease(addr unsafe.Pointer) // runtime/race.go:23:func RaceReleaseMerge(addr unsafe.Pointer) // runtime/race.go:25:func RaceRead(addr unsafe.Pointer) // runtime/race.go:26:func RaceWrite(addr unsafe.Pointer) // runtime/race.go:28:func RaceSemacquire(s *uint32) // runtime/race.go:29:func RaceSemrelease(s *uint32) // sync/atomic/doc.go:49:func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) // sync/atomic/doc.go:52:func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) // sync/atomic/doc.go:55:func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) // sync/atomic/doc.go:58:func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool) // sync/atomic/doc.go:61:func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) // sync/atomic/doc.go:67:func AddUint32(addr *uint32, delta uint32) (new uint32) // sync/atomic/doc.go:70:func AddInt64(addr *int64, delta int64) (new int64) // sync/atomic/doc.go:73:func AddUint64(addr *uint64, delta uint64) (new uint64) // sync/atomic/doc.go:76:func AddUintptr(addr *uintptr, delta uintptr) (new uintptr) // sync/atomic/doc.go:82:func LoadInt64(addr *int64) (val int64) // sync/atomic/doc.go:88:func LoadUint64(addr *uint64) (val uint64) // sync/atomic/doc.go:91:func LoadUintptr(addr *uintptr) (val uintptr) // sync/atomic/doc.go:94:func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) // sync/atomic/doc.go:100:func StoreInt64(addr *int64, val int64) // sync/atomic/doc.go:106:func StoreUint64(addr *uint64, val uint64) // sync/atomic/doc.go:109:func StoreUintptr(addr *uintptr, val uintptr) // sync/atomic/doc.go:112:func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) // sync/runtime.go:12:func runtime_Semacquire(s *uint32) // sync/runtime.go:18:func runtime_Semrelease(s *uint32) // syscall/env_unix.go:30:func setenv_c(k, v string) // syscall/syscall_linux_amd64.go:60:func Gettimeofday(tv *Timeval) (err error) // syscall/syscall_linux_amd64.go:61:func Time(t *Time_t) (tt Time_t, err error) // syscall/syscall_linux_arm.go:28:func Seek(fd int, offset int64, whence int) (newoffset int64, err error) // time/sleep.go:25:func startTimer(*runtimeTimer) // time/sleep.go:26:func stopTimer(*runtimeTimer) bool // time/time.go:758:func now() (sec int64, nsec int32) // unsafe/unsafe.go:27:func Sizeof(v ArbitraryType) uintptr // unsafe/unsafe.go:32:func Offsetof(v ArbitraryType) uintptr // unsafe/unsafe.go:37:func Alignof(v ArbitraryType) uintptr ./ssa/interp/external_windows.go0000644000014500017510000000253412246613010016511 0ustar michaelstaff// 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 windows plan9 package interp import ( "code.google.com/p/go.tools/ssa" ) func ext۰syscall۰Close(fn *ssa.Function, args []value) value { panic("syscall.Close not yet implemented") } func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value { panic("syscall.Fstat not yet implemented") } func ext۰syscall۰Kill(fn *ssa.Function, args []value) value { panic("syscall.Kill not yet implemented") } func ext۰syscall۰Lstat(fn *ssa.Function, args []value) value { panic("syscall.Lstat not yet implemented") } func ext۰syscall۰Open(fn *ssa.Function, args []value) value { panic("syscall.Open not yet implemented") } func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value { panic("syscall.ParseDirent not yet implemented") } func ext۰syscall۰Read(fn *ssa.Function, args []value) value { panic("syscall.Read not yet implemented") } func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value { panic("syscall.ReadDirent not yet implemented") } func ext۰syscall۰Stat(fn *ssa.Function, args []value) value { panic("syscall.Stat not yet implemented") } func ext۰syscall۰Write(fn *ssa.Function, args []value) value { panic("syscall.Write not yet implemented") } ./ssa/interp/interp.go0000644000014500017510000004641312246613010014422 0ustar michaelstaff// 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 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 ( "fmt" "go/ast" "go/token" "os" "reflect" "runtime" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) 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 { 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 } 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.Method(typ.MethodSet().Lookup(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)) 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) <- copyVal(fr.get(instr.X)) case *ssa.Store: *fr.get(instr.Addr).(*value) = copyVal(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) fr.env[instr] = &(*x.(*value)).(structure)[instr.Field] case *ssa.Field: fr.env[instr] = copyVal(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] = copyVal(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 == ast.RECV { 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 == ast.RECV { 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, copyVal(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) } if fn.Enclosing == nil { name := fn.String() if ext := externals[name]; ext != nil { if i.mode&EnableTracing != 0 { fmt.Fprintln(os.Stderr, "\t(external)") } return ext(fn, args) } if fn.Blocks == nil { panic("no code for function: " + name) } } fr := &frame{ i: i, caller: caller, // currently unused; for unwinding. fn: fn, env: make(map[ssa.Value]value), block: fn.Blocks[0], 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. // // After a recovered panic, fr.result is undefined and fr.block // contains the block at which to resume control, which may be // nil for a normal return. // 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 // recovered panic }() 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.Object.Path() + "." + name) } var stdSizes = types.StdSizes{WordSize: 8, MaxAlign: 8} // 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. // // 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, filename string, args []string) (exitCode int) { i := &interpreter{ prog: mainpkg.Prog, globals: make(map[ssa.Value]*value), mode: mode, } 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) 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.Object.Path() { case "syscall": var envs []value for _, s := range os.Environ() { envs = append(envs, s) } envs = append(envs, "GOSSAINTERP=1") envs = append(envs, "GOARCH="+runtime.GOARCH) setGlobal(i, pkg, "envs", envs) case "runtime": // (Assumes no custom Sizeof used during SSA construction.) sz := stdSizes.Sizeof(pkg.Object.Scope().Lookup("MemStats").Type()) setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz)) case "os": Args := []value{filename} for _, s := range args { Args = append(Args, s) } setGlobal(i, pkg, "Args", Args) } } // 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\n", 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 } ./ssa/interp/external_unix.go0000644000014500017510000000661212246613010016003 0ustar michaelstaff// 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 !windows,!plan9 package interp import ( "syscall" "code.google.com/p/go.tools/ssa" ) func fillStat(st *syscall.Stat_t, stat structure) { stat[0] = st.Dev stat[1] = st.Ino stat[2] = st.Nlink stat[3] = st.Mode stat[4] = st.Uid stat[5] = st.Gid stat[7] = st.Rdev stat[8] = st.Size stat[9] = st.Blksize stat[10] = st.Blocks // TODO(adonovan): fix: copy Timespecs. // stat[11] = st.Atim // stat[12] = st.Mtim // stat[13] = st.Ctim } func ext۰syscall۰Close(fn *ssa.Function, args []value) value { // func Close(fd int) (err error) return wrapError(syscall.Close(args[0].(int))) } func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value { // func Fstat(fd int, stat *Stat_t) (err error) fd := args[0].(int) stat := (*args[1].(*value)).(structure) var st syscall.Stat_t err := syscall.Fstat(fd, &st) fillStat(&st, stat) return wrapError(err) } func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value { // func ReadDirent(fd int, buf []byte) (n int, err error) fd := args[0].(int) p := args[1].([]value) b := make([]byte, len(p)) n, err := syscall.ReadDirent(fd, b) for i := 0; i < n; i++ { p[i] = b[i] } return tuple{n, wrapError(err)} } func ext۰syscall۰Kill(fn *ssa.Function, args []value) value { // func Kill(pid int, sig Signal) (err error) return wrapError(syscall.Kill(args[0].(int), syscall.Signal(args[1].(int)))) } func ext۰syscall۰Lstat(fn *ssa.Function, args []value) value { // func Lstat(name string, stat *Stat_t) (err error) name := args[0].(string) stat := (*args[1].(*value)).(structure) var st syscall.Stat_t err := syscall.Lstat(name, &st) fillStat(&st, stat) return wrapError(err) } func ext۰syscall۰Open(fn *ssa.Function, args []value) value { // func Open(path string, mode int, perm uint32) (fd int, err error) { path := args[0].(string) mode := args[1].(int) perm := args[2].(uint32) fd, err := syscall.Open(path, mode, perm) return tuple{fd, wrapError(err)} } func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value { // func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) max := args[1].(int) var names []string for _, iname := range args[2].([]value) { names = append(names, iname.(string)) } consumed, count, newnames := syscall.ParseDirent(valueToBytes(args[0]), max, names) var inewnames []value for _, newname := range newnames { inewnames = append(inewnames, newname) } return tuple{consumed, count, inewnames} } func ext۰syscall۰Read(fn *ssa.Function, args []value) value { // func Read(fd int, p []byte) (n int, err error) fd := args[0].(int) p := args[1].([]value) b := make([]byte, len(p)) n, err := syscall.Read(fd, b) for i := 0; i < n; i++ { p[i] = b[i] } return tuple{n, wrapError(err)} } func ext۰syscall۰Stat(fn *ssa.Function, args []value) value { // func Stat(name string, stat *Stat_t) (err error) name := args[0].(string) stat := (*args[1].(*value)).(structure) var st syscall.Stat_t err := syscall.Stat(name, &st) fillStat(&st, stat) return wrapError(err) } func ext۰syscall۰Write(fn *ssa.Function, args []value) value { // func Write(fd int, p []byte) (n int, err error) n, err := write(args[0].(int), valueToBytes(args[1])) return tuple{n, wrapError(err)} } ./ssa/interp/external_plan9.go0000644000014500017510000000265712246613010016050 0ustar michaelstaff// 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 interp import ( "syscall" "code.google.com/p/go.tools/ssa" ) func ext۰syscall۰Close(fn *ssa.Function, args []value) value { panic("syscall.Close not yet implemented") } func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value { panic("syscall.Fstat not yet implemented") } func ext۰syscall۰Kill(fn *ssa.Function, args []value) value { panic("syscall.Kill not yet implemented") } func ext۰syscall۰Lstat(fn *ssa.Function, args []value) value { panic("syscall.Lstat not yet implemented") } func ext۰syscall۰Open(fn *ssa.Function, args []value) value { panic("syscall.Open not yet implemented") } func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value { panic("syscall.ParseDirent not yet implemented") } func ext۰syscall۰Read(fn *ssa.Function, args []value) value { panic("syscall.Read not yet implemented") } func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value { panic("syscall.ReadDirent not yet implemented") } func ext۰syscall۰Stat(fn *ssa.Function, args []value) value { panic("syscall.Stat not yet implemented") } func ext۰syscall۰Write(fn *ssa.Function, args []value) value { // func Write(fd int, p []byte) (n int, err error) n, err := write(args[0].(int), valueToBytes(args[1])) return tuple{n, wrapError(err)} } ./ssa/interp/ops.go0000644000014500017510000007624612246613010013731 0ustar michaelstaff// 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 interp import ( "bytes" "fmt" "go/token" "strings" "sync" "syscall" "unsafe" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) // 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 } // By destination type: switch t := c.Type().Underlying().(type) { case *types.Basic: // 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())) case types.UnsafePointer: panic("unsafe.Pointer constant") // not possible case types.UntypedNil: // nil was handled above. } case *types.Slice: switch et := t.Elem().Underlying().(type) { case *types.Basic: switch et.Kind() { case types.Byte: // string -> []byte var v []value for _, b := range []byte(exact.StringVal(c.Value)) { v = append(v, b) } return v case types.Rune: // string -> []rune var v []value for _, r := range []rune(exact.StringVal(c.Value)) { v = append(v, r) } return v } } } panic(fmt.Sprintf("constValue: Value.(type)=%T Type()=%s", c.Value, c.Type())) } // 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.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]. Either or both of lo and hi may be nil. func slice(x, lo, hi value) value { l := 0 if lo != nil { l = asInt(lo) } switch x := x.(type) { case string: if hi != nil { return x[l:asInt(hi)] } return x[l:] case []value: if hi != nil { return x[l:asInt(hi)] } return x[l:] case *value: // *array a := (*x).(array) if hi != nil { return []value(a)[l:asInt(hi)] } return []value(a)[l:] } 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 = copyVal(v) } else { 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 copyVal(*x.(*value)) // load 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.IsIdentical(itf.t, instr.AssertedType) { v = copyVal(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) { if CapturedOutput != nil && (fd == 1 || fd == 2) { capturedOutputMu.Lock() CapturedOutput.Write(b) // ignore errors capturedOutputMu.Unlock() } return syscall.Write(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 if _, ok := args[1].(string); ok { panic("copy([]byte, string) not yet implemented") } return copy(args[0].([]value), args[1].([]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) } 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 } ./ssa/interp/testdata/0000755000014500017510000000000012246613010014373 5ustar michaelstaff./ssa/interp/testdata/ifaceconv.go0000644000014500017510000000241112246613010016655 0ustar michaelstaffpackage main // Tests of interface conversions and type assertions. type I0 interface { } type I1 interface { f() } type I2 interface { f() g() } type C0 struct{} type C1 struct{} func (C1) f() {} type C2 struct{} func (C2) f() {} func (C2) g() {} func main() { var i0 I0 var i1 I1 var i2 I2 // Nil always causes a type assertion to fail, even to the // same type. if _, ok := i0.(I0); ok { panic("nil i0.(I0) succeeded") } if _, ok := i1.(I1); ok { panic("nil i1.(I1) succeeded") } if _, ok := i2.(I2); ok { panic("nil i2.(I2) succeeded") } // Conversions can't fail, even with nil. _ = I0(i0) _ = I0(i1) _ = I1(i1) _ = I0(i2) _ = I1(i2) _ = I2(i2) // Non-nil type assertions pass or fail based on the concrete type. i1 = C1{} if _, ok := i1.(I0); !ok { panic("C1 i1.(I0) failed") } if _, ok := i1.(I1); !ok { panic("C1 i1.(I1) failed") } if _, ok := i1.(I2); ok { panic("C1 i1.(I2) succeeded") } i1 = C2{} if _, ok := i1.(I0); !ok { panic("C2 i1.(I0) failed") } if _, ok := i1.(I1); !ok { panic("C2 i1.(I1) failed") } if _, ok := i1.(I2); !ok { panic("C2 i1.(I2) failed") } // Conversions can't fail. i1 = C1{} if I0(i1) == nil { panic("C1 I0(i1) was nil") } if I1(i1) == nil { panic("C1 I1(i1) was nil") } } ./ssa/interp/testdata/coverage.go0000644000014500017510000002422512246613010016522 0ustar michaelstaff// This interpreter test is designed to run very quickly yet provide // some coverage of a broad selection of constructs. // TODO(adonovan): more. // // Validate this file with 'go run' after editing. // TODO(adonovan): break this into small files organized by theme. package main import ( "fmt" "reflect" ) const zero int = 1 var v = []int{1 + zero: 42} // Nonliteral keys in composite literal. func init() { if x := fmt.Sprint(v); x != "[0 0 42]" { panic(x) } } func init() { // Call of variadic function with (implicit) empty slice. if x := fmt.Sprint(); x != "" { panic(x) } } type empty interface{} type I interface { f() int } type T struct{ z int } func (t T) f() int { return t.z } func use(interface{}) {} var counter = 2 // Test initialization, including init blocks containing 'return'. // Assertion is in main. func init() { counter *= 3 return counter *= 3 } func init() { counter *= 5 return counter *= 5 } // Recursion. func fib(x int) int { if x < 2 { return x } return fib(x-1) + fib(x-2) } func fibgen(ch chan int) { for x := 0; x < 10; x++ { ch <- fib(x) } close(ch) } // Goroutines and channels. func init() { ch := make(chan int) go fibgen(ch) var fibs []int for v := range ch { fibs = append(fibs, v) if len(fibs) == 10 { break } } if x := fmt.Sprint(fibs); x != "[0 1 1 2 3 5 8 13 21 34]" { panic(x) } } // Test of aliasing. func init() { type S struct { a, b string } s1 := []string{"foo", "bar"} s2 := s1 // creates an alias s2[0] = "wiz" if x := fmt.Sprint(s1, s2); x != "[wiz bar] [wiz bar]" { panic(x) } pa1 := &[2]string{"foo", "bar"} pa2 := pa1 // creates an alias (*pa2)[0] = "wiz" // * required to workaround typechecker bug if x := fmt.Sprint(*pa1, *pa2); x != "[wiz bar] [wiz bar]" { panic(x) } a1 := [2]string{"foo", "bar"} a2 := a1 // creates a copy a2[0] = "wiz" if x := fmt.Sprint(a1, a2); x != "[foo bar] [wiz bar]" { panic(x) } t1 := S{"foo", "bar"} t2 := t1 // copy t2.a = "wiz" if x := fmt.Sprint(t1, t2); x != "{foo bar} {wiz bar}" { panic(x) } } // Range over string. func init() { if x := len("Hello, 世界"); x != 13 { // bytes panic(x) } var indices []int var runes []rune for i, r := range "Hello, 世界" { runes = append(runes, r) indices = append(indices, i) } if x := fmt.Sprint(runes); x != "[72 101 108 108 111 44 32 19990 30028]" { panic(x) } if x := fmt.Sprint(indices); x != "[0 1 2 3 4 5 6 7 10]" { panic(x) } s := "" for _, r := range runes { s = fmt.Sprintf("%s%c", s, r) } if s != "Hello, 世界" { panic(s) } } func main() { print() // legal if counter != 2*3*5 { panic(counter) } // Test builtins (e.g. complex) preserve named argument types. type N complex128 var n N n = complex(1.0, 2.0) if n != complex(1.0, 2.0) { panic(n) } if x := reflect.TypeOf(n).String(); x != "main.N" { panic(x) } if real(n) != 1.0 || imag(n) != 2.0 { panic(n) } // Channel + select. ch := make(chan int, 1) select { case ch <- 1: // ok default: panic("couldn't send") } if <-ch != 1 { panic("couldn't receive") } // A "receive" select-case that doesn't declare its vars. (regression test) anint := 0 ok := false select { case anint, ok = <-ch: case anint = <-ch: default: } _ = anint _ = ok // Anon structs with methods. anon := struct{ T }{T: T{z: 1}} if x := anon.f(); x != 1 { panic(x) } var i I = anon if x := i.f(); x != 1 { panic(x) } // NB. precise output of reflect.Type.String is undefined. if x := reflect.TypeOf(i).String(); x != "struct { main.T }" && x != "struct{main.T}" { panic(x) } // fmt. const message = "Hello, World!" if fmt.Sprintf("%s, %s!", "Hello", "World") != message { panic("oops") } // Type assertion. type S struct { f int } var e empty = S{f: 42} switch v := e.(type) { case S: if v.f != 42 { panic(v.f) } default: panic(reflect.TypeOf(v)) } if i, ok := e.(I); ok { panic(i) } // Switch. var x int switch x { case 1: panic(x) fallthrough case 2, 3: panic(x) default: // ok } // empty switch switch { } // empty switch switch { default: } // empty switch switch { default: fallthrough case false: } // string -> []rune conversion. use([]rune("foo")) // Calls of form x.f(). type S2 struct { f func() int } S2{f: func() int { return 1 }}.f() // field is a func value T{}.f() // method call i.f() // interface method invocation (interface { f() int }(T{})).f() // anon interface method invocation // Map lookup. if v, ok := map[string]string{}["foo5"]; v != "" || ok { panic("oops") } // Regression test: implicit address-taken struct literal // inside literal map element. _ = map[int]*struct{}{0: {}} } // Parens should not prevent intrinsic treatment of built-ins. // (Regression test for a crash.) func init() { _ = (new)(int) _ = (make)([]int, 0) } type mybool bool func (mybool) f() {} func init() { type mybool bool var b mybool var i interface{} = b || b // result preserves types of operands _ = i.(mybool) i = false && b // result preserves type of "typed" operand _ = i.(mybool) i = b || true // result preserves type of "typed" operand _ = i.(mybool) } func init() { var x, y int var b mybool = x == y // x==y is an untyped bool b.f() } // Simple closures. func init() { b := 3 f := func(a int) int { return a + b } b++ if x := f(1); x != 5 { // 1+4 == 5 panic(x) } b++ if x := f(2); x != 7 { // 2+5 == 7 panic(x) } if b := f(1) < 16 || f(2) < 17; !b { panic("oops") } } var order []int func create(x int) int { order = append(order, x) return x } var c = create(b + 1) var a, b = create(1), create(2) // Initialization order of package-level value specs. func init() { if x := fmt.Sprint(order); x != "[2 3 1]" { panic(x) } if c != 3 { panic(c) } } // Shifts. func init() { var i int64 = 1 var u uint64 = 1 << 32 if x := i << uint32(u); x != 1 { panic(x) } if x := i << uint64(u); x != 0 { panic(x) } } // Implicit conversion of delete() key operand. func init() { type I interface{} m := make(map[I]bool) m[1] = true m[I(2)] = true if len(m) != 2 { panic(m) } delete(m, I(1)) delete(m, 2) if len(m) != 0 { panic(m) } } // An I->I conversion always succeeds. func init() { var x I if I(x) != I(nil) { panic("I->I conversion failed") } } // An I->I type-assert fails iff the value is nil. func init() { // TODO(adonovan): temporarily disabled; see comment at bottom of file. // defer func() { // r := fmt.Sprint(recover()) // if r != "interface conversion: interface is nil, not main.I" { // panic("I->I type assertion succeeed for nil value") // } // }() // var x I // _ = x.(I) } ////////////////////////////////////////////////////////////////////// // Variadic bridge methods and interface thunks. type VT int var vcount = 0 func (VT) f(x int, y ...string) { vcount++ if x != 1 { panic(x) } if len(y) != 2 || y[0] != "foo" || y[1] != "bar" { panic(y) } } type VS struct { VT } type VI interface { f(x int, y ...string) } func init() { foobar := []string{"foo", "bar"} var s VS s.f(1, "foo", "bar") s.f(1, foobar...) if vcount != 2 { panic("s.f not called twice") } fn := VI.f fn(s, 1, "foo", "bar") fn(s, 1, foobar...) if vcount != 4 { panic("I.f not called twice") } } // Multiple labels on same statement. func multipleLabels() { var trace []int i := 0 one: two: for ; i < 3; i++ { trace = append(trace, i) switch i { case 0: continue two case 1: i++ goto one case 2: break two } } if x := fmt.Sprint(trace); x != "[0 1 2]" { panic(x) } } func init() { multipleLabels() } //////////////////////////////////////////////////////////////////////// // Defer func deferMutatesResults(noArgReturn bool) (a, b int) { defer func() { if a != 1 || b != 2 { panic(fmt.Sprint(a, b)) } a, b = 3, 4 }() if noArgReturn { a, b = 1, 2 return } return 1, 2 } func init() { a, b := deferMutatesResults(true) if a != 3 || b != 4 { panic(fmt.Sprint(a, b)) } a, b = deferMutatesResults(false) if a != 3 || b != 4 { panic(fmt.Sprint(a, b)) } } // We concatenate init blocks to make a single function, but we must // run defers at the end of each block, not the combined function. var deferCount = 0 func init() { deferCount = 1 defer func() { deferCount++ }() // defer runs HERE } func init() { // Strictly speaking the spec says deferCount may be 0 or 2 // since the relative order of init blocks is unspecified. if deferCount != 2 { panic(deferCount) // defer call has not run! } } func init() { // Struct equivalence ignores blank fields. type s struct{ x, _, z int } s1 := s{x: 1, z: 3} s2 := s{x: 1, z: 3} if s1 != s2 { panic("not equal") } } func init() { // A slice var can be compared to const []T nil. var i interface{} = []string{"foo"} var j interface{} = []string(nil) if i.([]string) == nil { panic("expected i non-nil") } if j.([]string) != nil { panic("expected j nil") } // But two slices cannot be compared, even if one is nil. defer func() { r := fmt.Sprint(recover()) if r != "runtime error: comparing uncomparable type []string" { panic("want panic from slice comparison, got " + r) } }() _ = i == j // interface comparison recurses on types } // Composite literals func init() { type M map[int]int m1 := []*M{{1: 1}, &M{2: 2}} want := "map[1:1] map[2:2]" if got := fmt.Sprint(*m1[0], *m1[1]); got != want { panic(got) } m2 := []M{{1: 1}, M{2: 2}} if got := fmt.Sprint(m2[0], m2[1]); got != want { panic(got) } } func init() { // Regression test for SSA renaming bug. var ints []int for _ = range "foo" { var x int x++ ints = append(ints, x) } if fmt.Sprint(ints) != "[1 1 1]" { panic(ints) } } func init() { // Regression test for issue 6806. ch := make(chan int) select { case n, _ := <-ch: _ = n default: // The default case disables the simplification of // select to a simple receive statement. } // value,ok-form receive where TypeOf(ok) is a named boolean. type mybool bool var x int var y mybool select { case x, y = <-ch: default: // The default case disables the simplification of // select to a simple receive statement. } _ = x _ = y } ./ssa/interp/testdata/recover.go0000644000014500017510000000036112246613010016367 0ustar michaelstaffpackage main // Tests of panic/recover. func fortyTwo() (r int) { r = 42 // The next two statements simulate a 'return' statement. defer func() { recover() }() panic(nil) } func main() { if r := fortyTwo(); r != 42 { panic(r) } } ./ssa/interp/testdata/boundmeth.go0000644000014500017510000000302512246613010016707 0ustar michaelstaff// Tests of bound method closures. package main func assert(b bool) { if !b { panic("oops") } } type I int func (i I) add(x int) int { return int(i) + x } func valueReceiver() { var three I = 3 assert(three.add(5) == 8) var add3 func(int) int = three.add assert(add3(5) == 8) } type S struct{ x int } func (s *S) incr() { s.x++ } func (s *S) get() int { return s.x } func pointerReceiver() { ps := new(S) incr := ps.incr get := ps.get assert(get() == 0) incr() incr() incr() assert(get() == 3) } func addressibleValuePointerReceiver() { var s S incr := s.incr get := s.get assert(get() == 0) incr() incr() incr() assert(get() == 3) } type S2 struct { S } func promotedReceiver() { var s2 S2 incr := s2.incr get := s2.get assert(get() == 0) incr() incr() incr() assert(get() == 3) } func anonStruct() { var s struct{ S } incr := s.incr get := s.get assert(get() == 0) incr() incr() incr() assert(get() == 3) } func typeCheck() { var i interface{} i = (*S).incr _ = i.(func(*S)) // type assertion: receiver type prepended to params var s S i = s.incr _ = i.(func()) // type assertion: receiver type disappears } type errString string func (err errString) Error() string { return string(err) } // Regression test for a builder crash. func regress1(x error) func() string { return x.Error } func main() { valueReceiver() pointerReceiver() addressibleValuePointerReceiver() promotedReceiver() anonStruct() typeCheck() if e := regress1(errString("hi"))(); e != "hi" { panic(e) } } ./ssa/interp/testdata/mrvchain.go0000644000014500017510000000203512246613010016531 0ustar michaelstaff// Tests of call chaining f(g()) when g has multiple return values (MRVs). // See https://code.google.com/p/go/issues/detail?id=4573. package main func assert(actual, expected int) { if actual != expected { panic(actual) } } func g() (int, int) { return 5, 7 } func g2() (float64, float64) { return 5, 7 } func f1v(x int, v ...int) { assert(x, 5) assert(v[0], 7) } func f2(x, y int) { assert(x, 5) assert(y, 7) } func f2v(x, y int, v ...int) { assert(x, 5) assert(y, 7) assert(len(v), 0) } func complexArgs() (float64, float64) { return 5, 7 } func appendArgs() ([]string, string) { return []string{"foo"}, "bar" } func h() (i interface{}, ok bool) { m := map[int]string{1: "hi"} i, ok = m[1] // string->interface{} conversion within multi-valued expression return } func main() { f1v(g()) f2(g()) f2v(g()) if c := complex(complexArgs()); c != 5+7i { panic(c) } if s := append(appendArgs()); len(s) != 2 || s[0] != "foo" || s[1] != "bar" { panic(s) } i, ok := h() if !ok || i.(string) != "hi" { panic(i) } } ./ssa/interp/testdata/fieldprom.go0000644000014500017510000000255012246613010016705 0ustar michaelstaffpackage main // Tests of field promotion logic. type A struct { x int y *int } type B struct { p int q *int } type C struct { A *B } type D struct { a int C } func assert(cond bool) { if !cond { panic("failed") } } func f1(c C) { assert(c.x == c.A.x) assert(c.y == c.A.y) assert(&c.x == &c.A.x) assert(&c.y == &c.A.y) assert(c.p == c.B.p) assert(c.q == c.B.q) assert(&c.p == &c.B.p) assert(&c.q == &c.B.q) c.x = 1 *c.y = 1 c.p = 1 *c.q = 1 } func f2(c *C) { assert(c.x == c.A.x) assert(c.y == c.A.y) assert(&c.x == &c.A.x) assert(&c.y == &c.A.y) assert(c.p == c.B.p) assert(c.q == c.B.q) assert(&c.p == &c.B.p) assert(&c.q == &c.B.q) c.x = 1 *c.y = 1 c.p = 1 *c.q = 1 } func f3(d D) { assert(d.x == d.C.A.x) assert(d.y == d.C.A.y) assert(&d.x == &d.C.A.x) assert(&d.y == &d.C.A.y) assert(d.p == d.C.B.p) assert(d.q == d.C.B.q) assert(&d.p == &d.C.B.p) assert(&d.q == &d.C.B.q) d.x = 1 *d.y = 1 d.p = 1 *d.q = 1 } func f4(d *D) { assert(d.x == d.C.A.x) assert(d.y == d.C.A.y) assert(&d.x == &d.C.A.x) assert(&d.y == &d.C.A.y) assert(d.p == d.C.B.p) assert(d.q == d.C.B.q) assert(&d.p == &d.C.B.p) assert(&d.q == &d.C.B.q) d.x = 1 *d.y = 1 d.p = 1 *d.q = 1 } func main() { y := 123 c := C{ A{x: 42, y: &y}, &B{p: 42, q: &y}, } assert(&c.x == &c.A.x) f1(c) f2(&c) d := D{C: c} f3(d) f4(&d) } ./ssa/interp/testdata/a_test.go0000644000014500017510000000037412246613010016205 0ustar michaelstaffpackage a import "testing" func TestFoo(t *testing.T) { t.Error("foo") } func TestBar(t *testing.T) { t.Error("bar") } func BenchmarkWiz(b *testing.B) { b.Error("wiz") } // Don't test Examples since that testing package needs pipe(2) for that. ./ssa/interp/testdata/b_test.go0000644000014500017510000000020312246613010016175 0ustar michaelstaffpackage b import "testing" func NotATest(t *testing.T) { t.Error("foo") } func NotABenchmark(b *testing.B) { b.Error("wiz") } ./ssa/interp/testdata/methprom.go0000644000014500017510000000173612246613010016564 0ustar michaelstaffpackage main // Tests of method promotion logic. type A struct{ magic int } func (a A) x() { if a.magic != 1 { panic(a.magic) } } func (a *A) y() *A { return a } type B struct{ magic int } func (b B) p() { if b.magic != 2 { panic(b.magic) } } func (b *B) q() { if b != theC.B { panic("oops") } } type I interface { f() } type impl struct{ magic int } func (i impl) f() { if i.magic != 3 { panic("oops") } } type C struct { A *B I } func assert(cond bool) { if !cond { panic("failed") } } var theC = C{ A: A{1}, B: &B{2}, I: impl{3}, } func addr() *C { return &theC } func value() C { return theC } func main() { // address addr().x() if addr().y() != &theC.A { panic("oops") } addr().p() addr().q() addr().f() // addressable value var c C = value() c.x() if c.y() != &c.A { panic("oops") } c.p() c.q() c.f() // non-addressable value value().x() // value().y() // not in method set value().p() value().q() value().f() } ./ssa/interp/testdata/ifaceprom.go0000644000014500017510000000152612246613010016673 0ustar michaelstaffpackage main // Test of promotion of methods of an interface embedded within a // struct. In particular, this test exercises that the correct // method is called. type I interface { one() int two() string } type S struct { I } type impl struct{} func (impl) one() int { return 1 } func (impl) two() string { return "two" } func main() { var s S s.I = impl{} if one := s.I.one(); one != 1 { panic(one) } if one := s.one(); one != 1 { panic(one) } closOne := s.I.one if one := closOne(); one != 1 { panic(one) } closOne = s.one if one := closOne(); one != 1 { panic(one) } if two := s.I.two(); two != "two" { panic(two) } if two := s.two(); two != "two" { panic(two) } closTwo := s.I.two if two := closTwo(); two != "two" { panic(two) } closTwo = s.two if two := closTwo(); two != "two" { panic(two) } } ./ssa/interp/testdata/initorder.go0000644000014500017510000000200312246613010016714 0ustar michaelstaffpackage main // Test of initialization order of package-level vars. type T int var counter int func next() int { c := counter counter++ return c } func (T) next() int { return next() } var t T func makeOrder1() [6]int { return [6]int{f1, b1, d1, e1, c1, a1} } func makeOrder2() [6]int { return [6]int{f2, b2, d2, e2, c2, a2} } var order1 = makeOrder1() func main() { // order1 is a package-level variable: // [a-f]1 are initialized is reference order. if order1 != [6]int{0, 1, 2, 3, 4, 5} { panic(order1) } // order2 is a local variable: // [a-f]2 are initialized in lexical order. var order2 = makeOrder2() if order2 != [6]int{11, 7, 9, 10, 8, 6} { panic(order2) } } // The references traversal visits through calls to package-level // functions (next), method expressions (T.next) and methods (t.next). var a1, b1 = next(), next() var c1, d1 = T.next(0), T.next(0) var e1, f1 = t.next(), t.next() var a2, b2 = next(), next() var c2, d2 = T.next(0), T.next(0) var e2, f2 = t.next(), t.next() ./ssa/interp/value.go0000644000014500017510000002444612246613010014237 0ustar michaelstaff// 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 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" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types/typemap" "code.google.com/p/go.tools/ssa" ) 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 = typemap.MakeHasher() ) // hashType returns a hash for t such that // types.IsIdentical(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.IsIdentical. func sameType(x, y types.Type) bool { if x == nil { return y == nil } return y != nil && types.IsIdentical(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.IsIdentical(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)) } // copyVal returns a copy of value v. // TODO(adonovan): add tests of aliasing and mutation. func copyVal(v value) value { if v == nil { panic("copyVal(nil)") } switch v := v.(type) { case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string, unsafe.Pointer: return v case map[value]value: return v case *hashmap: return v case chan value: return v case *value: return v case *ssa.Function, *ssa.Builtin, *closure: return v case iface: return v case []value: return v case structure: a := make(structure, len(v)) copy(a, v) return a case array: a := make(array, len(v)) copy(a, v) return a case tuple: break case rtype: return v } panic(fmt.Sprintf("cannot copy %T", 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 toWriter(w io.Writer, 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(w, "%v", v) case map[value]value: io.WriteString(w, "map[") sep := "" for k, e := range v { io.WriteString(w, sep) sep = " " toWriter(w, k) io.WriteString(w, ":") toWriter(w, e) } io.WriteString(w, "]") case *hashmap: io.WriteString(w, "map[") sep := " " for _, e := range v.table { for e != nil { io.WriteString(w, sep) sep = " " toWriter(w, e.key) io.WriteString(w, ":") toWriter(w, e.value) e = e.next } } io.WriteString(w, "]") case chan value: fmt.Fprintf(w, "%v", v) // (an address) case *value: if v == nil { io.WriteString(w, "") } else { fmt.Fprintf(w, "%p", v) } case iface: fmt.Fprintf(w, "(%s, ", v.t) toWriter(w, v.v) io.WriteString(w, ")") case structure: io.WriteString(w, "{") for i, e := range v { if i > 0 { io.WriteString(w, " ") } toWriter(w, e) } io.WriteString(w, "}") case array: io.WriteString(w, "[") for i, e := range v { if i > 0 { io.WriteString(w, " ") } toWriter(w, e) } io.WriteString(w, "]") case []value: io.WriteString(w, "[") for i, e := range v { if i > 0 { io.WriteString(w, " ") } toWriter(w, e) } io.WriteString(w, "]") case *ssa.Function, *ssa.Builtin, *closure: fmt.Fprintf(w, "%p", v) // (an address) case rtype: io.WriteString(w, v.t.String()) case tuple: // Unreachable in well-formed Go programs io.WriteString(w, "(") for i, e := range v { if i > 0 { io.WriteString(w, ", ") } toWriter(w, e) } io.WriteString(w, ")") default: fmt.Fprintf(w, "<%T>", v) } } // Implements printing of Go values in the style of built-in println. func toString(v value) string { var b bytes.Buffer toWriter(&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]} } ./ssa/interp/reflect.go0000644000014500017510000003610612246613010014543 0ustar michaelstaff// 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 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" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) 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", nil) // rtype is the concrete type the interpreter uses to implement the // reflect.Type interface. Since its type is opaque to the target // language, we use a types.Basic. // // type rtype 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(fn *ssa.Function, args []value) value { // Signature: func() return nil } func ext۰reflect۰rtype۰Bits(fn *ssa.Function, 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)) } switch basic.Kind() { case types.Int8, types.Uint8: return 8 case types.Int16, types.Uint16: return 16 case types.Int, types.UntypedInt: // Assume sizeof(int) is same on host and target; ditto uint. return reflect.TypeOf(int(0)).Bits() case types.Uintptr: // Assume sizeof(uintptr) is same on host and target. return reflect.TypeOf(uintptr(0)).Bits() case types.Int32, types.Uint32: return 32 case types.Int64, types.Uint64: return 64 case types.Float32: return 32 case types.Float64, types.UntypedFloat: return 64 case types.Complex64: return 64 case types.Complex128, types.UntypedComplex: return 128 default: panic(fmt.Sprintf("reflect.Type.Bits(%s)", basic)) } return nil } func ext۰reflect۰rtype۰Elem(fn *ssa.Function, 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(fn *ssa.Function, 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۰Kind(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) uint return uint(reflectKind(args[0].(rtype).t)) } func ext۰reflect۰rtype۰NumField(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) int return args[0].(rtype).t.Underlying().(*types.Struct).NumFields() } func ext۰reflect۰rtype۰NumMethod(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) int return args[0].(rtype).t.MethodSet().Len() } func ext۰reflect۰rtype۰NumOut(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) int return args[0].(rtype).t.(*types.Signature).Results().Len() } func ext۰reflect۰rtype۰Out(fn *ssa.Function, 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(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) uintptr // (Assumes no custom Sizeof used during SSA construction.) return uintptr(stdSizes.Sizeof(args[0].(rtype).t)) } func ext۰reflect۰rtype۰String(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) string return args[0].(rtype).t.String() } func ext۰reflect۰New(fn *ssa.Function, 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۰TypeOf(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) string return makeReflectType(rtype{args[0].(iface).t}) } func ext۰reflect۰ValueOf(fn *ssa.Function, args []value) value { // Signature: func (interface{}) reflect.Value itf := args[0].(iface) return makeReflectValue(itf.t, itf.v) } 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(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) uint return uint(reflectKind(rV2T(args[0]).t)) } func ext۰reflect۰Value۰String(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) string return toString(rV2V(args[0])) } func ext۰reflect۰Value۰Type(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) reflect.Type return makeReflectType(rV2T(args[0])) } func ext۰reflect۰Value۰Uint(fn *ssa.Function, 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(fn *ssa.Function, 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)) } return nil // unreachable } func ext۰reflect۰Value۰MapIndex(fn *ssa.Function, 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(fn *ssa.Function, 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(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) int return len(rV2V(args[0]).(structure)) } func ext۰reflect۰Value۰NumMethod(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) int return rV2T(args[0]).t.MethodSet().Len() } func ext۰reflect۰Value۰Pointer(fn *ssa.Function, 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)) default: panic(fmt.Sprintf("reflect.(Value).Pointer(%T)", v)) } return nil // unreachable } func ext۰reflect۰Value۰Index(fn *ssa.Function, 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)) } return nil // unreachable } func ext۰reflect۰Value۰Bool(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) bool return rV2V(args[0]).(bool) } func ext۰reflect۰Value۰CanAddr(fn *ssa.Function, args []value) value { // Signature: func (v reflect.Value) bool // Always false for our representation. return false } func ext۰reflect۰Value۰CanInterface(fn *ssa.Function, args []value) value { // Signature: func (v reflect.Value) bool // Always true for our representation. return true } func ext۰reflect۰Value۰Elem(fn *ssa.Function, 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)) } return nil // unreachable } func ext۰reflect۰Value۰Field(fn *ssa.Function, 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(fn *ssa.Function, 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(fn *ssa.Function, args []value) value { // Signature: func (v reflect.Value) interface{} return ext۰reflect۰valueInterface(fn, args) } func ext۰reflect۰Value۰Int(fn *ssa.Function, 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)) } return nil // unreachable } func ext۰reflect۰Value۰IsNil(fn *ssa.Function, 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)) } return nil // unreachable } func ext۰reflect۰Value۰IsValid(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) bool return rV2V(args[0]) != nil } func ext۰reflect۰Value۰Set(fn *ssa.Function, args []value) value { // TODO(adonovan): implement. return nil } func ext۰reflect۰valueInterface(fn *ssa.Function, 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(fn *ssa.Function, 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(nil, types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false) fn := ssa.NewFunction(name, sig, "fake reflect method") fn.Pkg = pkg fn.Prog = pkg.Prog return fn } func initReflect(i *interpreter) { i.reflectPackage = &ssa.Package{ Prog: i.prog, Object: reflectTypesPackage, Members: make(map[string]ssa.Member), } i.rtypeMethods = methodSet{ "Bits": newMethod(i.reflectPackage, rtypeType, "Bits"), "Elem": newMethod(i.reflectPackage, rtypeType, "Elem"), "Field": newMethod(i.reflectPackage, rtypeType, "Field"), "Kind": newMethod(i.reflectPackage, rtypeType, "Kind"), "NumField": newMethod(i.reflectPackage, rtypeType, "NumField"), "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"), } } ./ssa/create.go0000644000014500017510000001726712246613010013070 0ustar michaelstaff// 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 ssa // This file implements the CREATE phase of SSA construction. // See builder.go for explanation. import ( "fmt" "go/ast" "go/token" "os" "strings" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" ) // BuilderMode is a bitmask of options for diagnostics and checking. type BuilderMode uint const ( LogPackages BuilderMode = 1 << iota // Dump package inventory to stderr LogFunctions // Dump function SSA code to stderr LogSource // Show source locations as SSA builder progresses SanityCheckFunctions // Perform sanity checking of function bodies NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers BuildSerially // Build packages serially, not in parallel. ) // NewProgram returns a new SSA Program initially containing no // packages. // // fset specifies the mapping from token positions to source location // that will be used by all ASTs of this 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), builtins: make(map[*types.Builtin]*Builtin), boundMethodWrappers: make(map[*types.Func]*Function), ifaceMethodWrappers: make(map[*types.Func]*Function), mode: mode, } // Create Values for built-in functions. for _, name := range types.Universe.Names() { if obj, ok := types.Universe.Lookup(name).(*types.Builtin); ok { prog.builtins[obj] = &Builtin{obj} } } 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.values[obj] = nil // for needMethods 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: fn := &Function{ name: name, object: obj, Signature: obj.Type().(*types.Signature), syntax: syntax, pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: pkg.Prog, } if syntax == nil { fn.Synthetic = "loaded from gc object file" } pkg.values[obj] = fn if fn.Signature.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.objectOf(id), nil) } } } case token.VAR: for _, spec := range decl.Specs { for _, id := range spec.(*ast.ValueSpec).Names { if !isBlankIdent(id) { memberFromObject(pkg, pkg.objectOf(id), spec) } } } case token.TYPE: for _, spec := range decl.Specs { id := spec.(*ast.TypeSpec).Name if !isBlankIdent(id) { memberFromObject(pkg, pkg.objectOf(id), nil) } } } case *ast.FuncDecl: id := decl.Name if decl.Recv == nil && id.Name == "init" { return // no object } if !isBlankIdent(id) { memberFromObject(pkg, pkg.objectOf(id), decl) } } } // CreatePackage constructs and returns an SSA Package from an // error-free package described by info, and populates its Members // mapping. // // Repeated calls with the same info return the same Package. // // The real work of building SSA form for each function is not done // until a subsequent call to Package.Build(). // func (prog *Program) CreatePackage(info *importer.PackageInfo) *Package { if info.Err != nil { panic(fmt.Sprintf("package %s has errors: %s", info, info.Err)) } if p := prog.packages[info.Pkg]; p != nil { return p // already loaded } p := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: info.Pkg, info: info, // 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(info.Files) > 0 { // Go source package. for _, file := range info.Files { for _, decl := range file.Decls { membersFromDecl(p, decl) } } } else { // GC-compiled binary package. // No code. // No position information. scope := p.Object.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) } } } } // Add initializer guard variable. initguard := &Global{ Pkg: p, name: "init$guard", typ: types.NewPointer(tBool), } p.Members[initguard.Name()] = initguard if prog.mode&LogPackages != 0 { p.DumpTo(os.Stderr) } if info.Importable { prog.imported[info.Pkg.Path()] = p } prog.packages[p.Object] = p if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(p) } return p } // CreatePackages creates SSA Packages for all error-free packages // loaded by the specified Importer. // // If all packages were error-free, it is safe to call // prog.BuildAll(), and nil is returned. Otherwise an error is // returned. // func (prog *Program) CreatePackages(imp *importer.Importer) error { var errpkgs []string for _, info := range imp.AllPackages() { if info.Err != nil { errpkgs = append(errpkgs, info.Pkg.Path()) } else { prog.CreatePackage(info) } } if errpkgs != nil { return fmt.Errorf("couldn't create these SSA packages due to type errors: %s", strings.Join(errpkgs, ", ")) } return nil } // 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] } ./ssa/const.go0000644000014500017510000000755312246613010012750 0ustar michaelstaff// 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 ssa // This file defines the Const SSA value type. import ( "fmt" "go/token" "strconv" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.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 untyped integer constant that evaluates to i. func intConst(i int64) *Const { return NewConst(exact.MakeInt64(i), types.Typ[types.UntypedInt]) } // 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) } // 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) valstring() string { if c.Value == nil { return "nil" } else if c.Value.Kind() == exact.String { s := exact.StringVal(c.Value) const max = 20 if len(s) > max { s = s[:max-3] + "..." // abbreviate } return strconv.Quote(s) } else { return c.Value.String() } } func (c *Const) Name() string { return fmt.Sprintf("%s:%s", c.valstring(), c.typ) } func (v *Const) String() string { return v.Name() } func (c *Const) Type() types.Type { return c.typ } func (c *Const) Referrers() *[]Instruction { 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) } ./ssa/sanity.go0000644000014500017510000003021212246613010013115 0ustar michaelstaff// 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 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" "code.google.com/p/go.tools/go/types" ) type sanity struct { reporter io.Writer fn *Function block *BasicBlock 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.DumpTo(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)) } // Check that value-defining instructions have valid types. 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) } } // TODO(adonovan): sanity-check Consts used as instruction Operands(), // e.g. reject Consts with "untyped" types. // // All other non-Instruction Values can be found via their // enclosing Function or Package. } func (s *sanity) checkFinalInstr(idx int, instr Instruction) { switch 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 } // TODO(adonovan): check number and types of results 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 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.Enclosing == nil { continue // only anon functions are local } } 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) 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.pkgobj()) // must not crash // All functions have a package, except wrappers (which are // shared across packages, or duplicated as weak symbols in a // separate-compilation model), and error.Error. if fn.Pkg == nil { if strings.Contains(fn.Synthetic, "wrapper") || 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) } } for i, p := range fn.Params { if p.Parent() != fn { s.errorf("Param %s at index %d has wrong parent", p.Name(), i) } } for i, fv := range fn.FreeVars { if fv.Parent() != fn { s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i) } } 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.Enclosing != fn { s.errorf("AnonFuncs[%d]=%s but %s.Enclosing=%s", i, anon, anon, anon.Enclosing) } } 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.Object == 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.Object.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 { panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s", pkg.Object.Path(), mem, obj.Name(), name)) } if obj.Pos() != mem.Pos() { panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos())) } } } ./ssa/testmain.go0000644000014500017510000001407312246613010013441 0ustar michaelstaff// 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 ssa // CreateTestMainPackage synthesizes a main package that runs all the // tests of the supplied packages. // It is closely coupled to src/cmd/go/test.go and src/pkg/testing. import ( "go/ast" "go/token" "os" "strings" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" ) // 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 { testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("testmain", "testmain", nil), } prog.packages[testmain.Object] = testmain // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() var expfuncs []*Function // all exported functions of *_test.go in pkgs, unordered for _, pkg := range pkgs { // Initialize package to test. var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) // Enumerate its possible tests/benchmarks. 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") { expfuncs = append(expfuncs, f) } } } init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Members[init.name] = init testingPkg := prog.ImportedPackage("testing") if testingPkg == nil { // If the program doesn't import "testing", it can't // contain any tests. // TODO(adonovan): but it might contain Examples. // Support them (by just calling them directly). return 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) // } main := &Function{ name: "main", Signature: new(types.Signature), Synthetic: "test main function", Prog: prog, Pkg: testmain, } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", Enclosing: 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() main.startBody() var c Call c.Call.Value = testingMain tests := testMainSlice(main, expfuncs, "Test", testingMainParams.At(1).Type()) benchmarks := testMainSlice(main, expfuncs, "Benchmark", testingMainParams.At(2).Type()) examples := testMainSlice(main, expfuncs, "Example", testingMainParams.At(3).Type()) _, noTests := tests.(*Const) // i.e. nil slice _, noBenchmarks := benchmarks.(*Const) _, noExamples := examples.(*Const) if noTests && noBenchmarks && noExamples { return nil } c.Call.Args = []Value{matcher, tests, benchmarks, examples} // Emit: testing.Main(nil, tests, benchmarks, examples) emitTailCall(main, &c) main.finishBody() testmain.Members["main"] = main if prog.mode&LogPackages != 0 { testmain.DumpTo(os.Stderr) } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(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 expfuncs whose name starts with prefix (one of // "Test", "Benchmark" or "Example") and whose type is appropriate. // It returns the slice value. // func testMainSlice(fn *Function, expfuncs []*Function, prefix string, slice types.Type) Value { tElem := slice.(*types.Slice).Elem() tFunc := tElem.Underlying().(*types.Struct).Field(1).Type() var testfuncs []*Function for _, f := range expfuncs { if isTest(f.Name(), prefix) && types.IsIdentical(f.Signature, tFunc) { testfuncs = append(testfuncs, f) } } if testfuncs == nil { return nilConst(slice) } tString := types.Typ[types.String] tPtrString := types.NewPointer(tString) tPtrElem := types.NewPointer(tElem) tPtrFunc := types.NewPointer(tFunc) // 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, NewConst(exact.MakeString(testfunc.Name()), tString)) // 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) } // Emit: slice array[:] sl := &Slice{X: array} sl.setType(slice) return fn.emit(sl) } // 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):]) } ./ssa/lift.go0000644000014500017510000003713212246613010012554 0ustar michaelstaff// 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 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" "code.google.com/p/go.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 *domNode) { p := &df[u.Block.Index] *p = append(*p, v.Block) } // 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 *domNode) { // Encounter each node u in postorder of dom tree. for _, child := range u.Children { df.build(child) } for _, vb := range u.Block.Succs { if v := vb.dom; v.Idom != u { df.add(u, v) } } for _, w := range u.Children { for _, vb := range df[w.Block.Index] { // TODO(adonovan): opt: use word-parallel bitwise union. if v := vb.dom; v.Idom != u { df.add(u, v) } } } } func buildDomFrontier(fn *Function) domFrontier { df := make(domFrontier, len(fn.Blocks)) df.build(fn.Blocks[0].dom) if fn.Recover != nil { df.build(fn.Recover.dom) } return df } // 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. // func lift(fn *Function) { // TODO(adonovan): opt: lots of little optimizations may be // worthwhile here, especially if they cause us to avoid // buildDomTree. 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. buildDomTree(fn) 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) { continue // discard it } 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()) } // 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 } 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.Block, r, newPhis) } } ./ssa/visit.go0000644000014500017510000000306512246613010012752 0ustar michaelstaff// 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 ssa // This file defines utilities for visiting the SSA representation of // a Program. // // TODO(adonovan): test coverage. // AllFunctions finds and returns the set of functions potentially // needed by program prog, as determined by a simple linker-style // reachability algorithm starting from the members and method-sets of // each package. The result may include anonymous functions and // synthetic wrappers. // // Precondition: all packages are built. // func AllFunctions(prog *Program) map[*Function]bool { visit := visitor{ prog: prog, seen: make(map[*Function]bool), } visit.program() return visit.seen } type visitor struct { prog *Program seen map[*Function]bool } func (visit *visitor) program() { for _, pkg := range visit.prog.AllPackages() { for _, mem := range pkg.Members { if fn, ok := mem.(*Function); ok { visit.function(fn) } } } for _, T := range visit.prog.TypesWithMethodSets() { mset := T.MethodSet() for i, n := 0, mset.Len(); i < n; i++ { visit.function(visit.prog.Method(mset.At(i))) } } } func (visit *visitor) function(fn *Function) { if !visit.seen[fn] { visit.seen[fn] = true for _, b := range fn.Blocks { for _, instr := range b.Instrs { var buf [10]*Value // avoid alloc in common case for _, op := range instr.Operands(buf[:0]) { if fn, ok := (*op).(*Function); ok { visit.function(fn) } } } } } } ./ssa/builder.go0000644000014500017510000017441712246613010013254 0ustar michaelstaff// 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 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" "sync/atomic" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" ) type opaqueType struct { types.Type name string } func (t *opaqueType) String() string { return t.name } var ( varOk = types.NewVar(token.NoPos, nil, "ok", tBool) varIndex = types.NewVar(token.NoPos, nil, "index", tInt) // Type constants. tBool = types.Typ[types.Bool] tByte = types.Typ[types.Byte] tInt = types.Typ[types.Int] tInvalid = types.Typ[types.Invalid] 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) vFalse = NewConst(exact.MakeBool(false), 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 } } switch cond := b.expr(fn, e).(type) { case *Const: // Dispatch constant conditions statically. if exact.BoolVal(cond.Value) { emitJump(fn, t) } else { emitJump(fn, f) } default: emitIf(fn, cond, 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 { var typ types.Type var tuple Value 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 = fn.Pkg.typeOf(e) return fn.emit(&c) case *ast.IndexExpr: mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map) typ = mapt.Elem() lookup := &Lookup{ X: b.expr(fn, e.X), Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), CommaOk: true, } lookup.setPos(e.Lbrack) tuple = fn.emit(lookup) case *ast.TypeAssertExpr: t := fn.Pkg.typeOf(e).(*types.Tuple).At(0).Type() return emitTypeTest(fn, b.expr(fn, e.X), t, e.Lparen) case *ast.UnaryExpr: // must be receive <- typ = fn.Pkg.typeOf(e.X).Underlying().(*types.Chan).Elem() unop := &UnOp{ Op: token.ARROW, X: b.expr(fn, e.X), CommaOk: true, } unop.setPos(e.OpPos) tuple = fn.emit(unop) default: panic(fmt.Sprintf("unexpected exprN: %T", e)) } // The typechecker sets the type of the expression to just the // asserted type in the "value, ok" form, not to *types.Tuple // (though it includes the valueOk operand in its error messages). tuple.(interface { setType(types.Type) }).setType(types.NewTuple( types.NewVar(token.NoPos, nil, "value", typ), varOk, )) return tuple } // 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]) } 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 vFalse // 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, 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" b.compLit(fn, v, e) // initialize in place return &address{addr: v, expr: e} case *ast.ParenExpr: return b.addr(fn, e.X, escaping) case *ast.SelectorExpr: switch sel := fn.Pkg.info.Selections[e]; sel.Kind() { case types.PackageObj: obj := sel.Obj() if v := fn.Prog.packageLevelValue(obj); v != nil { return &address{addr: v, expr: e} } panic("undefined package-qualified name: " + obj.Name()) case types.FieldVal: 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()), 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), expr: e} case *ast.StarExpr: return &address{addr: b.expr(fn, e.X), starPos: e.Star, expr: e} } panic(fmt.Sprintf("unexpected address expression: %T", e)) } // exprInPlace emits to fn code to initialize the lvalue loc with the // value of expression e. // // 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. // func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { 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) loc.store(fn, ptr) // copy address return } } if _, ok := loc.(*address); ok { if _, ok := loc.typ().Underlying().(*types.Interface); ok { // e.g. var x interface{} = T{...} // Can't in-place initialize an interface value. // Fall back to copying. } else { addr := loc.address(fn) b.compLit(fn, addr, e) // in place emitDebugRef(fn, e, addr, true) return } } } loc.store(fn, b.expr(fn, e)) // copy value } // 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 { // Is expression a constant? if v := fn.Pkg.info.ValueOf(e); v != nil { return NewConst(v, fn.Pkg.typeOf(e)) } e = unparen(e) v := b.expr0(fn, e) if fn.debugInfo() { emitDebugRef(fn, e, v, false) } return v } func (b *builder) expr0(fn *Function, e ast.Expr) Value { switch e := e.(type) { case *ast.BasicLit: panic("non-constant BasicLit") // unreachable case *ast.FuncLit: posn := fn.Prog.Fset.Position(e.Type.Func) fn2 := &Function{ name: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature), pos: e.Type.Func, Enclosing: 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(fn.Pkg.typeOf(e)) 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), fn.Pkg.typeOf(e), e.Lparen) case *ast.CallExpr: typ := fn.Pkg.typeOf(e) if fn.Pkg.info.IsType(e.Fun) { // Explicit type conversion, e.g. string(x) or big.Int(x) x := b.expr(fn, e.Args[0]) y := emitConv(fn, x, typ) 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.objectOf(id).(*types.Builtin); ok { if v := b.builtin(fn, obj, e.Args, typ, e.Lparen); v != nil { return v } } } // Regular function call. var v Call b.setCall(fn, e, &v.Call) v.setType(typ) 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. 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(fn.Pkg.typeOf(e)) 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), fn.Pkg.typeOf(e), 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(fn.Pkg.typeOf(e))) default: panic("illegal op in BinaryExpr: " + e.Op.String()) } case *ast.SliceExpr: var low, high 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: unreachable() } if e.High != nil { high = b.expr(fn, e.High) } if e.Low != nil { low = b.expr(fn, e.Low) } v := &Slice{ X: x, Low: low, High: high, } v.setPos(e.Lbrack) v.setType(fn.Pkg.typeOf(e)) return fn.emit(v) case *ast.Ident: obj := fn.Pkg.objectOf(e) // Universal built-in or nil? switch obj := obj.(type) { case *types.Builtin: return fn.Prog.builtins[obj] case *types.Nil: return nilConst(fn.Pkg.typeOf(e)) } // 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: switch sel := fn.Pkg.info.Selections[e]; sel.Kind() { case types.PackageObj: return b.expr(fn, e.Sel) case types.MethodExpr: // (*T).f or T.f, the method f from the method-set of type T. // For declared methods, a simple conversion will suffice. return emitConv(fn, fn.Prog.Method(sel), fn.Pkg.typeOf(e)) case types.MethodVal: // e.f where e is an expression and f is a method. // The result is a bound method closure. obj := sel.Obj().(*types.Func) wantAddr := isPointer(recvType(obj)) escaping := true v := b.receiver(fn, e.X, wantAddr, escaping, sel) c := &MakeClosure{ Fn: boundMethodWrapper(fn.Prog, obj), Bindings: []Value{v}, } c.setPos(e.Sel.Pos()) c.setType(fn.Pkg.typeOf(e)) 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.Pos()) emitDebugRef(fn, e.Sel, v, false) 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 build 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 c.HasEllipsis = e.Ellipsis != 0 // Is this a method call? if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok { switch sel := fn.Pkg.info.Selections[selector]; sel.Kind() { case types.PackageObj: // e.g. fmt.Println case types.MethodExpr: // 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. case types.FieldVal: // A field access, not a method call. case types.MethodVal: obj := sel.Obj().(*types.Func) wantAddr := isPointer(recvType(obj)) escaping := true v := b.receiver(fn, selector.X, wantAddr, escaping, sel) if _, ok := deref(v.Type()).Underlying().(*types.Interface); ok { // Invoke-mode call. c.Value = v c.Method = obj } else { // "Call"-mode call. // TODO(adonovan): fix: in -build=G // mode, declaredFunc panics for // cross-package calls. c.Value = fn.Prog.declaredFunc(obj) c.Args = append(c.Args, v) } return default: panic(fmt.Sprintf("illegal (%s).%s() call; X:%T", fn.Pkg.typeOf(selector.X), selector.Sel.Name, selector.X)) } } // 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.IsVariadic() { 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.IsVariadic() { 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))) // Don't set pos for implicit Allocs. a := emitNew(fn, at, token.NoPos) 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) } 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.exprInPlace(fn, lval, spec.Values[i]) } 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. var lvals []lvalue for _, lhs := range lhss { var lval lvalue = blank{} if !isBlankIdent(lhs) { if isDef { // Local may be "redeclared" in the same // scope, so don't blindly create anew. obj := fn.Pkg.objectOf(lhs.(*ast.Ident)) if _, ok := fn.objects[obj]; !ok { fn.addNamedLocal(obj) } } lval = b.addr(fn, lhs, false) // non-escaping } lvals = append(lvals, lval) } if len(lhss) == len(rhss) { // e.g. x, y = f(), g() if len(lhss) == 1 { // x = type{...} // Optimization: in-place construction // of composite literals. b.exprInPlace(fn, lvals[0], rhss[0]) } else { // Parallel assignment. All reads must occur // before all updates, precluding exprInPlace. // TODO(adonovan): opt: is it sound to // perform exprInPlace if !isDef? var rvals []Value for _, rval := range rhss { rvals = append(rvals, b.expr(fn, rval)) } for i, lval := range lvals { lval.store(fn, rvals[i]) } } } else { // e.g. x, y = pos() tuple := b.exprN(fn, rhss[0]) 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, typically allocated by Alloc. // Nested composite literals are recursively initialized in place // where possible. // // 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) { typ := deref(fn.Pkg.typeOf(e)) switch t := typ.Underlying().(type) { case *types.Struct: for i, e := range e.Elts { fieldIndex := i 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 e = kv.Value break } } } sf := t.Field(fieldIndex) faddr := &FieldAddr{ X: addr, Field: fieldIndex, } faddr.setType(types.NewPointer(sf.Type())) fn.emit(faddr) b.exprInPlace(fn, &address{addr: faddr, expr: e}, e) } 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 } var idx *Const for _, e := range e.Elts { if kv, ok := e.(*ast.KeyValueExpr); ok { idx = b.expr(fn, kv.Key).(*Const) 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) b.exprInPlace(fn, &address{addr: iaddr, expr: e}, e) } if t != at { // slice s := &Slice{X: array} s.setPos(e.Lbrace) s.setType(typ) emitStore(fn, addr, fn.emit(s)) } case *types.Map: m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))} m.setPos(e.Lbrace) m.setType(typ) emitStore(fn, addr, fn.emit(m)) for _, e := range e.Elts { e := e.(*ast.KeyValueExpr) loc := &element{ m: m, k: emitConv(fn, b.expr(fn, e.Key), t.Key()), t: t.Elem(), pos: e.Colon, } b.exprInPlace(fn, loc, e.Value) } 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. // More efficient strategies (e.g. multiway dispatch) // are possible if all cases 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 much 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. More efficient strategies (e.g. multiway // dispatch) are possible. // 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 // if ok1 then goto S1 else goto .caseT2 // .caseT2: // t2, ok2 := typeswitch,ok x // if ok2 then goto S1 else goto .caseNil // .S1: // y := x // ...S1... // goto done // .caseNil: // if t2, ok2 := typeswitch,ok x // if x == nil then goto SN else goto .caseT3 // .SN: // y := x // ...SN... // goto done // .caseT3: // t3, ok3 := typeswitch,ok x // 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 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.TypeCaseVar(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) } 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: ast.SEND, 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: ast.RECV, 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: ast.RECV, 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 == ast.RECV { tElem := st.Chan.Type().Underlying().(*types.Chan).Elem() vars = append(vars, types.NewVar(token.NoPos, nil, "", 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 } 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. // func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (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. length = intConst(arr.Len()) } else { // length = len(x). var c Call c.Call.Value = fn.Prog.builtins[types.Universe.Lookup("len").(*types.Builtin)] c.Call.Args = []Value{x} c.setType(tInt) length = fn.emit(&c) } index := fn.addLocal(tInt, token.NoPos) emitStore(fn, index, intConst(-1)) 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)) 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().(*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, types.NewVar(token.NoPos, nil, "k", tk), types.NewVar(token.NoPos, nil, "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( types.NewVar(token.NoPos, nil, "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 !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) case *types.Chan: k, loop, done = b.rangeChan(fn, x, tk, s.TokPos) 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 } b.assignOp(fn, b.addr(fn, s.X, false), vOne, 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. // If the panicking function has named results, // control resumes at the Recover block to load those // locals (which may be mutated by the deferred call) // and return them. if fn.namedResults != nil { // Optimization: if we can prove the deferred call // won't cause recovery from panic, we can avoid a // Recover block. // We scan the callee for calls to recover() iff: // - it's a static call // - to a function in the same package // (other packages' SSA building happens concurrently) // - whose SSA building has started (Blocks != nil) // - and finished (i.e. not this function) // NB, this is always true for: defer func() { ... } () // // TODO(adonovan): optimize interpackage cases, e.g. // (sync.Mutex).Unlock(), (io.Closer).Close if callee, ok := v.Call.Value.(*Function); ok && callee.Pkg == fn.Pkg && callee != fn && callee.Blocks != nil && !callsRecover(callee) { // Deferred call cannot cause recovery from panic. } else { 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) } } // 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) { // Run function calls deferred in this function when // falling off the end of the body block. 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 } var fn *Function if decl.Recv == nil && id.Name == "init" { pkg.ninit++ fn = &Function{ name: fmt.Sprintf("init$%d", pkg.ninit), Signature: new(types.Signature), pos: decl.Name.NamePos, Pkg: pkg, Prog: pkg.Prog, syntax: decl, } var v Call v.Call.Value = fn v.setType(types.NewTuple()) pkg.init.emit(&v) } else { fn = pkg.values[pkg.objectOf(id)].(*Function) } 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 idempotent and thread-safe. // func (prog *Program) BuildAll() { 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() { if !atomic.CompareAndSwapInt32(&p.started, 0, 1) { return // already started } if p.info == nil { return // synthetic package, e.g. "testmain" } // 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 obj := range p.values { if obj.IsExported() { p.needMethodsOf(obj.Type()) } } if p.Prog.mode&LogSource != 0 { defer logStack("build %s", p)() } init := p.init init.startBody() // 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) // Call the init() function of each package we import. for _, pkg := range p.info.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())) } 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)} } else { lval = blank{} } b.exprInPlace(init, lval, varinit.Rhs) } 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)) } } } // 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.info.Files { for _, decl := range file.Decls { if decl, ok := decl.(*ast.FuncDecl); ok { b.buildFuncDecl(p, decl) } } } // Finish up init(). emitJump(init, done) init.currentBlock = done init.emit(new(Return)) init.finishBody() p.info = nil // We no longer need ASTs or go/types deductions. } // 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()))) } // Only valid during p's create and build phases. func (p *Package) typeOf(e ast.Expr) types.Type { return p.info.TypeOf(e) } // 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. // func (p *Package) needMethodsOf(T types.Type) { p.needMethods(T, false) } // Recursive case: skip => don't call makeMethods(T). func (p *Package) needMethods(T types.Type, skip bool) { // Each package maintains its own set of types it has visited. if p.needRTTI.Set(T, true) != nil { return // already seen } // Prune the recursion if we find a named or *named type // belonging to another package. 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 } if owner != p.Object { return // belongs to another package } } // All the actual method sets live in the Program so that // multiple packages can share a single copy in memory of the // symbols that would be compiled into multiple packages (as // weak symbols). if !skip && p.Prog.makeMethods(T) { p.methodSets = append(p.methodSets, T) } switch t := T.(type) { case *types.Basic: // nop case *types.Interface: for i, n := 0, t.NumMethods(); i < n; i++ { p.needMethodsOf(t.Method(i).Type()) } case *types.Pointer: p.needMethodsOf(t.Elem()) case *types.Slice: p.needMethodsOf(t.Elem()) case *types.Chan: p.needMethodsOf(t.Elem()) case *types.Map: p.needMethodsOf(t.Key()) p.needMethodsOf(t.Elem()) case *types.Signature: if t.Recv() != nil { p.needMethodsOf(t.Recv().Type()) } p.needMethodsOf(t.Params()) p.needMethodsOf(t.Results()) case *types.Named: // A pointer-to-named type can be derived from a named // type via reflection. It may have methods too. p.needMethodsOf(types.NewPointer(T)) // 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. p.needMethods(t.Underlying(), true) case *types.Array: p.needMethodsOf(t.Elem()) case *types.Struct: // TODO(adonovan): must we recur over the types of promoted methods? for i, n := 0, t.NumFields(); i < n; i++ { p.needMethodsOf(t.Field(i).Type()) } case *types.Tuple: for i, n := 0, t.Len(); i < n; i++ { p.needMethodsOf(t.At(i).Type()) } default: panic(T) } } ./ssa/util.go0000644000014500017510000000534412246613010012573 0ustar michaelstaff// 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 ssa // This file defines a number of miscellaneous utility functions. import ( "fmt" "go/ast" "io" "os" "code.google.com/p/go.tools/go/types" ) func unreachable() { panic("unreachable") } //// AST utilities // unparen returns e with any enclosing parentheses stripped. func unparen(e ast.Expr) ast.Expr { for { p, ok := e.(*ast.ParenExpr) if !ok { break } e = p.X } return 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 } // 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 } // 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 exp/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") } } // callsRecover reports whether f contains a direct call to recover(). func callsRecover(f *Function) bool { for _, b := range f.Blocks { for _, instr := range b.Instrs { if call, ok := instr.(*Call); ok { if blt, ok := call.Call.Value.(*Builtin); ok { if blt.Name() == "recover" { return true } } } } } return false } ./ssa/testdata/0000755000014500017510000000000012246613010013072 5ustar michaelstaff./ssa/testdata/valueforexpr.go0000644000014500017510000000670412246613010016152 0ustar michaelstaff//+build ignore package main // This file is the input to TestValueForExpr in source_test.go, which // ensures that each expression e immediately following a /*@kind*/(x) // annotation, when passed to Function.ValueForExpr(e), returns a // non-nil Value of the same type as e and of kind 'kind'. func f(spilled, unspilled int) { _ = /*@UnOp*/ (spilled) _ = /*@Parameter*/ (unspilled) _ = /*@*/ (1 + 2) // (constant) i := 0 /*@Call*/ (print( /*@BinOp*/ (i + 1))) ch := /*@MakeChan*/ (make(chan int)) /*@UnOp*/ (<-ch) x := /*@UnOp*/ (<-ch) _ = x select { case /*@Extract*/ (<-ch): case x := /*@Extract*/ (<-ch): _ = x } defer /*@Function*/ (func() { })() go /*@Function*/ (func() { })() y := 0 if true && /*@BinOp*/ (bool(y > 0)) { y = 1 } _ = /*@Phi*/ (y) map1 := /*@MakeMap*/ (make(map[string]string)) _ = map1 _ = /*@MakeSlice*/ (make([]int, 0)) _ = /*@MakeClosure*/ (func() { print(spilled) }) sl := []int{} _ = /*@Slice*/ (sl[:0]) _ = /*@*/ (new(int)) // optimized away tmp := /*@Alloc*/ (new(int)) _ = tmp var iface interface{} _ = /*@TypeAssert*/ (iface.(int)) _ = /*@UnOp*/ (sl[0]) _ = /*@IndexAddr*/ (&sl[0]) _ = /*@Index*/ ([2]int{}[0]) var p *int _ = /*@UnOp*/ (*p) _ = /*@UnOp*/ (global) /*@UnOp*/ (global)[""] = "" /*@Global*/ (global) = map[string]string{} var local t /*UnOp*/ (local.x) = 1 // Exercise corner-cases of lvalues vs rvalues. type N *N var n N /*@UnOp*/ (n) = /*@UnOp*/ (n) /*@ChangeType*/ (n) = /*@Alloc*/ (&n) /*@UnOp*/ (n) = /*@UnOp*/ (*n) /*@UnOp*/ (n) = /*@UnOp*/ (**n) } func complit() { // Composite literals. // We get different results for // - composite literal as value (e.g. operand to print) // - composite literal initializer for addressable value // - composite literal value assigned to blank var // 1. Slices print( /*@Slice*/ ([]int{})) print( /*@Alloc*/ (&[]int{})) print(& /*@Alloc*/ ([]int{})) sl1 := /*@Slice*/ ([]int{}) sl2 := /*@Alloc*/ (&[]int{}) sl3 := & /*@Alloc*/ ([]int{}) _, _, _ = sl1, sl2, sl3 _ = /*@Slice*/ ([]int{}) _ = /*@*/ (& /*@Slice*/ ([]int{})) // & optimized away _ = & /*@Slice*/ ([]int{}) // 2. Arrays print( /*@UnOp*/ ([1]int{})) print( /*@Alloc*/ (&[1]int{})) print(& /*@Alloc*/ ([1]int{})) arr1 := /*@Alloc*/ ([1]int{}) arr2 := /*@Alloc*/ (&[1]int{}) arr3 := & /*@Alloc*/ ([1]int{}) _, _, _ = arr1, arr2, arr3 _ = /*@UnOp*/ ([1]int{}) _ = /*@Alloc*/ (& /*@Alloc*/ ([1]int{})) // & optimized away _ = & /*@Alloc*/ ([1]int{}) // 3. Maps type M map[int]int print( /*@MakeMap*/ (M{})) print( /*@Alloc*/ (&M{})) print(& /*@Alloc*/ (M{})) m1 := /*@MakeMap*/ (M{}) m2 := /*@Alloc*/ (&M{}) m3 := & /*@Alloc*/ (M{}) _, _, _ = m1, m2, m3 _ = /*@MakeMap*/ (M{}) _ = /*@*/ (& /*@MakeMap*/ (M{})) // & optimized away _ = & /*@MakeMap*/ (M{}) // 4. Structs print( /*@UnOp*/ (struct{}{})) print( /*@Alloc*/ (&struct{}{})) print(& /*@Alloc*/ (struct{}{})) s1 := /*@Alloc*/ (struct{}{}) s2 := /*@Alloc*/ (&struct{}{}) s3 := & /*@Alloc*/ (struct{}{}) _, _, _ = s1, s2, s3 _ = /*@UnOp*/ (struct{}{}) _ = /*@Alloc*/ (& /*@Alloc*/ (struct{}{})) _ = & /*@Alloc*/ (struct{}{}) } type t struct{ x int } // Ensure we can locate methods of named types. func (t) f(param int) { _ = /*@Parameter*/ (param) } // Ensure we can locate init functions. func init() { m := /*@MakeMap*/ (make(map[string]string)) _ = m } // Ensure we can locate variables in initializer expressions. var global = /*@MakeMap*/ (make(map[string]string)) ./ssa/testdata/objlookup.go0000644000014500017510000001001512246613010015422 0ustar michaelstaff//+build ignore package main // This file is the input to TestObjValueLookup in source_test.go, // which ensures that each occurrence of an ident defining or // referring to a func, var or const object can be mapped to its // corresponding SSA Value. // // For every reference to a var object, we use annotations in comments // to denote both the expected SSA Value kind, and whether to expect // its value (x) or its address (&x). // // For const and func objects, the results don't vary by reference and // are always values not addresses, so no annotations are needed. The // declaration is enough. import "fmt" import "os" type J int func (*J) method() {} const globalConst = 0 var globalVar int // &globalVar::Global func globalFunc() {} type I interface { interfaceMethod() } type S struct { x int // x::nil } func main() { print(globalVar) // globalVar::UnOp globalVar = 1 // globalVar::Const var v0 int = 1 // v0::Const (simple local value spec) if v0 > 0 { // v0::Const v0 = 2 // v0::Const } print(v0) // v0::Phi // v1 is captured and thus implicitly address-taken. var v1 int = 1 // v1::Const v1 = 2 // v1::Const fmt.Println(v1) // v1::UnOp (load) f := func(param int) { // f::MakeClosure param::Parameter if y := 1; y > 0 { // y::Const print(v1, param) // v1::UnOp (load) param::Parameter } param = 2 // param::Const println(param) // param::Const } f(0) // f::MakeClosure var v2 int // v2::Const (implicitly zero-initialized local value spec) print(v2) // v2::Const m := make(map[string]int) // m::MakeMap // Local value spec with multi-valued RHS: var v3, v4 = m[""] // v3::Extract v4::Extract m::MakeMap print(v3) // v3::Extract print(v4) // v4::Extract v3++ // v3::BinOp (assign with op) v3 += 2 // v3::BinOp (assign with op) v5, v6 := false, "" // v5::Const v6::Const (defining assignment) print(v5) // v5::Const print(v6) // v6::Const var v7 S // &v7::Alloc v7.x = 1 // &v7::Alloc x::Const print(v7.x) // v7::UnOp x::Field var v8 [1]int // &v8::Alloc v8[0] = 0 // &v8::Alloc print(v8[:]) // &v8::Alloc _ = v8[0] // v8::UnOp (load from Alloc) _ = v8[:][0] // &v8::Alloc v8ptr := &v8 // v8ptr::Alloc &v8::Alloc _ = v8ptr[0] // v8ptr::Alloc _ = *v8ptr // v8ptr::Alloc v8a := make([]int, 1) // v8a::MakeSlice v8a[0] = 0 // v8a::MakeSlice print(v8a[:]) // v8a::MakeSlice v9 := S{} // &v9::Alloc v10 := &v9 // v10::Alloc &v9::Alloc _ = v10 // v10::Alloc var v11 *J = nil // v11::Const v11.method() // v11::Const var v12 J // &v12::Alloc v12.method() // &v12::Alloc (implicitly address-taken) // NB, in the following, 'method' resolves to the *types.Func // of (*J).method, so it doesn't help us locate the specific // ssa.Values here: a bound-method closure and a promotion // wrapper. _ = v11.method // v11::Const _ = (*struct{ J }).method // These vars are optimised away. if false { v13 := 0 // v13::nil println(v13) // v13::nil } switch x := 1; x { // x::Const case v0: // v0::Phi } for k, v := range m { // k::Extract v::Extract m::MakeMap _ = k // k::Extract v++ // v::BinOp } if y := 0; y > 1 { // y::Const y::Const } var i interface{} // i::Const (nil interface) i = 1 // i::MakeInterface switch i := i.(type) { // i::MakeInterface i::MakeInterface case int: println(i) // i::Extract } ch := make(chan int) // ch::MakeChan select { case x := <-ch: // x::UnOp (receive) ch::MakeChan _ = x // x::UnOp } // .Op is an inter-package FieldVal-selection. var err os.PathError // &err::Alloc _ = err.Op // err::UnOp Op::Field _ = &err.Op // &err::Alloc &Op::FieldAddr // Exercise corner-cases of lvalues vs rvalues. // (Guessing IsAddr from the 'pointerness' won't cut it here.) type N *N var n N // n::Const n1 := n // n1::Const n::Const n2 := &n1 // n2::Alloc &n1::Alloc n3 := *n2 // n3::UnOp n2::Alloc n4 := **n3 // n4::UnOp n3::UnOp _ = n4 // n4::UnOp } ./ssa/emit.go0000644000014500017510000002670712246613010012562 0ustar michaelstaff// 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 ssa // Helpers for emitting SSA instructions. import ( "go/ast" "go/token" "code.google.com/p/go.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.IsIdentical(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.IsIdentical(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 case *types.Signature: // Conversion from (T) func f() method to f(T) function? _, ok := ut_src.(*types.Signature) 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.IsIdentical(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.needMethodsOf(val.Type()) mi := &MakeInterface{X: val} mi.setType(typ) return f.emit(mi) } // Conversion of a constant to a non-interface type results in // a new constant of the destination type and (initially) the // same abstract value. We don't compute the representation // change yet; this defers the point at which the number of // possible representations explodes. if c, ok := val.(*Const); ok { return NewConst(c.Value, typ) } // A representation-changing conversion. c := &Convert{X: val} c.setType(typ) return f.emit(c) } // emitStore emits to f an instruction to store value val at location // addr, applying implicit conversions as required by assignabilty rules. // func emitStore(f *Function, addr, val Value) *Store { s := &Store{ Addr: addr, Val: emitConv(f, val, deref(addr.Type())), } 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( types.NewVar(token.NoPos, nil, "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. // func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, pos token.Pos) Value { fld := deref(v.Type()).Underlying().(*types.Struct).Field(index) if isPointer(v.Type()) { instr := &FieldAddr{ X: v, Field: index, } instr.setPos(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(pos) instr.setType(fld.Type()) v = f.emit(instr) } return v } // 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() var v Value // Return zero value of each result type. switch T.Underlying().(type) { case *types.Struct, *types.Array: v = emitLoad(f, f.addLocal(T, token.NoPos)) default: v = zeroConst(T) } results = append(results, v) } } f.emit(&Return{Results: results}) f.currentBlock = saved } ./ssa/builder_test.go0000644000014500017510000001512612246613010014302 0ustar michaelstaff// 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 ssa_test import ( "go/parser" "reflect" "sort" "strings" "testing" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" ) 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 TestExternalPackages(t *testing.T) { test := ` 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 } ` imp := importer.New(new(importer.Config)) // no go/build.Context; uses GC importer f, err := parser.ParseFile(imp.Fset, "", test, 0) if err != nil { t.Error(err) return } mainInfo := imp.CreatePackage("main", f) prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) if err := prog.CreatePackages(imp); err != nil { t.Error(err) return } mainPkg := prog.Package(mainInfo.Pkg) mainPkg.Build() // Only the main package and its immediate dependencies are loaded. deps := []string{"bytes", "io", "testing"} all := prog.AllPackages() if len(all) != 1+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 := types.NewPointer(mem.Type()).MethodSet() for i, n := 0, mset.Len(); i < n; i++ { m := prog.Method(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) } } // TestMethodSets tests that Package.TypesWithMethodSets includes all necessary types. func TestTypesWithMethodSets(t *testing.T) { tests := []struct { input string want []string }{ // An exported package-level type is needed. {`package p; type T struct{}; func (T) f() {}`, []string{"*p.T", "p.T"}, }, // An unexported package-level type is not needed. {`package p; type t struct{}; func (t) f() {}`, nil, }, // Subcomponents of type of exported package-level var are needed. {`package p; import "bytes"; var V struct {*bytes.Buffer}`, []string{"struct{*bytes.Buffer}"}, }, // Subcomponents of type of unexported package-level var are not needed. {`package p; import "bytes"; var v struct {*bytes.Buffer}`, nil, }, // Subcomponents of type of exported package-level function are needed. {`package p; import "bytes"; func F(struct {*bytes.Buffer}) {}`, []string{"struct{*bytes.Buffer}"}, }, // Subcomponents of type of unexported package-level function are not needed. {`package p; import "bytes"; func f(struct {*bytes.Buffer}) {}`, nil, }, // Subcomponents of type of exported method are needed. {`package p; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}`, []string{"*p.x", "p.x", "struct{*bytes.Buffer}"}, }, // Subcomponents of type of unexported method are not needed. {`package p; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`, []string{"*p.X", "p.X", "struct{*bytes.Buffer}"}, }, // Local types aren't needed. {`package p; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, nil, }, // ...unless used by MakeInterface. {`package p; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, []string{"*p.T", "p.T"}, }, // Types used as operand of MakeInterface are needed. {`package p; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, []string{"struct{*bytes.Buffer}"}, }, // MakeInterface is optimized away when storing to a blank. {`package p; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, nil, }, } for i, test := range tests { imp := importer.New(new(importer.Config)) // no go/build.Context; uses GC importer f, err := parser.ParseFile(imp.Fset, "", test.input, 0) if err != nil { t.Errorf("test %d: %s", i, err) continue } mainInfo := imp.CreatePackage("p", f) prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) if err := prog.CreatePackages(imp); err != nil { t.Errorf("test %d: %s", i, err) continue } mainPkg := prog.Package(mainInfo.Pkg) prog.BuildAll() var typstrs []string for _, T := range mainPkg.TypesWithMethodSets() { typstrs = append(typstrs, T.String()) } sort.Strings(typstrs) if !reflect.DeepEqual(typstrs, test.want) { t.Errorf("test %d: got %q, want %q", i, typstrs, test.want) } } } ./ssa/print.go0000644000014500017510000002471112246613010012751 0ustar michaelstaff// 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 ssa // This file implements the String() methods for all Value and // Instruction types. import ( "bytes" "fmt" "go/ast" "io" "reflect" "sort" "code.google.com/p/go.tools/go/types" ) // 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().pkgobj() } switch v := v.(type) { case Member: // *Function or *Global return v.RelString(from) case *Const: return v.valstring() + ":" + relType(v.Type(), from) } return v.Name() } func relType(t types.Type, from *types.Package) string { return types.TypeString(from, t) } 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()) } 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 { return fmt.Sprintf("parameter %s : %s", v.Name(), v.Type()) } func (v *Capture) String() string { return fmt.Sprintf("capture %s : %s", v.Name(), v.Type()) } 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" } return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), v.Parent().pkgobj()), 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. blockname := "?" if v.block != nil && i < len(v.block.Preds) { blockname = v.block.Preds[i].String() } b.WriteString(blockname) b.WriteString(": ") edgeVal := "" // 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.HasEllipsis { 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 *ChangeType) String() string { return fmt.Sprintf("changetype %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), v.X.Type(), relName(v.X, 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 (v *Convert) String() string { return fmt.Sprintf("convert %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), v.X.Type(), relName(v.X, v)) } func (v *ChangeInterface) String() string { return fmt.Sprintf("change interface %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v)) } func (v *MakeInterface) String() string { return fmt.Sprintf("make %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), relType(v.X.Type(), v.Parent().pkgobj()), relName(v.X, v)) } 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 { var b bytes.Buffer b.WriteString("make ") b.WriteString(v.Type().String()) b.WriteString(" ") b.WriteString(relName(v.Len, v)) b.WriteString(" ") b.WriteString(relName(v.Cap, v)) return b.String() } 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)) } b.WriteString("]") return b.String() } func (v *MakeMap) String() string { res := "" if v.Reserve != nil { res = relName(v.Reserve, v) } return fmt.Sprintf("make %s %s", v.Type(), res) } func (v *MakeChan) String() string { return fmt.Sprintf("make %s %s", v.Type(), 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 { return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, v.Parent().pkgobj())) } 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. blockname := "?" if s.block != nil && len(s.block.Succs) == 1 { blockname = s.block.Succs[0].String() } return fmt.Sprintf("jump %s", blockname) } func (s *If) String() string { // Be robust against malformed CFG. tblockname, fblockname := "?", "?" if s.block != nil && len(s.block.Succs) == 2 { tblockname = s.block.Succs[0].String() fblockname = s.block.Succs[1].String() } return fmt.Sprintf("if %s goto %s else %s", relName(s.Cond, s), tblockname, fblockname) } 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 == ast.RECV { 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.Object.Path() } func (p *Package) DumpTo(w io.Writer) { fmt.Fprintf(w, "%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) } sort.Strings(names) for _, name := range names { switch mem := p.Members[name].(type) { case *NamedConst: fmt.Fprintf(w, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.Name()) case *Function: fmt.Fprintf(w, " func %-*s %s\n", maxname, name, mem.Type()) case *Type: fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.Type().Underlying()) for _, meth := range IntuitiveMethodSet(mem.Type()) { fmt.Fprintf(w, " %s\n", meth) } case *Global: fmt.Fprintf(w, " var %-*s %s\n", maxname, name, mem.Type().(*types.Pointer).Elem()) } } fmt.Fprintf(w, "\n") } // 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 similarly // 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). // // TODO(gri): move this to go/types? // func IntuitiveMethodSet(T types.Type) []*types.Selection { var result []*types.Selection mset := T.MethodSet() if _, ok := T.Underlying().(*types.Interface); ok { for i, n := 0, mset.Len(); i < n; i++ { result = append(result, mset.At(i)) } } else { pmset := types.NewPointer(T).MethodSet() 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 } func commaOk(x bool) string { if x { return ",ok" } return "" } ./CONTRIBUTORS0000644000014500017510000000025212246613010012352 0ustar michaelstaff# This source code was written by the Go contributors. # The master list of contributors is in the main Go distribution, # visible at http://tip.golang.org/CONTRIBUTORS. ./codereview.cfg0000644000014500017510000000013612246613010013310 0ustar michaelstaffdefaultcc: golang-dev@googlegroups.com contributors: http://go.googlecode.com/hg/CONTRIBUTORS ./astutil/0000755000014500017510000000000012246613010012160 5ustar michaelstaff./astutil/imports_test.go0000644000014500017510000002051112246613010015242 0ustar michaelstaff// 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 astutil import ( "bytes" "go/ast" "go/format" "go/parser" "go/token" "reflect" "strconv" "testing" ) var fset = token.NewFileSet() func parse(t *testing.T, name, in string) *ast.File { file, err := parser.ParseFile(fset, name, in, parser.ParseComments) if err != nil { t.Fatalf("%s parse: %v", name, err) } return file } func print(t *testing.T, name string, f *ast.File) string { var buf bytes.Buffer if err := format.Node(&buf, fset, f); err != nil { t.Fatalf("%s gofmt: %v", name, err) } return string(buf.Bytes()) } type test struct { name string renamedPkg string pkg string in string out string broken bool // known broken } var addTests = []test{ { name: "leave os alone", pkg: "os", in: `package main import ( "os" ) `, out: `package main import ( "os" ) `, }, { name: "import.1", pkg: "os", in: `package main `, out: `package main import "os" `, }, { name: "import.2", pkg: "os", in: `package main // Comment import "C" `, out: `package main // Comment import "C" import "os" `, }, { name: "import.3", pkg: "os", in: `package main // Comment import "C" import ( "io" "utf8" ) `, out: `package main // Comment import "C" import ( "io" "os" "utf8" ) `, }, { name: "import.17", pkg: "x/y/z", in: `package main // Comment import "C" import ( "a" "b" "x/w" "d/f" ) `, out: `package main // Comment import "C" import ( "a" "b" "x/w" "x/y/z" "d/f" ) `, }, { name: "import into singular block", pkg: "bytes", in: `package main import "os" `, out: `package main import ( "bytes" "os" ) `, }, { name: "", renamedPkg: "fmtpkg", pkg: "fmt", in: `package main import "os" `, out: `package main import ( fmtpkg "fmt" "os" ) `, }, { broken: true, name: "struct comment", pkg: "time", in: `package main // This is a comment before a struct. type T struct { t time.Time } `, out: `package main import "time" // This is a comment before a struct. type T struct { t time.Time } `, }, } func TestAddImport(t *testing.T) { for _, test := range addTests { file := parse(t, test.name, test.in) AddNamedImport(file, test.renamedPkg, test.pkg) if got := print(t, test.name, file); got != test.out { if test.broken { t.Logf("%s is known broken:\ngot: %s\nwant: %s", test.name, got, test.out) } else { t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) } } } } func TestDoubleAddImport(t *testing.T) { file := parse(t, "doubleimport", "package main\n") AddImport(file, "os") AddImport(file, "bytes") want := `package main import ( "bytes" "os" ) ` if got := print(t, "doubleimport", file); got != want { t.Errorf("got: %s\nwant: %s", got, want) } } var deleteTests = []test{ { name: "import.4", pkg: "os", in: `package main import ( "os" ) `, out: `package main `, }, { name: "import.5", pkg: "os", in: `package main // Comment import "C" import "os" `, out: `package main // Comment import "C" `, }, { name: "import.6", pkg: "os", in: `package main // Comment import "C" import ( "io" "os" "utf8" ) `, out: `package main // Comment import "C" import ( "io" "utf8" ) `, }, { name: "import.7", pkg: "io", in: `package main import ( "io" // a "os" // b "utf8" // c ) `, out: `package main import ( // a "os" // b "utf8" // c ) `, }, { name: "import.8", pkg: "os", in: `package main import ( "io" // a "os" // b "utf8" // c ) `, out: `package main import ( "io" // a // b "utf8" // c ) `, }, { name: "import.9", pkg: "utf8", in: `package main import ( "io" // a "os" // b "utf8" // c ) `, out: `package main import ( "io" // a "os" // b // c ) `, }, { name: "import.10", pkg: "io", in: `package main import ( "io" "os" "utf8" ) `, out: `package main import ( "os" "utf8" ) `, }, { name: "import.11", pkg: "os", in: `package main import ( "io" "os" "utf8" ) `, out: `package main import ( "io" "utf8" ) `, }, { name: "import.12", pkg: "utf8", in: `package main import ( "io" "os" "utf8" ) `, out: `package main import ( "io" "os" ) `, }, { name: "handle.raw.quote.imports", pkg: "os", in: "package main\n\nimport `os`", out: `package main `, }, } func TestDeleteImport(t *testing.T) { for _, test := range deleteTests { file := parse(t, test.name, test.in) DeleteImport(file, test.pkg) if got := print(t, test.name, file); got != test.out { t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) } } } type rewriteTest struct { name string srcPkg string dstPkg string in string out string } var rewriteTests = []rewriteTest{ { name: "import.13", srcPkg: "utf8", dstPkg: "encoding/utf8", in: `package main import ( "io" "os" "utf8" // thanks ken ) `, out: `package main import ( "encoding/utf8" // thanks ken "io" "os" ) `, }, { name: "import.14", srcPkg: "asn1", dstPkg: "encoding/asn1", in: `package main import ( "asn1" "crypto" "crypto/rsa" _ "crypto/sha1" "crypto/x509" "crypto/x509/pkix" "time" ) var x = 1 `, out: `package main import ( "crypto" "crypto/rsa" _ "crypto/sha1" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "time" ) var x = 1 `, }, { name: "import.15", srcPkg: "url", dstPkg: "net/url", in: `package main import ( "bufio" "net" "path" "url" ) var x = 1 // comment on x, not on url `, out: `package main import ( "bufio" "net" "net/url" "path" ) var x = 1 // comment on x, not on url `, }, { name: "import.16", srcPkg: "http", dstPkg: "net/http", in: `package main import ( "flag" "http" "log" "text/template" ) var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 `, out: `package main import ( "flag" "log" "net/http" "text/template" ) var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 `, }, } func TestRewriteImport(t *testing.T) { for _, test := range rewriteTests { file := parse(t, test.name, test.in) RewriteImport(file, test.srcPkg, test.dstPkg) if got := print(t, test.name, file); got != test.out { t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) } } } var renameTests = []rewriteTest{ { name: "rename pkg use", srcPkg: "bytes", dstPkg: "bytes_", in: `package main func f() []byte { buf := new(bytes.Buffer) return buf.Bytes() } `, out: `package main func f() []byte { buf := new(bytes_.Buffer) return buf.Bytes() } `, }, } func TestRenameTop(t *testing.T) { for _, test := range renameTests { file := parse(t, test.name, test.in) RenameTop(file, test.srcPkg, test.dstPkg) if got := print(t, test.name, file); got != test.out { t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) } } } var importsTests = []struct { name string in string want [][]string }{ { name: "no packages", in: `package foo `, want: nil, }, { name: "one group", in: `package foo import ( "fmt" "testing" ) `, want: [][]string{{"fmt", "testing"}}, }, { name: "four groups", in: `package foo import "C" import ( "fmt" "testing" "appengine" "myproject/mylib1" "myproject/mylib2" ) `, want: [][]string{ {"C"}, {"fmt", "testing"}, {"appengine"}, {"myproject/mylib1", "myproject/mylib2"}, }, }, { name: "multiple factored groups", in: `package foo import ( "fmt" "testing" "appengine" ) import ( "reflect" "bytes" ) `, want: [][]string{ {"fmt", "testing"}, {"appengine"}, {"reflect"}, {"bytes"}, }, }, } func unquote(s string) string { res, err := strconv.Unquote(s) if err != nil { return "could_not_unquote" } return res } func TestImports(t *testing.T) { fset := token.NewFileSet() for _, test := range importsTests { f, err := parser.ParseFile(fset, "test.go", test.in, 0) if err != nil { t.Errorf("%s: %v", test.name, err) continue } var got [][]string for _, block := range Imports(fset, f) { var b []string for _, spec := range block { b = append(b, unquote(spec.Path.Value)) } got = append(got, b) } if !reflect.DeepEqual(got, test.want) { t.Errorf("Imports(%s)=%v, want %v", test.name, got, test.want) } } } ./astutil/imports.go0000644000014500017510000002146612246613010014215 0ustar michaelstaff// 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 astutil contains common utilities for working with the Go AST. package astutil import ( "go/ast" "go/token" "path" "strconv" "strings" ) // AddImport adds the import path to the file f, if absent. func AddImport(f *ast.File, ipath string) (added bool) { return AddNamedImport(f, "", ipath) } // AddNamedImport adds the import path to the file f, if absent. // If name is not empty, it is used to rename the import. // // For example, calling // AddNamedImport(f, "pathpkg", "path") // adds // import pathpkg "path" func AddNamedImport(f *ast.File, name, ipath string) (added bool) { if imports(f, ipath) { return false } newImport := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote(ipath), }, } if name != "" { newImport.Name = &ast.Ident{Name: name} } // Find an import decl to add to. var ( bestMatch = -1 lastImport = -1 impDecl *ast.GenDecl impIndex = -1 ) for i, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if ok && gen.Tok == token.IMPORT { lastImport = i // Do not add to import "C", to avoid disrupting the // association with its doc comment, breaking cgo. if declImports(gen, "C") { continue } // Compute longest shared prefix with imports in this block. for j, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) n := matchLen(importPath(impspec), ipath) if n > bestMatch { bestMatch = n impDecl = gen impIndex = j } } } } // If no import decl found, add one after the last import. if impDecl == nil { impDecl = &ast.GenDecl{ Tok: token.IMPORT, } f.Decls = append(f.Decls, nil) copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) f.Decls[lastImport+1] = impDecl } // Ensure the import decl has parentheses, if needed. if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() { impDecl.Lparen = impDecl.Pos() } insertAt := impIndex + 1 if insertAt == 0 { insertAt = len(impDecl.Specs) } impDecl.Specs = append(impDecl.Specs, nil) copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) impDecl.Specs[insertAt] = newImport if insertAt > 0 { // Assign same position as the previous import, // so that the sorter sees it as being in the same block. prev := impDecl.Specs[insertAt-1] newImport.Path.ValuePos = prev.Pos() newImport.EndPos = prev.Pos() } if len(impDecl.Specs) > 1 && impDecl.Lparen == 0 { // set Lparen to something not zero, so the printer prints // the full block rather just the first ImportSpec. impDecl.Lparen = 1 } f.Imports = append(f.Imports, newImport) return true } // DeleteImport deletes the import path from the file f, if present. func DeleteImport(f *ast.File, path string) (deleted bool) { oldImport := importSpec(f, path) // Find the import node that imports path, if any. for i, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if !ok || gen.Tok != token.IMPORT { continue } for j, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) if oldImport != impspec { continue } // We found an import spec that imports path. // Delete it. deleted = true copy(gen.Specs[j:], gen.Specs[j+1:]) gen.Specs = gen.Specs[:len(gen.Specs)-1] // If this was the last import spec in this decl, // delete the decl, too. if len(gen.Specs) == 0 { copy(f.Decls[i:], f.Decls[i+1:]) f.Decls = f.Decls[:len(f.Decls)-1] } else if len(gen.Specs) == 1 { gen.Lparen = token.NoPos // drop parens } if j > 0 { // We deleted an entry but now there will be // a blank line-sized hole where the import was. // Close the hole by making the previous // import appear to "end" where this one did. gen.Specs[j-1].(*ast.ImportSpec).EndPos = impspec.End() } break } } // Delete it from f.Imports. for i, imp := range f.Imports { if imp == oldImport { copy(f.Imports[i:], f.Imports[i+1:]) f.Imports = f.Imports[:len(f.Imports)-1] break } } return } // RewriteImport rewrites any import of path oldPath to path newPath. func RewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) { for _, imp := range f.Imports { if importPath(imp) == oldPath { rewrote = true // record old End, because the default is to compute // it using the length of imp.Path.Value. imp.EndPos = imp.End() imp.Path.Value = strconv.Quote(newPath) } } return } // UsesImport reports whether a given import is used. func UsesImport(f *ast.File, path string) (used bool) { spec := importSpec(f, path) if spec == nil { return } name := spec.Name.String() switch name { case "": // If the package name is not explicitly specified, // make an educated guess. This is not guaranteed to be correct. lastSlash := strings.LastIndex(path, "/") if lastSlash == -1 { name = path } else { name = path[lastSlash+1:] } case "_", ".": // Not sure if this import is used - err on the side of caution. return true } ast.Walk(visitFn(func(n ast.Node) { sel, ok := n.(*ast.SelectorExpr) if ok && isTopName(sel.X, name) { used = true } }), f) return } type visitFn func(node ast.Node) func (fn visitFn) Visit(node ast.Node) ast.Visitor { fn(node) return fn } // imports returns true if f imports path. func imports(f *ast.File, path string) bool { return importSpec(f, path) != nil } // importSpec returns the import spec if f imports path, // or nil otherwise. func importSpec(f *ast.File, path string) *ast.ImportSpec { for _, s := range f.Imports { if importPath(s) == path { return s } } return nil } // importPath returns the unquoted import path of s, // or "" if the path is not properly quoted. func importPath(s *ast.ImportSpec) string { t, err := strconv.Unquote(s.Path.Value) if err == nil { return t } return "" } // declImports reports whether gen contains an import of path. func declImports(gen *ast.GenDecl, path string) bool { if gen.Tok != token.IMPORT { return false } for _, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) if importPath(impspec) == path { return true } } return false } // RenameTop renames all references to the top-level name old. // It returns true if it makes any changes. func RenameTop(f *ast.File, old, new string) bool { var fixed bool // Rename any conflicting imports // (assuming package name is last element of path). for _, s := range f.Imports { if s.Name != nil { if s.Name.Name == old { s.Name.Name = new fixed = true } } else { _, thisName := path.Split(importPath(s)) if thisName == old { s.Name = ast.NewIdent(new) fixed = true } } } // Rename any top-level declarations. for _, d := range f.Decls { switch d := d.(type) { case *ast.FuncDecl: if d.Recv == nil && d.Name.Name == old { d.Name.Name = new d.Name.Obj.Name = new fixed = true } case *ast.GenDecl: for _, s := range d.Specs { switch s := s.(type) { case *ast.TypeSpec: if s.Name.Name == old { s.Name.Name = new s.Name.Obj.Name = new fixed = true } case *ast.ValueSpec: for _, n := range s.Names { if n.Name == old { n.Name = new n.Obj.Name = new fixed = true } } } } } } // Rename top-level old to new, both unresolved names // (probably defined in another file) and names that resolve // to a declaration we renamed. ast.Walk(visitFn(func(n ast.Node) { id, ok := n.(*ast.Ident) if ok && isTopName(id, old) { id.Name = new fixed = true } if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new { id.Name = id.Obj.Name fixed = true } }), f) return fixed } // matchLen returns the length of the longest prefix shared by x and y. func matchLen(x, y string) int { i := 0 for i < len(x) && i < len(y) && x[i] == y[i] { i++ } return i } // isTopName returns true if n is a top-level unresolved identifier with the given name. func isTopName(n ast.Expr, name string) bool { id, ok := n.(*ast.Ident) return ok && id.Name == name && id.Obj == nil } // Imports returns the file imports grouped by paragraph. func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec { var groups [][]*ast.ImportSpec for _, decl := range f.Decls { genDecl, ok := decl.(*ast.GenDecl) if !ok || genDecl.Tok != token.IMPORT { break } group := []*ast.ImportSpec{} var lastLine int for _, spec := range genDecl.Specs { importSpec := spec.(*ast.ImportSpec) pos := importSpec.Path.ValuePos line := fset.Position(pos).Line if lastLine > 0 && pos > 0 && line-lastLine > 1 { groups = append(groups, group) group = []*ast.ImportSpec{} } group = append(group, importSpec) lastLine = line } groups = append(groups, group) } return groups } ./PATENTS0000644000014500017510000000242712246613010011541 0ustar michaelstaffAdditional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. ./pointer/0000755000014500017510000000000012246613010012153 5ustar michaelstaff./pointer/example_test.go0000644000014500017510000000434712246613010015204 0ustar michaelstaff// 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 pointer_test import ( "fmt" "go/build" "go/parser" "sort" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) // This program demonstrates how to use the pointer analysis to // obtain a conservative call-graph of a Go program. // func Example() { const myprog = ` package main import "fmt" type I interface { f() } type C struct{} func (C) f() { fmt.Println("C.f()") } func main() { var i I = C{} i.f() // dynamic method call } ` // Construct an importer. // Imports will be loaded as if by 'go build'. imp := importer.New(&importer.Config{Build: &build.Default}) // Parse the input file. file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, 0) if err != nil { fmt.Print(err) // parse error return } // Create single-file main package and import its dependencies. mainInfo := imp.CreatePackage("main", file) // Create SSA-form program representation. var mode ssa.BuilderMode prog := ssa.NewProgram(imp.Fset, mode) if err := prog.CreatePackages(imp); err != nil { fmt.Print(err) // type error in some package return } mainPkg := prog.Package(mainInfo.Pkg) // Build SSA code for bodies of all functions in the whole program. prog.BuildAll() // Run the pointer analysis and build the complete callgraph. config := &pointer.Config{ Mains: []*ssa.Package{mainPkg}, BuildCallGraph: true, } result := pointer.Analyze(config) // Find edges originating from the main package. // By converting to strings, we de-duplicate nodes // representing the same function due to context sensitivity. var edges []string call.GraphVisitEdges(result.CallGraph, func(edge call.Edge) error { caller := edge.Caller.Func() if caller.Pkg == mainPkg { edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func())) } return nil }) // Print the edges in sorted order. sort.Strings(edges) for _, edge := range edges { fmt.Println(edge) } // Output: // (main.C).f --> fmt.Println // main.init --> fmt.init // main.main --> (main.C).f } ./pointer/solve.go0000644000014500017510000002201212246613010013627 0ustar michaelstaff// 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 pointer // This file defines a naive Andersen-style solver for the inclusion // constraint system. import ( "fmt" "code.google.com/p/go.tools/go/types" ) func (a *analysis) solve() { // Solver main loop. for round := 1; ; round++ { if a.log != nil { fmt.Fprintf(a.log, "Solving, round %d\n", round) } // Add new constraints to the graph: // static constraints from SSA on round 1, // dynamic constraints from reflection thereafter. a.processNewConstraints() id := a.work.take() if id == empty { break } if a.log != nil { fmt.Fprintf(a.log, "\tnode n%d\n", id) } n := a.nodes[id] // Difference propagation. delta := n.pts.diff(n.prevPts) if delta == nil { continue } n.prevPts = n.pts.clone() // 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.pts) } } if len(a.nodes[0].pts) > 0 { panic(fmt.Sprintf("pts(0) is nonempty: %s", a.nodes[0].pts)) } if a.log != nil { fmt.Fprintf(a.log, "Solver done\n") } } // 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.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.copyTo != nil || dst.complex != nil { 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].copyTo.add(c.dst) default: // complex constraint id = c.ptr() a.nodes[id].complex.add(c) } if n := a.nodes[id]; len(n.pts) > 0 { if len(n.prevPts) > 0 { stale.add(id) } a.addWork(id) } } // Apply new constraints to pre-existing PTS labels. for id := range stale { n := a.nodes[id] a.solveConstraints(n, n.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 == nil { return } // Process complex constraints dependent on n. for c := range n.complex { if a.log != nil { fmt.Fprintf(a.log, "\t\tconstraint %s\n", c) } // TODO(adonovan): parameter n is never needed, since // it's equal to c.ptr(). Remove. c.solve(a, n, delta) } // Process copy constraints. var copySeen nodeset for mid := range n.copyTo { if copySeen.add(mid) { if a.nodes[mid].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 { return a.nodes[ptr].pts.add(label) } func (a *analysis) addWork(id nodeid) { a.work.add(id) if a.log != nil { fmt.Fprintf(a.log, "\t\twork: n%d\n", id) } } func (c *addrConstraint) ptr() nodeid { panic("addrConstraint: not a complex constraint") } func (c *copyConstraint) ptr() nodeid { panic("addrConstraint: not a complex constraint") } // Complex constraints attach themselves to the relevant pointer node. func (c *storeConstraint) ptr() nodeid { return c.dst } func (c *loadConstraint) ptr() nodeid { return c.src } func (c *offsetAddrConstraint) ptr() nodeid { return c.src } func (c *typeFilterConstraint) ptr() nodeid { return c.src } func (c *untagConstraint) ptr() nodeid { return c.src } func (c *invokeConstraint) ptr() nodeid { return c.iface } // 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.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].pts.addAll(nsrc.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, n *node, delta nodeset) { var changed bool for k := range delta { 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, n *node, delta nodeset) { for k := range delta { koff := k + nodeid(c.offset) if a.onlineCopy(koff, c.src) { a.addWork(koff) } } } func (c *offsetAddrConstraint) solve(a *analysis, n *node, delta nodeset) { dst := a.nodes[c.dst] for k := range delta { if dst.pts.add(k + nodeid(c.offset)) { a.addWork(c.dst) } } } func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) { for ifaceObj := range delta { 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.IsAssignableTo(tDyn, c.typ) { if a.addLabel(c.dst, ifaceObj) { a.addWork(c.dst) } } } } func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) { predicate := types.IsAssignableTo if c.exact { predicate = types.IsIdentical } for ifaceObj := range delta { 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 tConc 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, n *node, delta nodeset) { for ifaceObj := range delta { 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. meth := tDyn.MethodSet().Lookup(c.method.Pkg(), c.method.Name()) if meth == nil { panic(fmt.Sprintf("n%d: type %s has no method %s (iface=n%d)", c.iface, tDyn, c.method, ifaceObj)) } fn := a.prog.Method(meth) if fn == nil { panic(fmt.Sprintf("n%d: no ssa.Function for %s", c.iface, meth)) } 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, n *node, delta nodeset) { panic("addr is not a complex constraint") } func (c *copyConstraint) solve(a *analysis, n *node, delta nodeset) { panic("copy is not a complex constraint") } ./pointer/doc.go0000644000014500017510000004771012246613010013260 0ustar michaelstaff// 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 pointer implements Andersen's analysis, an inclusion-based pointer analysis algorithm first described in (Andersen, 1994). 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. We intend to add various presolving optimisations such as Pointer and Location Equivalence from (Hardekopf & Lin, SAS '07) and solver optimisatisions such as Hybrid- and Lazy- Cycle Detection from (Hardekopf & Lin, PLDI'07), 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 to 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. TERMINOLOGY 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. 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 pts(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 a contiguous P/R block for the callsite and adding a dynamic rule triggered by each tagged object E 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. 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 currently the logical field offsets (plus one for the identity node), so the sizes of the fields can be ignored by the analysis. Sound treatment of unsafe.Pointer conversions (not yet implemented) would require us to model memory layout using physical field offsets to ascertain which object field(s) might be aliased by a given FieldAddr of a different base pointer type. It would also require us to dispense with the identity node. *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 params block in the function object (this includes the receiver if the callee is a method); - creating copy edges from the results block in the function object to the value nodes for the result of the call. 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: *fn->params = callargs callresult = *fn->results (Recall that the function object's params and results 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 block to the callsite's P/R block. 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. SOLVER The solver is currently a very naive Andersen-style implementation, although it does use difference propagation (Pearce et al, SQC'04). There is much to do. 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. */ package pointer ./pointer/pointer_test.go0000644000014500017510000003542312246613010015230 0ustar michaelstaff// 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 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/build" "go/parser" "go/token" "io/ioutil" "os" "regexp" "strconv" "strings" "testing" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types/typemap" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) 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", "testdata/interfaces.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", } // 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" } // A record of a call to the built-in print() function. Used for testing. type probe struct { instr *ssa.CallCommon arg0 pointer.Pointer // first argument to print } // Find probe (call to print(x)) of same source // file/line as expectation. func findProbe(prog *ssa.Program, probes []probe, e *expectation) *probe { for _, p := range probes { pos := prog.Fset.Position(p.instr.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 &p } } return nil // e.g. analysis didn't reach this call } func doOneInput(input, filename string) bool { impctx := &importer.Config{Build: &build.Default} imp := importer.New(impctx) // Parsing. f, err := parser.ParseFile(imp.Fset, filename, input, 0) if err != nil { // TODO(adonovan): err is a scanner error list; // display all errors not just first? fmt.Println(err) return false } // Create single-file main package and import its dependencies. info := imp.CreatePackage("main", f) // SSA creation + building. prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) if err := prog.CreatePackages(imp); err != nil { fmt.Println(err) return false } prog.BuildAll() mainpkg := prog.Package(info.Pkg) 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) } 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 != "..." { texpr, err := parser.ParseExpr(typstr) if err != nil { ok = false // Don't print err since its location is bad. e.errorf("'%s' is not a valid type", typstr) continue } mainFileScope := mainpkg.Object.Scope().Child(0) t, _, err = types.EvalNode(imp.Fset, texpr, mainpkg.Object, mainFileScope) 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 } } 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 probes []probe var log bytes.Buffer // Run the analysis. config := &pointer.Config{ Reflection: true, BuildCallGraph: true, Mains: []*ssa.Package{ptrmain}, Log: &log, Print: func(site *ssa.CallCommon, p pointer.Pointer) { probes = append(probes, probe{site, p}) }, } // Print the log is there was an error or a panic. complete := false defer func() { if !complete || !ok { log.WriteTo(os.Stderr) } }() result := pointer.Analyze(config) // Check the expectations. for _, e := range exps { var pr *probe if e.needsProbe() { if pr = findProbe(prog, probes, e); pr == nil { ok = false e.errorf("unreachable print() statement has expectation %s", e) continue } if pr.arg0 == nil { ok = false e.errorf("expectation on non-pointerlike operand: %s", pr.instr.Args[0].Type()) continue } } switch e.kind { case "pointsto": if !checkPointsToExpectation(e, pr, lineMapping, prog) { ok = false } case "types": if !checkTypesExpectation(e, pr) { 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, pr *probe, 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 pr.arg0.PointsTo().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 } // underlying returns the underlying type of typ. Copied from go/types. func underlyingType(typ types.Type) types.Type { if typ, ok := typ.(*types.Named); ok { return typ.Underlying() // underlying types are never NamedTypes } if typ == nil { panic("underlying(nil)") } return typ } func checkTypesExpectation(e *expectation, pr *probe) bool { var expected typemap.M var surplus typemap.M exact := true for _, g := range e.types { if g == types.Typ[types.Invalid] { exact = false continue } expected.Set(g, struct{}{}) } if t := pr.instr.Args[0].Type(); !pointer.CanHaveDynamicTypes(t) { e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", t) return false } // Find the set of types that the probe's // argument (x in print(x)) may contain. for _, T := range pr.arg0.PointsTo().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, callgraph call.Graph) bool { found := make(map[string]int) err := call.GraphVisitEdges(callgraph, func(edge call.Edge) error { // Name-based matching is inefficient but it allows us to // match functions whose names that would not appear in an // index ("") 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 } ./pointer/api.go0000644000014500017510000001415612246613010013262 0ustar michaelstaff// 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 pointer import ( "fmt" "go/token" "io" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/go/types/typemap" "code.google.com/p/go.tools/ssa" ) // 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. 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 // Print is invoked during the analysis for each discovered // call to the built-in print(x), providing a convenient way // to identify arbitrary expressions of interest in the tests. // // Pointer p may be saved until the analysis is complete, at // which point its methods provide access to the analysis // (The result of callings its methods within the Print // callback is undefined.) p is nil if x is non-pointerlike. // Print func(site *ssa.CallCommon, p Pointer) // The client populates Queries[v] for each ssa.Value v of // interest. // // The boolean (Indirect) indicates whether to compute the // points-to set for v (false) or *v (true): the latter is // typically wanted for Values corresponding to source-level // lvalues, e.g. an *ssa.Global. // // The pointer analysis will populate the corresponding // Results.Queries value when it creates the pointer variable // for v or *v. Upon completion the client can inspect that // map for the results. // // If a Value belongs to a function that the analysis treats // context-sensitively, the corresponding Results.Queries slice // may have multiple Pointers, one per distinct context. Use // PointsToCombined to merge them. // Queries map[ssa.Value]Indirect // If Log is non-nil, log messages are written to it. // Logging is extremely verbose. Log io.Writer } type Indirect bool // map[ssa.Value]Indirect is not a set 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 call.Graph // discovered call graph Queries map[ssa.Value][]Pointer // points-to sets for queried ssa.Values Warnings []Warning // warnings of unsoundness } // A Pointer is an equivalence class of pointerlike values. type Pointer interface { // PointsTo returns the points-to set of this pointer. PointsTo() PointsToSet // MayAlias reports whether the receiver pointer may alias // the argument pointer. MayAlias(Pointer) bool // Context returns the context of this pointer, // if it corresponds to a local variable. Context() call.GraphNode String() string } // A PointsToSet is a set of labels (locations or allocations). // type PointsToSet interface { // PointsTo returns the set of labels that this points-to set // contains. Labels() []*Label // Intersects reports whether this points-to set and the // argument points-to set contain common members. Intersects(PointsToSet) bool // 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 a set of pointer abstractions of // that dynamic type, represented as a []Pointer slice. Use // PointsToCombined to merge them. // // The result is empty unless CanHaveDynamicTypes(T). // DynamicTypes() *typemap.M } // Union returns the set containing all the elements of each set in sets. func Union(sets ...PointsToSet) PointsToSet { var union ptset for _, set := range sets { set := set.(ptset) union.a = set.a union.pts.addAll(set.pts) } return union } // PointsToCombined returns the combined points-to set of all the // specified pointers. func PointsToCombined(ptrs []Pointer) PointsToSet { var ptsets []PointsToSet for _, ptr := range ptrs { ptsets = append(ptsets, ptr.PointsTo()) } return Union(ptsets...) } // ---- PointsToSet public interface type ptset struct { a *analysis // may be nil if pts is nil pts nodeset } func (s ptset) Labels() []*Label { var labels []*Label for l := range s.pts { labels = append(labels, s.a.labelFor(l)) } return labels } func (s ptset) DynamicTypes() *typemap.M { var tmap typemap.M tmap.SetHasher(s.a.hasher) for ifaceObjId := range s.pts { if !s.a.isTaggedObject(ifaceObjId) { continue // !CanHaveDynamicTypes(tDyn) } tDyn, v, indirect := s.a.taggedValue(ifaceObjId) if indirect { panic("indirect tagged object") // implement later } prev, _ := tmap.At(tDyn).([]Pointer) tmap.Set(tDyn, append(prev, ptr{s.a, nil, v})) } return &tmap } func (x ptset) Intersects(y_ PointsToSet) bool { y := y_.(ptset) for l := range x.pts { if _, ok := y.pts[l]; ok { return true } } return false } // ---- Pointer public interface // ptr adapts a node to the Pointer interface. type ptr struct { a *analysis cgn *cgnode n nodeid // non-zero } func (p ptr) String() string { return fmt.Sprintf("n%d", p.n) } func (p ptr) Context() call.GraphNode { return p.cgn } func (p ptr) PointsTo() PointsToSet { return ptset{p.a, p.a.nodes[p.n].pts} } func (p ptr) MayAlias(q Pointer) bool { return p.PointsTo().Intersects(q.PointsTo()) } func (p ptr) DynamicTypes() *typemap.M { return p.PointsTo().DynamicTypes() } ./pointer/labels.go0000644000014500017510000001135112246613010013745 0ustar michaelstaff// 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 pointer import ( "fmt" "go/token" "strings" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) // A Label is an entity that may be pointed to by a pointer, map, // channel, 'func', slice or interface. Labels include: // // Labels include: // - functions // - globals // - tagged objects, representing interfaces and reflect.Values // - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s)) // - stack- and heap-allocated variables (including composite literals) // - channels, maps and arrays created by make() // - instrinsic or reflective operations that allocate (e.g. append, reflect.New) // - instrinsic 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 } // Context returns the analytic context in which this label's object was allocated, // or nil for global objects: global, const, and shared contours for functions. // func (l Label) Context() call.GraphNode { return l.obj.cgn } // 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.Func().Pos() } return token.NoPos } // String returns the printed form of this label. // // Examples: Object type: // (sync.Mutex).Lock (a function) // "foo":[]byte (a slice constant) // makemap (map allocated via make) // makechan (channel allocated via make) // makeinterface (tagged object allocated by makeinterface) // (allocation in instrinsic) // sync.Mutex (a reflect.rtype instance) // (an instrinsic 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) // 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("", l.obj.cgn.Func()) } else { s = "" // 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() } ./pointer/intrinsics.go0000644000014500017510000004244012246613010014673 0ustar michaelstaff// 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 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" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) // 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 map[string]intrinsic func init() { // Key strings are from Function.String(). // That little dot ۰ is an Arabic zero numeral (U+06F0), // categories [Nd]. intrinsicsByName = 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, // 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, "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, "os.epipecheck": ext۰NoEffect, "runtime.BlockProfile": ext۰NoEffect, "runtime.Breakpoint": ext۰NoEffect, "runtime.CPUProfile": ext۰NotYetImplemented, "runtime.Caller": ext۰NoEffect, "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.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/atomic.AddInt32": ext۰NoEffect, "sync/atomic.AddUint32": 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.LoadUint32": ext۰NoEffect, "sync/atomic.LoadUint64": ext۰NoEffect, "sync/atomic.StoreInt32": ext۰NoEffect, "sync/atomic.StoreUint32": 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, "time.Sleep": ext۰NoEffect, "time.now": ext۰NoEffect, "time.startTimer": ext۰NoEffect, "time.stopTimer": ext۰NoEffect, } } // 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 fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object { 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 } // 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) { // TODO(adonovan): enable this warning when we've implemented // enough that it's not unbearably annoying. // 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 f nodeid // (ptr) x nodeid } func (c *runtimeSetFinalizerConstraint) String() string { return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f) } func (c *runtimeSetFinalizerConstraint) ptr() nodeid { return c.f } func (c *runtimeSetFinalizerConstraint) solve(a *analysis, _ *node, delta nodeset) { for fObj := range delta { tDyn, f, indirect := a.taggedValue(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, }) } ./pointer/TODO0000644000014500017510000000257712246613010012656 0ustar michaelstaff-*- text -*- Pointer analysis to-do list =========================== CONSTRAINT GENERATION: - support reflection - implement native intrinsics. These vary by platform. - unsafe.Pointer conversions. Three options: 1) unsoundly (but type-safely) treat p=unsafe.Pointer(x) conversions as allocations, losing aliases. This is what's currently implemented. 2) unsoundly (but type-safely) treat p=unsafe.Pointer(x) and T(p) conversions as interface boxing and unboxing operations. This may preserve some aliasing relations at little cost. 3) soundly track physical field offsets. (Summarise dannyb's email here.) A downside is that we can't keep the identity field of struct allocations that identifies the object. OPTIMISATIONS - pre-solver: PE and LE via HVN/HRU. - solver: HCD, LCD. - use sparse bitvectors for ptsets - use sparse bitvectors for graph edges - experiment with different worklist algorithms: priority queue (solver visit-time order) red-black tree (node id order) double-ended queue (insertion order) fast doubly-linked list (See Zhanh et al PLDI'13) (insertion order with fast membership test) dannyb recommends sparse bitmap. API: - Some optimisations (e.g. LE, PE) may change the API. Think about them sooner rather than later. MISC: - Test on all platforms. Currently we assume these go/build tags: linux, amd64, !cgo. ./pointer/gen.go0000644000014500017510000010744712246613010013270 0ustar michaelstaff// 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 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/ast" "go/token" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) var ( tEface = types.NewInterface(nil, nil) 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}) 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) } // Record the (v, id) relation if the client has queried v. if indirect, ok := a.config.Queries[v]; ok { if indirect { tmp := a.addNodes(v.Type(), "query.indirect") a.genLoad(cgn, tmp, v, 0, a.sizeof(v.Type())) id = tmp } a.result.Queries[v] = append(a.result.Queries[v], ptr{a, cgn, id}) } } // 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. 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(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.addOneNode(v.Type(), comment, nil) if obj := a.objectNode(nil, v); obj != 0 { a.addressOf(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 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 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. func (a *analysis) addressOf(id, obj nodeid) { if id == 0 { panic("addressOf: zero id") } if obj == 0 { panic("addressOf: zero obj") } 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. // func (a *analysis) offsetAddr(dst, src nodeid, offset uint32) { 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 == tUnsafePtr { // ignore for now // a.copy(res, a.valueNode(conv.X), 1) return } case *types.Basic: switch utDst := tDst.Underlying().(type) { case *types.Pointer: // unsafe.Pointer -> *T? (currently unsound) if utSrc == tUnsafePtr { // For now, suppress unsafe.Pointer conversion // warnings on "syscall" package. // TODO(adonovan): audit for soundness. if conv.Parent().Pkg.Object.Path() != "syscall" { a.warnf(conv.Pos(), "unsound: %s contains an unsafe.Pointer conversion (to %s)", conv.Parent(), tDst) } // For now, we treat unsafe.Pointer->*T // conversion like new(T) and create an // unaliased object. In future we may handle // unsafe conversions soundly; see TODO file. obj := a.addNodes(mustDeref(tDst), "unsafe.Pointer conversion") a.endObject(obj, cgn, conv) a.addressOf(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(res, obj) return } case *types.Basic: // TODO(adonovan): // unsafe.Pointer -> uintptr? // uintptr -> unsafe.Pointer // // The language doesn't adequately specify the // behaviour of these operations, but almost // all uses of these conversions (even in the // spec) seem to imply a non-moving garbage // collection strategy, or implicit "pinning" // semantics for unsafe.Pointer conversions. // TODO(adonovan): we need more work before we can handle // cryptopointers well. if utSrc == tUnsafePtr || utDst == tUnsafePtr { // Ignore for now. See TODO file for ideas. return } return // ignore all other basic type conversions } } 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(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).Object().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": // Analytically print is a no-op, but it's a convenient hook // for testing the pts of an expression, so we notify the client. // Existing uses in Go core libraries are few and harmless. if Print := a.config.Print; Print != nil { // Due to context-sensitivity, we may encounter // the same print() call in many contexts, so // we merge them to a canonical node. probe := a.probes[call] t := call.Args[0].Type() // First time? Create the canonical probe node. if probe == 0 { probe = a.addNodes(t, "print") a.probes[call] = probe Print(call, ptr{a, nil, probe}) // notify client } a.copy(probe, a.valueNode(call.Args[0]), a.sizeof(t)) } default: // No-ops: close len cap real imag complex 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(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(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, // and load the results from, the P/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 add // edges from the caller's P/R block to the callee's // P/R block 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. meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name()) fn := a.prog.Method(meth) obj := a.makeFunctionObject(fn, site) // new contour for this call a.callEdge(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(targets, obj) // (a singleton) // Copy receiver. params := a.funcParams(obj) a.copy(params, rtype, 1) params++ // Copy actual parameters into formal params 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 results block to actual result. if result != 0 { a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) } } // genCall generates contraints 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 { 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: // Capture, 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 { if cgn == nil { // 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: if t, ok := v.Type().Underlying().(*types.Slice); ok && !v.IsNil() { // Non-nil []byte or []rune constant. obj = a.nextNode() a.addNodes(sliceToArray(t), "array in slice constant") a.endObject(obj, nil, v) } case *ssa.Capture: // For now, Captures have the same cardinality as globals. // TODO(adonovan): treat captures context-sensitively. } 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") a.addNodes(tmap.Elem(), "makemap.value") 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(dst, obj) } else { a.offsetAddr(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 contraints 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 a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(instr.Type())) 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 ast.RECV: a.genLoad(cgn, recv, st.Chan, 0, elemSize) recv += nodeid(elemSize) case ast.SEND: 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(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()) // 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) } 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 := ssa.NewFunction("", new(types.Signature), "root of callgraph") r.Prog = a.prog // hack. r.Enclosing = r // hack, so Function.String() doesn't crash r.String() // (asserts that it doesn't crash) root := a.makeCGNode(r, 0, nil) // 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.Fprintln(a.log) fmt.Fprintln(a.log) // Hack: don't display body if intrinsic. if impl != nil { fn2 := *cgn.fn // copy fn2.Locals = nil fn2.Blocks = nil fn2.DumpTo(a.log) } else { cgn.fn.DumpTo(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 are treated like global variables: // the outer function sets them with MakeClosure; // the inner function accesses them with Capture. // Create value nodes for all value instructions // since SSA may contain forward references. 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) } } } // 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) { mset := T.MethodSet() for i, n := 0, mset.Len(); i < n; i++ { a.valueNode(a.prog.Method(mset.At(i))) } } // generate generates offline constraints for the entire program. // It returns the synthetic root of the callgraph. // func (a *analysis) generate() *cgnode { // 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() // Create nodes and constraints for all methods of all types // that are dynamically accessible via reflection or interfaces. for _, T := range a.prog.TypesWithMethodSets() { 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)[:] obj := a.addNodes(types.NewArray(types.Typ[types.String], 1), "") a.endObject(obj, nil, "") a.addressOf(a.objectNode(nil, os.Var("Args")), obj) } return root } ./pointer/analysis.go0000644000014500017510000002670712246613010014341 0ustar michaelstaff// 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 pointer // This file defines the main datatypes and Analyze function of the pointer analysis. import ( "fmt" "go/token" "io" "os" "reflect" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types/typemap" "code.google.com/p/go.tools/ssa" ) // 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 words 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" // Points-to sets. pts nodeset // points-to set of this node prevPts nodeset // pts(n) in previous iteration (for difference propagation) // Graph edges copyTo nodeset // simple copy constraint edges // Complex constraints attached to this node (x). // - *loadConstraint y=*x // - *offsetAddrConstraint y=&x.f or y=&x[0] // - *storeConstraint *x=z // - *typeFilterConstraint y=x.(I) // - *untagConstraint y=x.(C) // - *invokeConstraint y=x.f(params...) complex constraintset } type constraint interface { String() string // For a complex constraint, returns the nodeid of the pointer // to which it is attached. ptr() nodeid // solve is called for complex constraints when the pts for // the node to which they are attached has changed. solve(a *analysis, n *node, delta nodeset) } // dst = &src // pts(dst) ⊇ {src} // A base constraint used to initialize the solver's pt sets type addrConstraint struct { dst nodeid // (ptr) src nodeid } // dst = src // A simple constraint represented directly as a copyTo graph edge. type copyConstraint struct { dst nodeid src nodeid // (ptr) } // dst = src[offset] // A complex constraint attached to src (the pointer) type loadConstraint struct { offset uint32 dst nodeid src nodeid // (ptr) } // dst[offset] = src // A complex constraint attached to dst (the pointer) type storeConstraint struct { offset uint32 dst nodeid // (ptr) src nodeid } // 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) } // 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) } // 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 } // 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 first parameter in the params/results block } // 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() 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 probes map[*ssa.CallCommon]nodeid // maps call to print() to argument variable 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 work worklist // solver's worklist result *Result // results of the analysis // Reflection & intrinsics: hasher typemap.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 typemap.M // nodeid of canonical *rtype-tagged object for type T reflectZeros typemap.M // nodeid of canonical T-tagged object for zero value runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer } // enclosingObj returns the object (addressible memory object) that encloses node id. // Panic ensues if that node does not belong to any object. func (a *analysis) enclosingObj(id nodeid) *object { // 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 obj } } 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.enclosingObj(id), subelement: a.nodes[id].subelement, } } func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) { a.result.Warnings = append(a.result.Warnings, Warning{pos, fmt.Sprintf(format, args...)}) } // Analyze runs the pointer analysis with the scope and options // specified by config, and returns the (synthetic) root of the callgraph. // func Analyze(config *Config) *Result { 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), hasher: typemap.MakeHasher(), intrinsics: make(map[*ssa.Function]intrinsic), probes: make(map[*ssa.CallCommon]nodeid), work: makeMapWorklist(), result: &Result{ Queries: make(map[ssa.Value][]Pointer), }, } if false { a.log = os.Stderr // for debugging crashes; extremely verbose } if a.log != nil { fmt.Fprintln(a.log, "======== NEW ANALYSIS ========") } if reflect := a.prog.ImportedPackage("reflect"); reflect != nil { rV := reflect.Object.Scope().Lookup("Value") a.reflectValueObj = rV a.reflectValueCall = a.prog.Method(rV.Type().MethodSet().Lookup(nil, "Call")) a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named) a.reflectRtypeObj = reflect.Object.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}} a.rtypes.SetHasher(a.hasher) a.reflectZeros.SetHasher(a.hasher) } if runtime := a.prog.ImportedPackage("runtime"); runtime != nil { a.runtimeSetFinalizer = runtime.Func("SetFinalizer") } root := a.generate() if a.log != nil { // Show size of constraint system. 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)) for t, n := range counts { fmt.Fprintf(a.log, "\t%s:\t%d\n", t, n) } fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes)) } //a.optimize() a.solve() if a.log != nil { // Dump solution. for i, n := range a.nodes { if n.pts != nil { fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, n.pts, n.typ) } } } // Add dynamic edges to call graph. for _, caller := range a.cgnodes { for _, site := range caller.sites { for callee := range a.nodes[site.targets].pts { a.callEdge(site, callee) } } } if a.config.BuildCallGraph { a.result.CallGraph = &cgraph{root, a.cgnodes} } return a.result } // callEdge is called for each edge in the callgraph. // calleeid is the callee's object node (has otFunction flag). // func (a *analysis) callEdge(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 a.config.BuildCallGraph { site.callees = append(site.callees, callee) } 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)") } } ./pointer/callgraph.go0000644000014500017510000000453212246613010014443 0ustar michaelstaff// 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 pointer // This file defines our implementation of the call.Graph API. import ( "fmt" "go/token" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/ssa" ) // cgraph implements call.Graph. type cgraph struct { root *cgnode nodes []*cgnode } func (g *cgraph) Nodes() []call.GraphNode { nodes := make([]call.GraphNode, len(g.nodes)) for i, node := range g.nodes { nodes[i] = node } return nodes } func (g *cgraph) Root() call.GraphNode { return g.root } // cgnode implements call.GraphNode. type cgnode struct { fn *ssa.Function obj nodeid // start of this contour's object block sites []*callsite // ordered list of callsites within this function callersite *callsite // where called from, if known; nil for shared contours } func (n *cgnode) Func() *ssa.Function { return n.fn } func (n *cgnode) Sites() []ssa.CallInstruction { sites := make([]ssa.CallInstruction, len(n.sites)) for i, site := range n.sites { sites[i] = site.instr } return sites } func (n *cgnode) Edges() []call.Edge { var numEdges int for _, site := range n.sites { numEdges += len(site.callees) } edges := make([]call.Edge, 0, numEdges) for _, site := range n.sites { for _, callee := range site.callees { edges = append(edges, call.Edge{Caller: n, Site: site.instr, Callee: callee}) } } return edges } func (n *cgnode) String() string { return fmt.Sprintf("cg%d:%s", n.obj, n.fn) } // A callsite represents a single call site within a cgnode; // it is implicitly context-sensitive. // callsites never represent calls to built-ins; // they are handled as intrinsics. // type callsite struct { targets nodeid // pts(·) contains objects for dynamically called functions instr ssa.CallInstruction // the call instruction; nil for synthetic/intrinsic callees []*cgnode // unordered set of callees of this site } func (c *callsite) String() string { if c.instr != nil { return c.instr.Common().Description() } return "synthetic function call" } // pos returns the source position of this callsite, or token.NoPos if implicit. func (c *callsite) pos() token.Pos { if c.instr != nil { return c.instr.Pos() } return token.NoPos } ./pointer/util.go0000644000014500017510000001720412246613010013463 0ustar michaelstaff// 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 pointer import ( "bytes" "fmt" "code.google.com/p/go.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 } // isInterface reports whether T is an interface type. func isInterface(T types.Type) bool { _, ok := T.Underlying().(*types.Interface) return ok } // 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. for i, n := 0, t.Len(); 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))) } // 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 ------------------------------------------------------------------- // NB, mutator methods are attached to *nodeset. // nodeset may be a reference, but its address matters! type nodeset map[nodeid]struct{} // ---- Accessors ---- func (ns nodeset) String() string { var buf bytes.Buffer buf.WriteRune('{') var sep string for n := range ns { fmt.Fprintf(&buf, "%sn%d", sep, n) sep = ", " } buf.WriteRune('}') return buf.String() } // diff returns the set-difference x - y. nil => empty. // // TODO(adonovan): opt: extremely inefficient. BDDs do this in // constant time. Sparse bitvectors are linear but very fast. func (x nodeset) diff(y nodeset) nodeset { var z nodeset for k := range x { if _, ok := y[k]; !ok { z.add(k) } } return z } // clone() returns an unaliased copy of x. func (x nodeset) clone() nodeset { return x.diff(nil) } // ---- Mutators ---- func (ns *nodeset) add(n nodeid) bool { sz := len(*ns) if *ns == nil { *ns = make(nodeset) } (*ns)[n] = struct{}{} return len(*ns) > sz } func (x *nodeset) addAll(y nodeset) bool { if y == nil { return false } sz := len(*x) if *x == nil { *x = make(nodeset) } for n := range y { (*x)[n] = struct{}{} } return len(*x) > sz } // Constraint set ------------------------------------------------------------- type constraintset map[constraint]struct{} func (cs *constraintset) add(c constraint) bool { sz := len(*cs) if *cs == nil { *cs = make(constraintset) } (*cs)[c] = struct{}{} return len(*cs) > sz } // Worklist ------------------------------------------------------------------- const empty nodeid = 1<<32 - 1 type worklist interface { add(nodeid) // Adds a node to the set take() nodeid // Takes a node from the set and returns it, or empty } // Simple nondeterministic worklist based on a built-in map. type mapWorklist struct { set nodeset } func (w *mapWorklist) add(n nodeid) { w.set[n] = struct{}{} } func (w *mapWorklist) take() nodeid { for k := range w.set { delete(w.set, k) return k } return empty } func makeMapWorklist() worklist { return &mapWorklist{make(nodeset)} } ./pointer/testdata/0000755000014500017510000000000012246613010013764 5ustar michaelstaff./pointer/testdata/flow.go0000644000014500017510000000164612246613010015271 0ustar michaelstaff// +build ignore package main // Demonstration of directionality of flow edges. func f1() {} func f2() {} var somepred bool // Tracking functions. func flow1() { s := f1 p := f2 q := p r := q if somepred { r = s } print(s) // @pointsto main.f1 print(p) // @pointsto main.f2 print(q) // @pointsto main.f2 print(r) // @pointsto main.f1 | main.f2 } // Tracking concrete types in interfaces. func flow2() { var s interface{} = 1 var p interface{} = "foo" q := p r := q if somepred { r = s } print(s) // @types int print(p) // @types string print(q) // @types string print(r) // @types int | string } var g1, g2 int // Tracking addresses of globals. func flow3() { s := &g1 p := &g2 q := p r := q if somepred { r = s } print(s) // @pointsto main.g1 print(p) // @pointsto main.g2 print(q) // @pointsto main.g2 print(r) // @pointsto main.g2 | main.g1 } func main() { flow1() flow2() flow3() } ./pointer/testdata/func.go0000644000014500017510000000533112246613010015250 0ustar michaelstaff// +build ignore package main var a, b, c int var unknown bool // defeat dead-code elimination func func1() { var h int // @line f1h f := func(x *int) *int { if unknown { return &b } return x } // FV(g) = {f, h} g := func(x *int) *int { if unknown { return &h } return f(x) } print(g(&a)) // @pointsto main.a | main.b | h@f1h:6 print(f(&a)) // @pointsto main.a | main.b print(&a) // @pointsto main.a } // @calls main.func1 -> func@19.7 // @calls main.func1 -> func@11.7 // @calls func@19.7 -> func@11.7 func func2() { var x, y *int defer func() { x = &a }() go func() { y = &b }() print(x) // @pointsto main.a print(y) // @pointsto main.b } func func3() { x, y := func() (x, y *int) { x = &a y = &b if unknown { return nil, &c } return }() print(x) // @pointsto main.a print(y) // @pointsto main.b | main.c } func swap(x, y *int) (*int, *int) { // @line swap print(&x) // @pointsto x@swap:11 print(x) // @pointsto makeslice[*]@func4make:11 print(&y) // @pointsto y@swap:14 print(y) // @pointsto j@f4j:5 return y, x } func func4() { a := make([]int, 10) // @line func4make i, j := 123, 456 // @line f4j _ = i p, q := swap(&a[3], &j) print(p) // @pointsto j@f4j:5 print(q) // @pointsto makeslice[*]@func4make:11 f := &b print(f) // @pointsto main.b } type T int func (t *T) f(x *int) *int { print(t) // @pointsto main.a print(x) // @pointsto main.c return &b } func (t *T) g(x *int) *int { print(t) // @pointsto main.a print(x) // @pointsto main.b return &c } func (t *T) h(x *int) *int { print(t) // @pointsto main.a print(x) // @pointsto main.b return &c } var h func(*T, *int) *int func func5() { // Static call of method. t := (*T)(&a) print(t.f(&c)) // @pointsto main.b // Static call of method as function print((*T).g(t, &b)) // @pointsto main.c // Dynamic call (not invoke) of method. h = (*T).h print(h(t, &b)) // @pointsto main.c } // @calls main.func5 -> (*main.T).f // @calls main.func5 -> (*main.T).g // @calls main.func5 -> (*main.T).h func func6() { A := &a f := func() *int { return A // (free variable) } print(f()) // @pointsto main.a } // @calls main.func6 -> func@121.7 type I interface { f() } type D struct{} func (D) f() {} func func7() { var i I = D{} imethodClosure := i.f imethodClosure() // @calls main.func7 -> bound$(main.I).f // @calls bound$(main.I).f -> (main.D).f var d D cmethodClosure := d.f cmethodClosure() // @calls main.func7 -> bound$(main.D).f // @calls bound$(main.D).f ->(main.D).f methodExpr := D.f methodExpr(d) // @calls main.func7 -> (main.D).f } func main() { func1() func2() func3() func4() func5() func6() func7() } // @calls -> main.main // @calls -> main.init ./pointer/testdata/recur.go0000644000014500017510000000022112246613010015426 0ustar michaelstaff// +build ignore package main // Analysis abstraction of recursive calls is finite. func main() { main() } // @calls main.main -> main.main ./pointer/testdata/funcreflect.go0000644000014500017510000000630112246613010016613 0ustar michaelstaff// +build ignore package main import "reflect" var zero, a, b int var false2 bool func f(p *int, q hasF) *int { print(p) // @pointsto main.a print(q) // @types *T print(q.(*T)) // @pointsto new@newT1:22 return &b } func g(p *bool) (*int, *bool, hasF) { return &b, p, new(T) // @line newT2 } func reflectValueCall() { rvf := reflect.ValueOf(f) res := rvf.Call([]reflect.Value{ // argument order is not significant: reflect.ValueOf(new(T)), // @line newT1 reflect.ValueOf(&a), }) print(res[0].Interface()) // @types *int print(res[0].Interface().(*int)) // @pointsto main.b } // @calls main.reflectValueCall -> main.f func reflectValueCallIndirect() { rvf := reflect.ValueOf(g) call := rvf.Call // kids, don't try this at home // Indirect call uses shared contour. // // Also notice that argument position doesn't matter, and args // of inappropriate type (e.g. 'a') are ignored. res := call([]reflect.Value{ reflect.ValueOf(&a), reflect.ValueOf(&false2), }) res0 := res[0].Interface() print(res0) // @types *int | *bool | *T print(res0.(*int)) // @pointsto main.b print(res0.(*bool)) // @pointsto main.false2 print(res0.(hasF)) // @types *T print(res0.(*T)) // @pointsto new@newT2:19 } // @calls main.reflectValueCallIndirect -> bound$(reflect.Value).Call // @calls bound$(reflect.Value).Call -> main.g func reflectTypeInOut() { var f func(float64, bool) (string, int) print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface()) // @types float64 print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface()) // @types bool print(reflect.Zero(reflect.TypeOf(f).In(-1)).Interface()) // @types float64 | bool print(reflect.Zero(reflect.TypeOf(f).In(zero)).Interface()) // @types float64 | bool print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types int print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types } type hasF interface { F() } type T struct{} func (T) F() {} func (T) g(int) {} type U struct{} func (U) F(int) {} func (U) g(string) {} var nonconst string func reflectTypeMethodByName() { TU := reflect.TypeOf([]interface{}{T{}, U{}}[0]) print(reflect.Zero(TU)) // @types T | U F, _ := TU.MethodByName("F") print(reflect.Zero(F.Type)) // @types func(T) | func(U, int) print(F.Func) // @pointsto (main.T).F | (main.U).F g, _ := TU.MethodByName("g") print(reflect.Zero(g.Type)) // @types func(T, int) | func(U, string) print(g.Func) // @pointsto (main.T).g | (main.U).g // Non-literal method names are treated less precisely. U := reflect.TypeOf(U{}) X, _ := U.MethodByName(nonconst) print(reflect.Zero(X.Type)) // @types func(U, int) | func(U, string) print(X.Func) // @pointsto (main.U).F | (main.U).g } func reflectTypeMethod() { m := reflect.TypeOf(T{}).Method(0) print(reflect.Zero(m.Type)) // @types func(T) | func(T, int) print(m.Func) // @pointsto (main.T).F | (main.T).g } func main() { reflectValueCall() reflectValueCallIndirect() reflectTypeInOut() reflectTypeMethodByName() reflectTypeMethod() } ./pointer/testdata/another.go0000644000014500017510000000160612246613010015756 0ustar michaelstaff// +build ignore package main var unknown bool type S string func incr(x int) int { return x + 1 } func main() { var i interface{} i = 1 if unknown { i = S("foo") } if unknown { i = (func(int, int))(nil) // NB type compares equal to that below. } // Look, the test harness can handle equal-but-not-String-equal // types because we parse types and using a typemap. if unknown { i = (func(x int, y int))(nil) } if unknown { i = incr } print(i) // @types int | S | func(int, int) | func(int) int // NB, an interface may never directly alias any global // labels, even though it may contain pointers that do. print(i) // @pointsto makeinterface:func(x int) int | makeinterface:func(x int, y int) | makeinterface:func(int, int) | makeinterface:int | makeinterface:main.S print(i.(func(int) int)) // @pointsto main.incr print(i.(S)) // @pointsto } ./pointer/testdata/rtti.go0000644000014500017510000000121312246613010015272 0ustar michaelstaffpackage main // Regression test for oracle crash // https://code.google.com/p/go/issues/detail?id=6605 // // Using reflection, methods may be called on types that are not the // operand of any ssa.MakeInterface instruction. In this example, // (Y).F is called by deriving the type Y from *Y. Prior to the fix, // no RTTI (or method set) for type Y was included in the program, so // the F() call would crash. import "reflect" var a int type X struct{} func (X) F() *int { return &a } type I interface { F() *int } func main() { type Y struct{ X } print(reflect.Indirect(reflect.ValueOf(new(Y))).Interface().(I).F()) // @pointsto main.a } ./pointer/testdata/arrays.go0000644000014500017510000000462312246613010015621 0ustar michaelstaff// +build ignore package main var unknown bool // defeat dead-code elimination var a, b int func array1() { sliceA := make([]*int, 10) // @line a1make sliceA[0] = &a var sliceB []*int sliceB = append(sliceB, &b) // @line a1append print(sliceA) // @pointsto makeslice@a1make:16 print(sliceA[0]) // @pointsto main.a print(sliceB) // @pointsto append@a1append:17 print(sliceB[100]) // @pointsto main.b } func array2() { sliceA := make([]*int, 10) // @line a2make sliceA[0] = &a sliceB := sliceA[:] print(sliceA) // @pointsto makeslice@a2make:16 print(sliceA[0]) // @pointsto main.a print(sliceB) // @pointsto makeslice@a2make:16 print(sliceB[0]) // @pointsto main.a } func array3() { a := []interface{}{"", 1} b := []interface{}{true, func() {}} print(a[0]) // @types string | int print(b[0]) // @types bool | func() } // Test of append, copy, slice. func array4() { var s2 struct { // @line a4L0 a [3]int b struct{ c, d int } } var sl1 = make([]*int, 10) // @line a4make var someint int // @line a4L1 sl1[1] = &someint sl2 := append(sl1, &s2.a[1]) // @line a4append1 print(sl1) // @pointsto makeslice@a4make:16 print(sl2) // @pointsto append@a4append1:15 | makeslice@a4make:16 print(sl1[0]) // @pointsto someint@a4L1:6 | s2.a[*]@a4L0:6 print(sl2[0]) // @pointsto someint@a4L1:6 | s2.a[*]@a4L0:6 // In z=append(x,y) we should observe flow from y[*] to x[*]. var sl3 = make([]*int, 10) // @line a4L2 _ = append(sl3, &s2.a[1]) print(sl3) // @pointsto makeslice@a4L2:16 print(sl3[0]) // @pointsto s2.a[*]@a4L0:6 var sl4 = []*int{&a} // @line a4L3 sl4a := append(sl4) // @line a4L4 print(sl4a) // @pointsto slicelit@a4L3:18 | append@a4L4:16 print(&sl4a[0]) // @pointsto slicelit[*]@a4L3:18 | append[*]@a4L4:16 print(sl4a[0]) // @pointsto main.a var sl5 = []*int{&b} // @line a4L5 copy(sl5, sl4) print(sl5) // @pointsto slicelit@a4L5:18 print(&sl5[0]) // @pointsto slicelit[*]@a4L5:18 print(sl5[0]) // @pointsto main.b | main.a var sl6 = sl5[:0] print(sl6) // @pointsto slicelit@a4L5:18 print(&sl6[0]) // @pointsto slicelit[*]@a4L5:18 print(sl6[0]) // @pointsto main.b | main.a } func array5() { var arr [2]*int arr[0] = &a arr[1] = &b var n int print(arr[n]) // @pointsto main.a | main.b } func main() { array1() array2() array3() array4() array5() } ./pointer/testdata/conv.go0000644000014500017510000000261112246613010015260 0ustar michaelstaff// +build ignore package main import "unsafe" var a int func conv1() { // Conversions of channel direction. ch := make(chan int) // @line c1make print((<-chan int)(ch)) // @pointsto makechan@c1make:12 print((chan<- int)(ch)) // @pointsto makechan@c1make:12 } func conv2() { // []byte/[]rune literal print([]byte("foo")) // @pointsto "foo":[]byte print([]rune("bar")) // @pointsto "bar":[]rune // string -> []byte/[]rune conversion s := "foo" ba := []byte(s) // @line c2ba ra := []rune(s) // @line c2ra print(ba) // @pointsto convert@c2ba:14 print(ra) // @pointsto convert@c2ra:14 } func conv3() { // Conversion of same underlying types. type PI *int pi := PI(&a) print(pi) // @pointsto main.a pint := (*int)(pi) print(pint) // @pointsto main.a // Conversions between pointers to identical base types. var y *PI = &pi var x **int = (**int)(y) print(*x) // @pointsto main.a print(*y) // @pointsto main.a y = (*PI)(x) print(*y) // @pointsto main.a } // @warning "main.conv4 contains an unsafe.Pointer conversion" func conv4() { // Handling of unsafe.Pointer conversion is unsound: // we lose the alias to main.a and get something like new(int) instead. // We require users to provide aliasing summaries. p := (*int)(unsafe.Pointer(&a)) // @line c2p print(p) // @pointsto convert@c2p:13 } func main() { conv1() conv2() conv3() conv4() } ./pointer/testdata/chanreflect1.go0000644000014500017510000000132712246613010016655 0ustar michaelstaff// +build ignore package main import "reflect" // // This test is very sensitive to line-number perturbations! // Test of channels with reflection. var a, b int func chanreflect1() { ch := make(chan *int, 0) crv := reflect.ValueOf(ch) crv.Send(reflect.ValueOf(&a)) print(crv.Interface()) // @types chan *int print(crv.Interface().(chan *int)) // @pointsto makechan@testdata/chanreflect.go:15:12 print(<-ch) // @pointsto main.a } func chanreflect2() { ch := make(chan *int, 0) ch <- &b crv := reflect.ValueOf(ch) r, _ := crv.Recv() print(r.Interface()) // @types *int print(r.Interface().(*int)) // @pointsto main.b } func main() { chanreflect1() chanreflect2() } ./pointer/testdata/a_test.go0000644000014500017510000000207012246613010015571 0ustar michaelstaff// +build ignore package a // This test exercises the synthesis of testmain packages for tests. // The test framework doesn't directly let us perform negative // assertions (i.e. that TestingQuux isn't called, or that its // parameter's PTS is empty) so this test is rather roundabout. import "testing" func log(f func(*testing.T)) { // The PTS of f is the set of called tests. TestingQuux is not present. print(f) // @pointsto main.Test | main.TestFoo } func Test(t *testing.T) { // Don't assert @pointsto(t) since its label contains a fragile line number. log(Test) } func TestFoo(t *testing.T) { // Don't assert @pointsto(t) since its label contains a fragile line number. log(TestFoo) } func TestingQuux(t *testing.T) { // We can't assert @pointsto(t) since this is dead code. log(TestingQuux) } func BenchmarkFoo(b *testing.B) { } func ExampleBar() { } // Excludes TestingQuux. // @calls testing.tRunner -> main.Test // @calls testing.tRunner -> main.TestFoo // @calls testing.runExample -> main.ExampleBar // @calls (*testing.B).runN -> main.BenchmarkFoo ./pointer/testdata/maps.go0000644000014500017510000000166212246613010015260 0ustar michaelstaff// +build ignore package main // Test of maps. var a, b, c int func maps1() { m1 := map[*int]*int{&a: &b} // @line m1m1 m2 := make(map[*int]*int) // @line m1m2 m2[&b] = &a print(m1[nil]) // @pointsto main.b | main.c print(m2[nil]) // @pointsto main.a print(m1) // @pointsto makemap@m1m1:21 print(m2) // @pointsto makemap@m1m2:12 m1[&b] = &c for k, v := range m1 { print(k) // @pointsto main.a | main.b print(v) // @pointsto main.b | main.c } for k, v := range m2 { print(k) // @pointsto main.b print(v) // @pointsto main.a } // Lookup doesn't create any aliases. print(m2[&c]) // @pointsto main.a if _, ok := m2[&a]; ok { print(m2[&c]) // @pointsto main.a } } func maps2() { m1 := map[*int]*int{&a: &b} m2 := map[*int]*int{&b: &c} _ = []map[*int]*int{m1, m2} // (no spurious merging of m1, m2) print(m1[nil]) // @pointsto main.b print(m2[nil]) // @pointsto main.c } func main() { maps1() maps2() } ./pointer/testdata/channels.go0000644000014500017510000000507612246613010016116 0ustar michaelstaff// +build ignore package main func incr(x int) int { return x + 1 } func decr(x int) int { return x - 1 } var unknown bool // defeat dead-code elimination func chan1() { chA := make(chan func(int) int, 0) // @line c1makeA chB := make(chan func(int) int, 0) // @line c1makeB chA <- incr chB <- decr chB <- func(int) int { return 1 } print(chA) // @pointsto makechan@c1makeA:13 print(<-chA) // @pointsto main.incr print(chB) // @pointsto makechan@c1makeB:13 print(<-chB) // @pointsto main.decr | func@16.9 } func chan2() { chA := make(chan func(int) int, 0) // @line c2makeA chB := make(chan func(int) int, 0) // @line c2makeB chA <- incr chB <- decr chB <- func(int) int { return 1 } // Channels flow together. // Labelsets remain distinct but elements are merged. chAB := chA if unknown { chAB = chB } print(chA) // @pointsto makechan@c2makeA:13 print(<-chA) // @pointsto main.incr print(chB) // @pointsto makechan@c2makeB:13 print(<-chB) // @pointsto main.decr | func@30.9 print(chAB) // @pointsto makechan@c2makeA:13 | makechan@c2makeB:13 print(<-chAB) // @pointsto main.incr | main.decr | func@30.9 (<-chA)(3) } // @calls main.chan2 -> main.incr func chan3() { chA := make(chan func(int) int, 0) // @line c3makeA chB := make(chan func(int) int, 0) // @line c3makeB chA <- incr chB <- decr chB <- func(int) int { return 1 } print(chA) // @pointsto makechan@c3makeA:13 print(<-chA) // @pointsto main.incr print(chB) // @pointsto makechan@c3makeB:13 print(<-chB) // @pointsto main.decr | func@58.9 (<-chA)(3) } // @calls main.chan3 -> main.incr func chan4() { chA := make(chan func(int) int, 0) // @line c4makeA chB := make(chan func(int) int, 0) // @line c4makeB select { case chA <- incr: case chB <- decr: case a := <-chA: print(a) // @pointsto main.incr case b := <-chB: print(b) // @pointsto main.decr default: print(chA) // @pointsto makechan@c4makeA:13 print(chB) // @pointsto makechan@c4makeB:13 } for k := range chA { print(k) // @pointsto main.incr } // Exercise constraint generation (regtest for a crash). for _ = range chA { } } // Multi-word channel value in select with multiple receive cases. // (Regtest for a crash.) func chan5() { type T struct { x *int y interface{} } ch := make(chan T) ch <- T{new(int), incr} // @line ch5new select { case a := <-ch: print(a.x) // @pointsto new@ch5new:13 print(a.y) // @types func(x int) int case b := <-ch: print(b.x) // @pointsto new@ch5new:13 print(b.y) // @types func(x int) int } } func main() { chan1() chan2() chan3() chan4() chan5() } ./pointer/testdata/panic.go0000644000014500017510000000122312246613010015403 0ustar michaelstaff// +build ignore package main // Test of value flow from panic() to recover(). // We model them as stores/loads of a global location. // We ignore concrete panic types originating from the runtime. var someval int type myPanic struct{} func f(int) {} func g() string { return "" } func deadcode() { panic(123) // not reached } func main() { switch someval { case 0: panic("oops") case 1: panic(myPanic{}) case 2: panic(f) case 3: panic(g) } ex := recover() print(ex) // @types myPanic | string | func(int) | func() string print(ex.(func(int))) // @pointsto main.f print(ex.(func() string)) // @pointsto main.g } ./pointer/testdata/finalizer.go0000644000014500017510000000347512246613010016307 0ustar michaelstaffpackage main import "runtime" func final1a(x *int) int { print(x) // @pointsto new@newint:10 return *x } func final1b(x *bool) { print(x) // @pointsto } func runtimeSetFinalizer1() { x := new(int) // @line newint runtime.SetFinalizer(x, final1a) // ok: final1a's result is ignored runtime.SetFinalizer(x, final1b) // param type mismatch: no effect } // @calls main.runtimeSetFinalizer1 -> main.final1a // @calls main.runtimeSetFinalizer1 -> main.final1b func final2a(x *bool) { print(x) // @pointsto new@newbool1:10 | new@newbool2:10 } func final2b(x *bool) { print(x) // @pointsto new@newbool1:10 | new@newbool2:10 } func runtimeSetFinalizer2() { x := new(bool) // @line newbool1 f := final2a if unknown { x = new(bool) // @line newbool2 f = final2b } runtime.SetFinalizer(x, f) } // @calls main.runtimeSetFinalizer2 -> main.final2a // @calls main.runtimeSetFinalizer2 -> main.final2b type T int func (t *T) finalize() { print(t) // @pointsto new@final3:10 } func runtimeSetFinalizer3() { x := new(T) // @line final3 runtime.SetFinalizer(x, (*T).finalize) } // @calls main.runtimeSetFinalizer3 -> (*main.T).finalize // I hope I never live to see this code in the wild. var setFinalizer = runtime.SetFinalizer func final4(x *int) { print(x) // @pointsto new@finalIndirect:10 } func runtimeSetFinalizerIndirect() { // In an indirect call, the shared contour for SetFinalizer is // used, i.e. the call is not inlined and appears in the call graph. x := new(int) // @line finalIndirect setFinalizer(x, final4) } // @calls main.runtimeSetFinalizerIndirect -> runtime.SetFinalizer // @calls runtime.SetFinalizer -> main.final4 func main() { runtimeSetFinalizer1() runtimeSetFinalizer2() runtimeSetFinalizer3() runtimeSetFinalizerIndirect() } var unknown bool // defeat dead-code elimination ./pointer/testdata/chanreflect.go0000644000014500017510000000475412246613010016603 0ustar michaelstaff// +build ignore package main import "reflect" // Test of channels with reflection. var a, b int func chanreflect1() { ch := make(chan *int, 0) // @line cr1make crv := reflect.ValueOf(ch) crv.Send(reflect.ValueOf(&a)) print(crv.Interface()) // @types chan *int print(crv.Interface().(chan *int)) // @pointsto makechan@cr1make:12 print(<-ch) // @pointsto main.a } func chanreflect1i() { // Exercises reflect.Value conversions to/from interfaces: // a different code path than for concrete types. ch := make(chan interface{}, 0) reflect.ValueOf(ch).Send(reflect.ValueOf(&a)) v := <-ch print(v) // @types *int print(v.(*int)) // @pointsto main.a } func chanreflect2() { ch := make(chan *int, 0) ch <- &b crv := reflect.ValueOf(ch) r, _ := crv.Recv() print(r.Interface()) // @types *int print(r.Interface().(*int)) // @pointsto main.b } func chanOfRecv() { // MakeChan(<-chan) is a no-op. t := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types <-chan *int print(reflect.MakeChan(t, 0).Interface().(<-chan *int)) // @pointsto print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto } func chanOfSend() { // MakeChan(chan<-) is a no-op. t := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types chan<- *int print(reflect.MakeChan(t, 0).Interface().(chan<- *int)) // @pointsto print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto } func chanOfBoth() { t := reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types chan *int ch := reflect.MakeChan(t, 0) print(ch.Interface().(chan *int)) // @pointsto ch.Send(reflect.ValueOf(&b)) ch.Interface().(chan *int) <- &a r, _ := ch.Recv() print(r.Interface().(*int)) // @pointsto main.a | main.b print(<-ch.Interface().(chan *int)) // @pointsto main.a | main.b } var unknownDir reflect.ChanDir // not a constant func chanOfUnknown() { // Unknown channel direction: assume all three. // MakeChan only works on the bi-di channel type. t := reflect.ChanOf(unknownDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int print(reflect.MakeChan(t, 0).Interface()) // @types chan *int } func main() { chanreflect1() chanreflect1i() chanreflect2() chanOfRecv() chanOfSend() chanOfBoth() chanOfUnknown() } ./pointer/testdata/structs.go0000644000014500017510000000403612246613010016025 0ustar michaelstaff// +build ignore package main var unknown bool // defeat dead-code elimination var p, q int type A struct { f *int g interface{} } func (a A) m1() { print(a.f) // @pointsto main.p } func (a *A) m2() { print(a) // @pointsto complit.A@struct1s:9 print(a.f) // @pointsto main.p } type B struct { h *int A } func structs1() { b := &B{ // @line struct1s h: &q, } b.f = &p b.g = b print(b.h) // @pointsto main.q print(b.f) // @pointsto main.p print(b.g) // @types *B ptr := &b.f print(*ptr) // @pointsto main.p b.m1() b.m2() } // @calls main.structs1 -> (main.A).m1 // @calls main.structs1 -> (*main.A).m2 // @calls (*main.B).m1 -> (main.A).m1 // @calls (*main.B).m2 -> (*main.A).m2 type T struct { x int y int } type S struct { a [3]T b *[3]T c [3]*T } func structs2() { var s S // @line s2s print(&s) // @pointsto s@s2s:6 print(&s.a) // @pointsto s.a@s2s:6 print(&s.a[0]) // @pointsto s.a[*]@s2s:6 print(&s.a[0].x) // @pointsto s.a[*].x@s2s:6 print(&s.a[0].y) // @pointsto s.a[*].y@s2s:6 print(&s.b) // @pointsto s.b@s2s:6 print(&s.b[0]) // @pointsto print(&s.b[0].x) // @pointsto print(&s.b[0].y) // @pointsto print(&s.c) // @pointsto s.c@s2s:6 print(&s.c[0]) // @pointsto s.c[*]@s2s:6 print(&s.c[0].x) // @pointsto print(&s.c[0].y) // @pointsto var s2 S // @line s2s2 s2.b = new([3]T) // @line s2s2b print(s2.b) // @pointsto new@s2s2b:12 print(&s2.b) // @pointsto s2.b@s2s2:6 print(&s2.b[0]) // @pointsto new[*]@s2s2b:12 print(&s2.b[0].x) // @pointsto new[*].x@s2s2b:12 print(&s2.b[0].y) // @pointsto new[*].y@s2s2b:12 print(&s2.c[0].x) // @pointsto print(&s2.c[0].y) // @pointsto var s3 S // @line s2s3 s3.c[2] = new(T) // @line s2s3c print(s3.c) // @pointsto print(&s3.c) // @pointsto s3.c@s2s3:6 print(s3.c[1]) // @pointsto new@s2s3c:15 print(&s3.c[1]) // @pointsto s3.c[*]@s2s3:6 print(&s3.c[1].x) // @pointsto new.x@s2s3c:15 print(&s3.c[1].y) // @pointsto new.y@s2s3c:15 } func main() { structs1() structs2() } ./pointer/testdata/fmtexcerpt.go0000644000014500017510000000120312246613010016470 0ustar michaelstaff// +build ignore // This is a slice of the fmt package. package main type pp struct { field interface{} } func newPrinter() *pp { return new(pp) } func Fprintln(a ...interface{}) { p := newPrinter() p.doPrint(a, true, true) } func Println(a ...interface{}) { Fprintln(a...) } func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { print(a[0]) // @types S | string stringer := a[0].(interface { String() string }) stringer.String() print(stringer) // @types S } type S int func (S) String() string { return "" } func main() { Println("Hello, World!", S(0)) } // @calls (*main.pp).doPrint -> (main.S).String ./pointer/testdata/interfaces.go0000644000014500017510000000513612246613010016443 0ustar michaelstaff// +build ignore package main type I interface { f() } type C int func (*C) f() {} type D struct{ ptr *int } func (D) f() {} type E struct{} func (*E) f() {} var a, b int var unknown bool // defeat dead-code elimination func interface1() { var i interface{} = &a var j interface{} = D{&b} k := j if unknown { k = i } print(i) // @types *int print(j) // @types D print(k) // @types *int | D print(i.(*int)) // @pointsto main.a print(j.(*int)) // @pointsto print(k.(*int)) // @pointsto main.a print(i.(D).ptr) // @pointsto print(j.(D).ptr) // @pointsto main.b print(k.(D).ptr) // @pointsto main.b } func interface2() { var i I = (*C)(&a) var j I = D{&a} k := j if unknown { k = i } print(i) // @types *C print(j) // @types D print(k) // @types *C | D print(k) // @pointsto makeinterface:main.D | makeinterface:*main.C k.f() // @calls main.interface2 -> (*main.C).f // @calls main.interface2 -> (main.D).f print(i.(*C)) // @pointsto main.a print(j.(D).ptr) // @pointsto main.a print(k.(*C)) // @pointsto main.a switch x := k.(type) { case *C: print(x) // @pointsto main.a case D: print(x.ptr) // @pointsto main.a case *E: print(x) // @pointsto } } func interface3() { // There should be no backflow of concrete types from the type-switch to x. var x interface{} = 0 print(x) // @types int switch x.(type) { case int: case string: } } func interface4() { var i interface{} = D{&a} if unknown { i = 123 } print(i) // @types int | D j := i.(I) // interface narrowing type-assertion print(j) // @types D print(j.(D).ptr) // @pointsto main.a var l interface{} = j // interface widening assignment. print(l) // @types D print(l.(D).ptr) // @pointsto main.a m := j.(interface{}) // interface widening type-assertion. print(m) // @types D print(m.(D).ptr) // @pointsto main.a } // Interface method calls and value flow: type J interface { f(*int) *int } type P struct { x int } func (p *P) f(pi *int) *int { print(p) // @pointsto p@i5p:6 print(pi) // @pointsto i@i5i:6 return &p.x } func interface5() { var p P // @line i5p var j J = &p var i int // @line i5i print(j.f(&i)) // @pointsto p.x@i5p:6 print(&i) // @pointsto i@i5i:6 print(j) // @pointsto makeinterface:*main.P } // @calls main.interface5 -> (*main.P).f func interface6() { f := I.f print(f) // @pointsto (main.I).f f(new(struct{ D })) } // @calls main.interface6 -> (main.I).f // @calls (main.I).f -> (*struct{main.D}).f func main() { interface1() interface2() interface3() interface4() interface5() interface6() } ./pointer/testdata/structreflect.go0000644000014500017510000000177012246613010017211 0ustar michaelstaff// +build ignore package main import "reflect" type A struct { f *int g interface{} h bool } var dyn string func reflectTypeFieldByName() { f, _ := reflect.TypeOf(A{}).FieldByName("f") print(f.Type) // @pointsto *int g, _ := reflect.TypeOf(A{}).FieldByName("g") print(g.Type) // @pointsto interface{} print(reflect.Zero(g.Type)) // @pointsto print(reflect.Zero(g.Type)) // @types interface{} print(reflect.Zero(g.Type).Interface()) // @pointsto print(reflect.Zero(g.Type).Interface()) // @types h, _ := reflect.TypeOf(A{}).FieldByName("h") print(h.Type) // @pointsto bool missing, _ := reflect.TypeOf(A{}).FieldByName("missing") print(missing.Type) // @pointsto dyn, _ := reflect.TypeOf(A{}).FieldByName(dyn) print(dyn.Type) // @pointsto *int | bool | interface{} } func reflectTypeField() { fld := reflect.TypeOf(A{}).Field(0) print(fld.Type) // @pointsto *int | bool | interface{} } func main() { reflectTypeFieldByName() reflectTypeField() } ./pointer/testdata/arrayreflect.go0000644000014500017510000001271712246613010017006 0ustar michaelstaff// +build ignore package main // Test of arrays & slices with reflection. import "reflect" var a, b int type S string func reflectValueSlice() { // reflect.Value contains a slice. slice := make([]*int, 10) // @line slice slice[0] = &a rvsl := reflect.ValueOf(slice).Slice(0, 0) print(rvsl.Interface()) // @types []*int print(rvsl.Interface().([]*int)) // @pointsto makeslice@slice:15 print(rvsl.Interface().([]*int)[42]) // @pointsto main.a // reflect.Value contains an arrayay (non-addressable). array := [10]*int{&a} // @line array rvarray := reflect.ValueOf(array).Slice(0, 0) print(rvarray.Interface()) // @types print(rvarray.Interface().([]*int)) // @pointsto print(rvarray.Interface().([]*int)[42]) // @pointsto // reflect.Value contains a pointer-to-array rvparray := reflect.ValueOf(&array).Slice(0, 0) print(rvparray.Interface()) // @types []*int print(rvparray.Interface().([]*int)) // @pointsto array@array:2 print(rvparray.Interface().([]*int)[42]) // @pointsto main.a // reflect.Value contains a string. rvstring := reflect.ValueOf("hi").Slice(0, 0) print(rvstring.Interface()) // @types string // reflect.Value contains a (named) string type. rvS := reflect.ValueOf(S("hi")).Slice(0, 0) print(rvS.Interface()) // @types S // reflect.Value contains a non-array pointer. rvptr := reflect.ValueOf(new(int)).Slice(0, 0) print(rvptr.Interface()) // @types // reflect.Value contains a non-string basic type. rvint := reflect.ValueOf(3).Slice(0, 0) print(rvint.Interface()) // @types } func reflectValueBytes() { sl1 := make([]byte, 0) // @line ar5sl1 sl2 := make([]byte, 0) // @line ar5sl2 rvsl1 := reflect.ValueOf(sl1) print(rvsl1.Interface()) // @types []byte print(rvsl1.Interface().([]byte)) // @pointsto makeslice@ar5sl1:13 print(rvsl1.Bytes()) // @pointsto makeslice@ar5sl1:13 rvsl2 := reflect.ValueOf(123) rvsl2.SetBytes(sl2) print(rvsl2.Interface()) // @types int print(rvsl2.Interface().([]byte)) // @pointsto print(rvsl2.Bytes()) // @pointsto rvsl3 := reflect.ValueOf([]byte(nil)) rvsl3.SetBytes(sl2) print(rvsl3.Interface()) // @types []byte print(rvsl3.Interface().([]byte)) // @pointsto makeslice@ar5sl2:13 print(rvsl3.Bytes()) // @pointsto makeslice@ar5sl2:13 } func reflectValueIndex() { slice := []*int{&a} // @line ar6slice rv1 := reflect.ValueOf(slice) print(rv1.Index(42).Interface()) // @types *int print(rv1.Index(42).Interface().(*int)) // @pointsto main.a array := [10]*int{&a} rv2 := reflect.ValueOf(array) print(rv2.Index(42).Interface()) // @types *int print(rv2.Index(42).Interface().(*int)) // @pointsto main.a rv3 := reflect.ValueOf("string") print(rv3.Index(42).Interface()) // @types rune rv4 := reflect.ValueOf(&array) print(rv4.Index(42).Interface()) // @types rv5 := reflect.ValueOf(3) print(rv5.Index(42).Interface()) // @types } func reflectValueElem() { // Interface. var iface interface{} = &a rv1 := reflect.ValueOf(&iface).Elem() print(rv1.Interface()) // @types *int print(rv1.Interface().(*int)) // @pointsto main.a print(rv1.Elem().Interface()) // @types *int print(rv1.Elem().Interface().(*int)) // @pointsto main.a print(reflect.ValueOf(new(interface{})).Elem().Elem()) // @types // Pointer. ptr := &a rv2 := reflect.ValueOf(&ptr) print(rv2.Elem().Interface()) // @types *int print(rv2.Elem().Interface().(*int)) // @pointsto main.a // No other type works with (rV).Elem, not even those that // work with (rT).Elem: slice, array, map, chan. rv3 := reflect.ValueOf([]*int{&a}) print(rv3.Elem().Interface()) // @types rv4 := reflect.ValueOf([10]*int{&a}) print(rv4.Elem().Interface()) // @types rv5 := reflect.ValueOf(map[*int]*int{&a: &b}) print(rv5.Elem().Interface()) // @types ch := make(chan *int) ch <- &a rv6 := reflect.ValueOf(ch) print(rv6.Elem().Interface()) // @types rv7 := reflect.ValueOf(3) print(rv7.Elem().Interface()) // @types } func reflectTypeElem() { rt1 := reflect.TypeOf(make([]*int, 0)) print(reflect.Zero(rt1.Elem())) // @types *int rt2 := reflect.TypeOf([10]*int{}) print(reflect.Zero(rt2.Elem())) // @types *int rt3 := reflect.TypeOf(map[*int]*int{}) print(reflect.Zero(rt3.Elem())) // @types *int rt4 := reflect.TypeOf(make(chan *int)) print(reflect.Zero(rt4.Elem())) // @types *int ptr := &a rt5 := reflect.TypeOf(&ptr) print(reflect.Zero(rt5.Elem())) // @types *int rt6 := reflect.TypeOf(3) print(reflect.Zero(rt6.Elem())) // @types } func reflectPtrTo() { tInt := reflect.TypeOf(3) tPtrInt := reflect.PtrTo(tInt) print(reflect.Zero(tPtrInt)) // @types *int tPtrPtrInt := reflect.PtrTo(tPtrInt) print(reflect.Zero(tPtrPtrInt)) // @types **int } func reflectSliceOf() { tInt := reflect.TypeOf(3) tSliceInt := reflect.SliceOf(tInt) print(reflect.Zero(tSliceInt)) // @types []int } type T struct{ x int } func reflectMakeSlice() { rt := []reflect.Type{ reflect.TypeOf(3), reflect.TypeOf([]int{}), reflect.TypeOf([]T{}), }[0] sl := reflect.MakeSlice(rt, 0, 0) print(sl) // @types []int | []T print(sl) // @pointsto | print(&sl.Interface().([]T)[0].x) // @pointsto [*].x } func main() { reflectValueSlice() reflectValueBytes() reflectValueIndex() reflectValueElem() reflectTypeElem() reflectPtrTo() reflectSliceOf() reflectMakeSlice() } ./pointer/testdata/mapreflect.go0000644000014500017510000000723612246613010016445 0ustar michaelstaff// +build ignore package main // Test of maps with reflection. import "reflect" var a int var b bool func reflectMapKeysIndex() { m := make(map[*int]*bool) // @line mr1make m[&a] = &b mrv := reflect.ValueOf(m) print(mrv.Interface()) // @types map[*int]*bool print(mrv.Interface().(map[*int]*bool)) // @pointsto makemap@mr1make:11 print(mrv) // @pointsto makeinterface:map[*int]*bool print(mrv) // @types map[*int]*bool keys := mrv.MapKeys() print(keys) // @pointsto for _, k := range keys { print(k) // @pointsto print(k) // @types *int print(k.Interface()) // @types *int print(k.Interface().(*int)) // @pointsto main.a v := mrv.MapIndex(k) print(v.Interface()) // @types *bool print(v.Interface().(*bool)) // @pointsto main.b } } func reflectSetMapIndex() { m := make(map[*int]*bool) mrv := reflect.ValueOf(m) mrv.SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b)) print(m[nil]) // @pointsto main.b for _, k := range mrv.MapKeys() { print(k.Interface()) // @types *int print(k.Interface().(*int)) // @pointsto main.a } tmap := reflect.TypeOf(m) // types.EvalNode won't let us refer to non-exported types: // print(tmap) // #@types *reflect.rtype print(tmap) // @pointsto map[*int]*bool zmap := reflect.Zero(tmap) print(zmap) // @pointsto print(zmap.Interface()) // @pointsto print(tmap.Key()) // @pointsto *int print(tmap.Elem()) // @pointsto *bool print(reflect.Zero(tmap.Key())) // @pointsto print(reflect.Zero(tmap.Key()).Interface()) // @pointsto print(reflect.Zero(tmap.Key()).Interface()) // @types *int print(reflect.Zero(tmap.Elem())) // @pointsto print(reflect.Zero(tmap.Elem()).Interface()) // @pointsto print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool } func reflectSetMapIndexInterface() { // Exercises reflect.Value conversions to/from interfaces: // a different code path than for concrete types. m := make(map[interface{}]interface{}) reflect.ValueOf(m).SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b)) for k, v := range m { print(k) // @types *int print(k.(*int)) // @pointsto main.a print(v) // @types *bool print(v.(*bool)) // @pointsto main.b } } func reflectSetMapIndexAssignable() { // SetMapIndex performs implicit assignability conversions. type I *int type J *int str := reflect.ValueOf("") // *int is assignable to I. m1 := make(map[string]I) reflect.ValueOf(m1).SetMapIndex(str, reflect.ValueOf(new(int))) // @line int print(m1[""]) // @pointsto new@int:58 // I is assignable to I. m2 := make(map[string]I) reflect.ValueOf(m2).SetMapIndex(str, reflect.ValueOf(I(new(int)))) // @line I print(m2[""]) // @pointsto new@I:60 // J is not assignable to I. m3 := make(map[string]I) reflect.ValueOf(m3).SetMapIndex(str, reflect.ValueOf(J(new(int)))) print(m3[""]) // @pointsto } func reflectMakeMap() { t := reflect.TypeOf(map[*int]*bool(nil)) v := reflect.MakeMap(t) print(v) // @types map[*int]*bool print(v) // @pointsto } func main() { reflectMapKeysIndex() reflectSetMapIndex() reflectSetMapIndexInterface() reflectSetMapIndexAssignable() reflectMakeMap() // TODO(adonovan): reflect.MapOf(Type) } ./pointer/testdata/reflect.go0000644000014500017510000000650412246613010015744 0ustar michaelstaff// +build ignore package main import "reflect" import "unsafe" var a, b int var unknown bool func reflectIndirect() { ptr := &a // Pointer: print(reflect.Indirect(reflect.ValueOf(&ptr)).Interface().(*int)) // @pointsto main.a // Non-pointer: print(reflect.Indirect(reflect.ValueOf([]*int{ptr})).Interface().([]*int)[0]) // @pointsto main.a } func reflectNewAt() { var x [8]byte print(reflect.NewAt(reflect.TypeOf(3), unsafe.Pointer(&x)).Interface()) // @types *int } // @warning "unsound: main.reflectNewAt contains a reflect.NewAt.. call" func reflectTypeOf() { t := reflect.TypeOf(3) if unknown { t = reflect.TypeOf("foo") } // TODO(adonovan): make types.Eval let us refer to unexported types. print(t) // #@types *reflect.rtype print(reflect.Zero(t).Interface()) // @types int | string newint := reflect.New(t).Interface() // @line rtonew print(newint) // @types *int | *string print(newint.(*int)) // @pointsto print(newint.(*string)) // @pointsto } func reflectTypeElem() { print(reflect.Zero(reflect.TypeOf(&a).Elem()).Interface()) // @types int print(reflect.Zero(reflect.TypeOf([]string{}).Elem()).Interface()) // @types string print(reflect.Zero(reflect.TypeOf(make(chan bool)).Elem()).Interface()) // @types bool print(reflect.Zero(reflect.TypeOf(make(map[string]float64)).Elem()).Interface()) // @types float64 print(reflect.Zero(reflect.TypeOf([3]complex64{}).Elem()).Interface()) // @types complex64 print(reflect.Zero(reflect.TypeOf(3).Elem()).Interface()) // @types print(reflect.Zero(reflect.TypeOf(new(interface{})).Elem())) // @types interface{} print(reflect.Zero(reflect.TypeOf(new(interface{})).Elem()).Interface()) // @types } // reflect.Values within reflect.Values. func metareflection() { // "box" a *int twice, unbox it twice. v0 := reflect.ValueOf(&a) print(v0) // @types *int v1 := reflect.ValueOf(v0) // box print(v1) // @types reflect.Value v2 := reflect.ValueOf(v1) // box print(v2) // @types reflect.Value v1a := v2.Interface().(reflect.Value) // unbox print(v1a) // @types reflect.Value v0a := v1a.Interface().(reflect.Value) // unbox print(v0a) // @types *int print(v0a.Interface().(*int)) // @pointsto main.a // "box" an interface{} lvalue twice, unbox it twice. var iface interface{} = 3 x0 := reflect.ValueOf(&iface).Elem() print(x0) // @types interface{} x1 := reflect.ValueOf(x0) // box print(x1) // @types reflect.Value x2 := reflect.ValueOf(x1) // box print(x2) // @types reflect.Value x1a := x2.Interface().(reflect.Value) // unbox print(x1a) // @types reflect.Value x0a := x1a.Interface().(reflect.Value) // unbox print(x0a) // @types interface{} print(x0a.Interface()) // @types int } func main() { reflectIndirect() reflectNewAt() reflectTypeOf() reflectTypeElem() metareflection() } ./pointer/testdata/hello.go0000644000014500017510000000074712246613010015426 0ustar michaelstaff// +build ignore package main import ( "fmt" "os" ) type S int var theS S func (s *S) String() string { print(s) // @pointsto main.theS return "" } func main() { // os.Args is considered instrincally allocated, // but may also be set explicitly (e.g. on Windows), hence '...'. print(os.Args) // @pointsto | ... fmt.Println("Hello, World!", &theS) } // @calls main.main -> fmt.Println // @calls (*fmt.pp).handleMethods -> (*main.S).String ./pointer/testdata/context.go0000644000014500017510000000167012246613010016003 0ustar michaelstaff// +build ignore package main // Test of context-sensitive treatment of certain function calls, // e.g. static calls to simple accessor methods. var a, b int type T struct{ x *int } func (t *T) SetX(x *int) { t.x = x } func (t *T) GetX() *int { return t.x } func context1() { var t1, t2 T t1.SetX(&a) t2.SetX(&b) print(t1.GetX()) // @pointsto main.a print(t2.GetX()) // @pointsto main.b } func context2() { id := func(x *int) *int { print(x) // @pointsto main.a | main.b return x } print(id(&a)) // @pointsto main.a print(id(&b)) // @pointsto main.b // Same again, but anon func has free vars. var c int // @line context2c id2 := func(x *int) (*int, *int) { print(x) // @pointsto main.a | main.b return x, &c } p, q := id2(&a) print(p) // @pointsto main.a print(q) // @pointsto c@context2c:6 r, s := id2(&b) print(r) // @pointsto main.b print(s) // @pointsto c@context2c:6 } func main() { context1() context2() } ./pointer/print.go0000644000014500017510000000223312246613010013636 0ustar michaelstaff// 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 pointer import "fmt" func (c *addrConstraint) String() string { return fmt.Sprintf("addr n%d <- {&n%d}", c.dst, c.src) } func (c *copyConstraint) String() string { return fmt.Sprintf("copy n%d <- n%d", c.dst, c.src) } func (c *loadConstraint) String() string { return fmt.Sprintf("load n%d <- n%d[%d]", c.dst, c.src, c.offset) } func (c *storeConstraint) String() string { return fmt.Sprintf("store n%d[%d] <- n%d", c.dst, c.offset, c.src) } func (c *offsetAddrConstraint) String() string { return fmt.Sprintf("offsetAddr n%d <- n%d.#%d", c.dst, c.src, c.offset) } func (c *typeFilterConstraint) String() string { return fmt.Sprintf("typeFilter n%d <- n%d.(%s)", c.dst, c.src, c.typ) } func (c *untagConstraint) String() string { return fmt.Sprintf("untag n%d <- n%d.(%s)", c.dst, c.src, c.typ) } func (c *invokeConstraint) String() string { return fmt.Sprintf("invoke n%d.%s(n%d ...)", c.iface, c.method.Name(), c.params+1) } func (n nodeid) String() string { return fmt.Sprintf("n%d", n) } ./pointer/reflect.go0000644000014500017510000011634412246613010014137 0ustar michaelstaffpackage 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, instrinsics should // memoize as much as possible, like TypeOf and Zero do for their // tagged objects. // // TODO(adonovan): all {} functions are TODO. // // 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. import ( "fmt" "go/ast" "reflect" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) // -------------------- (reflect.Value) -------------------- func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} // ---------- func (Value).Bytes() Value ---------- // result = v.Bytes() type rVBytesConstraint struct { v nodeid // (ptr) result nodeid } func (c *rVBytesConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v) } func (c *rVBytesConstraint) ptr() nodeid { return c.v } func (c *rVBytesConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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.IsIdentical(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 v nodeid // (ptr) arg nodeid // = in[*] result nodeid dotdotdot bool // interpret last arg as a "..." slice } 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) ptr() nodeid { return c.v } func (c *rVCallConstraint) solve(a *analysis, _ *node, delta nodeset) { if c.targets == 0 { panic("no targets") } changed := false for vObj := range delta { 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(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) {} // ---------- func (Value).Elem() Value ---------- // result = v.Elem() type rVElemConstraint struct { cgn *cgnode v nodeid // (ptr) result nodeid } func (c *rVElemConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v) } func (c *rVElemConstraint) ptr() nodeid { return c.v } func (c *rVElemConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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) {} func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode) {} func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode) {} func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // ---------- func (Value).Index() Value ---------- // result = v.Index() type rVIndexConstraint struct { cgn *cgnode v nodeid // (ptr) result nodeid } func (c *rVIndexConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v) } func (c *rVIndexConstraint) ptr() nodeid { return c.v } func (c *rVIndexConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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 } func (c *rVInterfaceConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v) } func (c *rVInterfaceConstraint) ptr() nodeid { return c.v } func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) { resultPts := &a.nodes[c.result].pts changed := false for vObj := range delta { 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 resultPts.add(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 } func (c *rVMapIndexConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v) } func (c *rVMapIndexConstraint) ptr() nodeid { return c.v } func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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 } func (c *rVMapKeysConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v) } func (c *rVMapKeysConstraint) ptr() nodeid { return c.v } func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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() a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "reflect.MapKeys result") a.endObject(obj, cgn, nil) a.addressOf(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) {} func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} // ---------- func (Value).Recv(Value) ---------- // result, _ = v.Recv() type rVRecvConstraint struct { cgn *cgnode v nodeid // (ptr) result nodeid } func (c *rVRecvConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v) } func (c *rVRecvConstraint) ptr() nodeid { return c.v } func (c *rVRecvConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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) String() string { return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x) } func (c *rVSendConstraint) ptr() nodeid { return c.v } func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) { for vObj := range delta { 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) {} // ---------- func (Value).SetBytes(x []byte) ---------- // v.SetBytes(x) type rVSetBytesConstraint struct { cgn *cgnode v nodeid // (ptr) x nodeid } func (c *rVSetBytesConstraint) String() string { return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x) } func (c *rVSetBytesConstraint) ptr() nodeid { return c.v } func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) { for vObj := range delta { 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.IsIdentical(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) String() string { return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val) } func (c *rVSetMapIndexConstraint) ptr() nodeid { return c.v } func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) { for vObj := range delta { 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) {} // ---------- func (Value).Slice(v Value, i, j int) ---------- // result = v.Slice(_, _) type rVSliceConstraint struct { cgn *cgnode v nodeid // (ptr) result nodeid } func (c *rVSliceConstraint) String() string { return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v) } func (c *rVSliceConstraint) ptr() nodeid { return c.v } func (c *rVSliceConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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) {} func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} func ext۰reflect۰Copy(a *analysis, cgn *cgnode) {} // ---------- func ChanOf(ChanDir, Type) Type ---------- // result = ChanOf(dir, t) type reflectChanOfConstraint struct { cgn *cgnode t nodeid // (ptr) result nodeid dirs []ast.ChanDir } func (c *reflectChanOfConstraint) String() string { return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t) } func (c *reflectChanOfConstraint) ptr() nodeid { return c.t } func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for tObj := range delta { T := a.rtypeTaggedValue(tObj) 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 = [...][]ast.ChanDir{ 0: {ast.RECV, ast.SEND, ast.RECV | ast.SEND}, // unknown reflect.RecvDir: {ast.RECV}, reflect.SendDir: {ast.SEND}, reflect.BothDir: {ast.RECV | ast.SEND}, } 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 } func (c *reflectIndirectConstraint) String() string { return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v) } func (c *reflectIndirectConstraint) ptr() nodeid { return c.v } func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { 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 } func (c *reflectMakeChanConstraint) String() string { return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ) } func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ } func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for typObj := range delta { T := a.rtypeTaggedValue(typObj) tChan, ok := T.Underlying().(*types.Chan) if !ok || tChan.Dir() != ast.SEND|ast.RECV { 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) {} // ---------- func MakeMap(Type) Value ---------- // result = MakeMap(typ) type reflectMakeMapConstraint struct { cgn *cgnode typ nodeid // (ptr) result nodeid } func (c *reflectMakeMapConstraint) String() string { return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ) } func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ } func (c *reflectMakeMapConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for typObj := range delta { 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 } func (c *reflectMakeSliceConstraint) String() string { return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ) } func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ } func (c *reflectMakeSliceConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for typObj := range delta { 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) {} // ---------- func New(Type) Value ---------- // result = New(typ) type reflectNewConstraint struct { cgn *cgnode typ nodeid // (ptr) result nodeid } func (c *reflectNewConstraint) String() string { return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ) } func (c *reflectNewConstraint) ptr() nodeid { return c.typ } func (c *reflectNewConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for typObj := range delta { 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 } func (c *reflectPtrToConstraint) String() string { return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t) } func (c *reflectPtrToConstraint) ptr() nodeid { return c.t } func (c *reflectPtrToConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for tObj := range delta { T := a.rtypeTaggedValue(tObj) 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) {} // ---------- func SliceOf(Type) Type ---------- // result = SliceOf(t) type reflectSliceOfConstraint struct { cgn *cgnode t nodeid // (ptr) result nodeid } func (c *reflectSliceOfConstraint) String() string { return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t) } func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t } func (c *reflectSliceOfConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for tObj := range delta { T := a.rtypeTaggedValue(tObj) 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 } func (c *reflectTypeOfConstraint) String() string { return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i) } func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i } func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for iObj := range delta { 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 } func (c *reflectZeroConstraint) String() string { return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ) } func (c *reflectZeroConstraint) ptr() nodeid { return c.typ } func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for typObj := range delta { 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 } func (c *rtypeElemConstraint) String() string { return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t) } func (c *rtypeElemConstraint) ptr() nodeid { return c.t } func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) { // Implemented by *types.{Map,Chan,Array,Slice,Pointer}. type hasElem interface { Elem() types.Type } changed := false for tObj := range delta { 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 } 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) ptr() nodeid { return c.t } func (c *rtypeFieldByNameConstraint) solve(a *analysis, _ *node, 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 tObj := range delta { 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) {} func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // ---------- func (*rtype) In/Out(i int) Type ---------- // result = In/Out(t, i) type rtypeInOutConstraint struct { cgn *cgnode t nodeid // (ptr) result nodeid out bool i int // -ve if not a constant } 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) ptr() nodeid { return c.t } func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for tObj := range delta { 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 } func (c *rtypeKeyConstraint) String() string { return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t) } func (c *rtypeKeyConstraint) ptr() nodeid { return c.t } func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for tObj := range delta { 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 } func (c *rtypeMethodByNameConstraint) String() string { return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name) } func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t } // 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, nil, types.NewTuple(p2...), sig.Results(), sig.IsVariadic()) } func (c *rtypeMethodByNameConstraint) solve(a *analysis, _ *node, delta nodeset) { for tObj := range delta { T := a.nodes[tObj].obj.data.(types.Type) // We don't use Lookup(c.name) when c.name != "" to avoid // ambiguity: >1 unexported methods could match. mset := T.MethodSet() 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 // } fn := a.prog.Method(sel) // a.offsetOf(Type) is 3. if id := c.result + 3; a.addLabel(id, a.makeRtype(changeRecv(fn.Signature))) { a.addWork(id) } // 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), }) } ./dashboard/0000755000014500017510000000000012246613010012422 5ustar michaelstaff./dashboard/app/0000755000014500017510000000000012246613010013202 5ustar michaelstaff./dashboard/app/build/0000755000014500017510000000000012246613010014301 5ustar michaelstaff./dashboard/app/build/test.go0000644000014500017510000002135512246613010015615 0ustar michaelstaff// 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 appengine package build // TODO(adg): test authentication import ( "appengine" "appengine/datastore" "bytes" "encoding/json" "errors" "fmt" "io" "net/http" "net/http/httptest" "net/url" "strings" "time" ) func init() { http.HandleFunc("/buildtest", testHandler) } var testEntityKinds = []string{ "Package", "Commit", "Result", "Log", } const testPkg = "code.google.com/p/go.test" var testPackage = &Package{Name: "Test", Kind: "subrepo", Path: testPkg} var testPackages = []*Package{ {Name: "Go", Path: ""}, testPackage, } var tCommitTime = time.Now().Add(-time.Hour * 24 * 7) func tCommit(hash, parentHash, path string) *Commit { tCommitTime.Add(time.Hour) // each commit should have a different time return &Commit{ PackagePath: path, Hash: hash, ParentHash: parentHash, Time: tCommitTime, User: "adg", Desc: "change description " + hash, } } var testRequests = []struct { path string vals url.Values req interface{} res interface{} }{ // Packages {"/packages?kind=subrepo", nil, nil, []*Package{testPackage}}, // Go repo {"/commit", nil, tCommit("0001", "0000", ""), nil}, {"/commit", nil, tCommit("0002", "0001", ""), nil}, {"/commit", nil, tCommit("0003", "0002", ""), nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, {"/result", nil, &Result{Builder: "linux-386", Hash: "0001", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, {"/result", nil, &Result{Builder: "linux-386", Hash: "0002", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, // multiple builders {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, {"/result", nil, &Result{Builder: "linux-amd64", Hash: "0003", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0002"}}}, // branches {"/commit", nil, tCommit("0004", "0003", ""), nil}, {"/commit", nil, tCommit("0005", "0002", ""), nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}}, {"/result", nil, &Result{Builder: "linux-386", Hash: "0005", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0004"}}}, {"/result", nil, &Result{Builder: "linux-386", Hash: "0004", OK: false}, nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, // logs {"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil}, {"/log/a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", nil, nil, "test"}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil}, // repeat failure (shouldn't re-send mail) {"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil}, // non-Go repos {"/commit", nil, tCommit("1001", "1000", testPkg), nil}, {"/commit", nil, tCommit("1002", "1001", testPkg), nil}, {"/commit", nil, tCommit("1003", "1002", testPkg), nil}, {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}}, {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1003", GoHash: "0001", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1002"}}}, {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1002", GoHash: "0001", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1001"}}}, {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0001", OK: true}, nil}, {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, nil}, {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0002"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}}, // re-build Go revision for stale subrepos {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}}, {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0005", OK: false, Log: "boo"}, nil}, {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil}, } func testHandler(w http.ResponseWriter, r *http.Request) { if !appengine.IsDevAppServer() { fmt.Fprint(w, "These tests must be run under the dev_appserver.") return } c := appengine.NewContext(r) if err := nukeEntities(c, testEntityKinds); err != nil { logErr(w, r, err) return } if r.FormValue("nukeonly") != "" { fmt.Fprint(w, "OK") return } for _, p := range testPackages { if _, err := datastore.Put(c, p.Key(c), p); err != nil { logErr(w, r, err) return } } for i, t := range testRequests { c.Infof("running test %d %s", i, t.path) errorf := func(format string, args ...interface{}) { fmt.Fprintf(w, "%d %s: ", i, t.path) fmt.Fprintf(w, format, args...) fmt.Fprintln(w) } var body io.ReadWriter if t.req != nil { body = new(bytes.Buffer) json.NewEncoder(body).Encode(t.req) } url := "http://" + domain + t.path if t.vals != nil { url += "?" + t.vals.Encode() } req, err := http.NewRequest("POST", url, body) if err != nil { logErr(w, r, err) return } if t.req != nil { req.Method = "POST" } req.Header = r.Header rec := httptest.NewRecorder() // Make the request http.DefaultServeMux.ServeHTTP(rec, req) if rec.Code != 0 && rec.Code != 200 { errorf(rec.Body.String()) return } resp := new(dashResponse) // If we're expecting a *Todo value, // prime the Response field with a Todo and a Commit inside it. if _, ok := t.res.(*Todo); ok { resp.Response = &Todo{Data: &Commit{}} } if strings.HasPrefix(t.path, "/log/") { resp.Response = rec.Body.String() } else { err := json.NewDecoder(rec.Body).Decode(resp) if err != nil { errorf("decoding response: %v", err) return } } if e, ok := t.res.(string); ok { g, ok := resp.Response.(string) if !ok { errorf("Response not string: %T", resp.Response) return } if g != e { errorf("response mismatch: got %q want %q", g, e) return } } if e, ok := t.res.(*Todo); ok { g, ok := resp.Response.(*Todo) if !ok { errorf("Response not *Todo: %T", resp.Response) return } if e.Data == nil && g.Data != nil { errorf("Response.Data should be nil, got: %v", g.Data) return } if g.Data == nil { errorf("Response.Data is nil, want: %v", e.Data) return } gd, ok := g.Data.(*Commit) if !ok { errorf("Response.Data not *Commit: %T", g.Data) return } if eh := e.Data.(*Commit).Hash; eh != gd.Hash { errorf("hashes don't match: got %q, want %q", gd.Hash, eh) return } } if t.res == nil && resp.Response != nil { errorf("response mismatch: got %q expected ", resp.Response) return } } fmt.Fprint(w, "PASS\nYou should see only one mail notification (for 0003/linux-386) in the dev_appserver logs.") } func nukeEntities(c appengine.Context, kinds []string) error { if !appengine.IsDevAppServer() { return errors.New("can't nuke production data") } var keys []*datastore.Key for _, kind := range kinds { q := datastore.NewQuery(kind).KeysOnly() for t := q.Run(c); ; { k, err := t.Next(nil) if err == datastore.Done { break } if err != nil { return err } keys = append(keys, k) } } return datastore.DeleteMulti(c, keys) } ./dashboard/app/build/handler.go0000644000014500017510000003055112246613010016251 0ustar michaelstaff// 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 appengine package build import ( "crypto/hmac" "crypto/md5" "encoding/json" "errors" "fmt" "net/http" "strings" "appengine" "appengine/datastore" "cache" ) const commitsPerPage = 30 // commitHandler retrieves commit data or records a new commit. // // For GET requests it returns a Commit value for the specified // packagePath and hash. // // For POST requests it reads a JSON-encoded Commit value from the request // body and creates a new Commit entity. It also updates the "tip" Tag for // each new commit at tip. // // This handler is used by a gobuilder process in -commit mode. func commitHandler(r *http.Request) (interface{}, error) { c := contextForRequest(r) com := new(Commit) if r.Method == "GET" { com.PackagePath = r.FormValue("packagePath") com.Hash = r.FormValue("hash") if err := datastore.Get(c, com.Key(c), com); err != nil { return nil, fmt.Errorf("getting Commit: %v", err) } return com, nil } if r.Method != "POST" { return nil, errBadMethod(r.Method) } // POST request defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(com); err != nil { return nil, fmt.Errorf("decoding Body: %v", err) } if len(com.Desc) > maxDatastoreStringLen { com.Desc = com.Desc[:maxDatastoreStringLen] } if err := com.Valid(); err != nil { return nil, fmt.Errorf("validating Commit: %v", err) } defer cache.Tick(c) tx := func(c appengine.Context) error { return addCommit(c, com) } return nil, datastore.RunInTransaction(c, tx, nil) } // addCommit adds the Commit entity to the datastore and updates the tip Tag. // It must be run inside a datastore transaction. func addCommit(c appengine.Context, com *Commit) error { var tc Commit // temp value so we don't clobber com err := datastore.Get(c, com.Key(c), &tc) if err != datastore.ErrNoSuchEntity { // if this commit is already in the datastore, do nothing if err == nil { return nil } return fmt.Errorf("getting Commit: %v", err) } // get the next commit number p, err := GetPackage(c, com.PackagePath) if err != nil { return fmt.Errorf("GetPackage: %v", err) } com.Num = p.NextNum p.NextNum++ if _, err := datastore.Put(c, p.Key(c), p); err != nil { return fmt.Errorf("putting Package: %v", err) } // if this isn't the first Commit test the parent commit exists if com.Num > 0 { n, err := datastore.NewQuery("Commit"). Filter("Hash =", com.ParentHash). Ancestor(p.Key(c)). Count(c) if err != nil { return fmt.Errorf("testing for parent Commit: %v", err) } if n == 0 { return errors.New("parent commit not found") } } // update the tip Tag if this is the Go repo and this isn't on a release branch if p.Path == "" && !strings.HasPrefix(com.Desc, "[release-branch") { t := &Tag{Kind: "tip", Hash: com.Hash} if _, err = datastore.Put(c, t.Key(c), t); err != nil { return fmt.Errorf("putting Tag: %v", err) } } // put the Commit if _, err = datastore.Put(c, com.Key(c), com); err != nil { return fmt.Errorf("putting Commit: %v", err) } return nil } // tagHandler records a new tag. It reads a JSON-encoded Tag value from the // request body and updates the Tag entity for the Kind of tag provided. // // This handler is used by a gobuilder process in -commit mode. func tagHandler(r *http.Request) (interface{}, error) { if r.Method != "POST" { return nil, errBadMethod(r.Method) } t := new(Tag) defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(t); err != nil { return nil, err } if err := t.Valid(); err != nil { return nil, err } c := contextForRequest(r) defer cache.Tick(c) _, err := datastore.Put(c, t.Key(c), t) return nil, err } // Todo is a todoHandler response. type Todo struct { Kind string // "build-go-commit" or "build-package" Data interface{} } // todoHandler returns the next action to be performed by a builder. // It expects "builder" and "kind" query parameters and returns a *Todo value. // Multiple "kind" parameters may be specified. func todoHandler(r *http.Request) (interface{}, error) { c := contextForRequest(r) now := cache.Now(c) key := "build-todo-" + r.Form.Encode() var todo *Todo if cache.Get(r, now, key, &todo) { return todo, nil } var err error builder := r.FormValue("builder") for _, kind := range r.Form["kind"] { var com *Commit switch kind { case "build-go-commit": com, err = buildTodo(c, builder, "", "") case "build-package": packagePath := r.FormValue("packagePath") goHash := r.FormValue("goHash") com, err = buildTodo(c, builder, packagePath, goHash) } if com != nil || err != nil { if com != nil { // ResultData can be large and not needed on builder. com.ResultData = []string{} } todo = &Todo{Kind: kind, Data: com} break } } if err == nil { cache.Set(r, now, key, todo) } return todo, err } // buildTodo returns the next Commit to be built (or nil if none available). // // If packagePath and goHash are empty, it scans the first 20 Go Commits in // Num-descending order and returns the first one it finds that doesn't have a // Result for this builder. // // If provided with non-empty packagePath and goHash args, it scans the first // 20 Commits in Num-descending order for the specified packagePath and // returns the first that doesn't have a Result for this builder and goHash. func buildTodo(c appengine.Context, builder, packagePath, goHash string) (*Commit, error) { p, err := GetPackage(c, packagePath) if err != nil { return nil, err } t := datastore.NewQuery("Commit"). Ancestor(p.Key(c)). Limit(commitsPerPage). Order("-Num"). Run(c) for { com := new(Commit) if _, err := t.Next(com); err == datastore.Done { break } else if err != nil { return nil, err } if com.Result(builder, goHash) == nil { return com, nil } } // Nothing left to do if this is a package (not the Go tree). if packagePath != "" { return nil, nil } // If there are no Go tree commits left to build, // see if there are any subrepo commits that need to be built at tip. // If so, ask the builder to build a go tree at the tip commit. // TODO(adg): do the same for "weekly" and "release" tags. tag, err := GetTag(c, "tip") if err != nil { return nil, err } // Check that this Go commit builds OK for this builder. // If not, don't re-build as the subrepos will never get built anyway. com, err := tag.Commit(c) if err != nil { return nil, err } if r := com.Result(builder, ""); r != nil && !r.OK { return nil, nil } pkgs, err := Packages(c, "subrepo") if err != nil { return nil, err } for _, pkg := range pkgs { com, err := pkg.LastCommit(c) if err != nil { c.Warningf("%v: no Commit found: %v", pkg, err) continue } if com.Result(builder, tag.Hash) == nil { return tag.Commit(c) } } return nil, nil } // packagesHandler returns a list of the non-Go Packages monitored // by the dashboard. func packagesHandler(r *http.Request) (interface{}, error) { kind := r.FormValue("kind") c := contextForRequest(r) now := cache.Now(c) key := "build-packages-" + kind var p []*Package if cache.Get(r, now, key, &p) { return p, nil } p, err := Packages(c, kind) if err != nil { return nil, err } cache.Set(r, now, key, p) return p, nil } // resultHandler records a build result. // It reads a JSON-encoded Result value from the request body, // creates a new Result entity, and updates the relevant Commit entity. // If the Log field is not empty, resultHandler creates a new Log entity // and updates the LogHash field before putting the Commit entity. func resultHandler(r *http.Request) (interface{}, error) { if r.Method != "POST" { return nil, errBadMethod(r.Method) } c := contextForRequest(r) res := new(Result) defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(res); err != nil { return nil, fmt.Errorf("decoding Body: %v", err) } if err := res.Valid(); err != nil { return nil, fmt.Errorf("validating Result: %v", err) } defer cache.Tick(c) // store the Log text if supplied if len(res.Log) > 0 { hash, err := PutLog(c, res.Log) if err != nil { return nil, fmt.Errorf("putting Log: %v", err) } res.LogHash = hash } tx := func(c appengine.Context) error { // check Package exists if _, err := GetPackage(c, res.PackagePath); err != nil { return fmt.Errorf("GetPackage: %v", err) } // put Result if _, err := datastore.Put(c, res.Key(c), res); err != nil { return fmt.Errorf("putting Result: %v", err) } // add Result to Commit com := &Commit{PackagePath: res.PackagePath, Hash: res.Hash} if err := com.AddResult(c, res); err != nil { return fmt.Errorf("AddResult: %v", err) } // Send build failure notifications, if necessary. // Note this must run after the call AddResult, which // populates the Commit's ResultData field. return notifyOnFailure(c, com, res.Builder) } return nil, datastore.RunInTransaction(c, tx, nil) } // logHandler displays log text for a given hash. // It handles paths like "/log/hash". func logHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-type", "text/plain; charset=utf-8") c := contextForRequest(r) hash := r.URL.Path[len("/log/"):] key := datastore.NewKey(c, "Log", hash, 0, nil) l := new(Log) if err := datastore.Get(c, key, l); err != nil { logErr(w, r, err) return } b, err := l.Text() if err != nil { logErr(w, r, err) return } w.Write(b) } type dashHandler func(*http.Request) (interface{}, error) type dashResponse struct { Response interface{} Error string } // errBadMethod is returned by a dashHandler when // the request has an unsuitable method. type errBadMethod string func (e errBadMethod) Error() string { return "bad method: " + string(e) } // AuthHandler wraps a http.HandlerFunc with a handler that validates the // supplied key and builder query parameters. func AuthHandler(h dashHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { c := contextForRequest(r) // Put the URL Query values into r.Form to avoid parsing the // request body when calling r.FormValue. r.Form = r.URL.Query() var err error var resp interface{} // Validate key query parameter for POST requests only. key := r.FormValue("key") builder := r.FormValue("builder") if r.Method == "POST" && !validKey(c, key, builder) { err = errors.New("invalid key: " + key) } // Call the original HandlerFunc and return the response. if err == nil { resp, err = h(r) } // Write JSON response. dashResp := &dashResponse{Response: resp} if err != nil { c.Errorf("%v", err) dashResp.Error = err.Error() } w.Header().Set("Content-Type", "application/json") if err = json.NewEncoder(w).Encode(dashResp); err != nil { c.Criticalf("encoding response: %v", err) } } } func keyHandler(w http.ResponseWriter, r *http.Request) { builder := r.FormValue("builder") if builder == "" { logErr(w, r, errors.New("must supply builder in query string")) return } c := contextForRequest(r) fmt.Fprint(w, builderKey(c, builder)) } func init() { for _, d := range dashboards { // admin handlers http.HandleFunc(d.RelPath+"init", initHandler) http.HandleFunc(d.RelPath+"key", keyHandler) // authenticated handlers http.HandleFunc(d.RelPath+"commit", AuthHandler(commitHandler)) http.HandleFunc(d.RelPath+"packages", AuthHandler(packagesHandler)) http.HandleFunc(d.RelPath+"result", AuthHandler(resultHandler)) http.HandleFunc(d.RelPath+"tag", AuthHandler(tagHandler)) http.HandleFunc(d.RelPath+"todo", AuthHandler(todoHandler)) // public handlers http.HandleFunc(d.RelPath+"log/", logHandler) } } func validHash(hash string) bool { // TODO(adg): correctly validate a hash return hash != "" } func validKey(c appengine.Context, key, builder string) bool { if appengine.IsDevAppServer() { return true } if key == secretKey(c) { return true } return key == builderKey(c, builder) } func builderKey(c appengine.Context, builder string) string { h := hmac.New(md5.New, []byte(secretKey(c))) h.Write([]byte(builder)) return fmt.Sprintf("%x", h.Sum(nil)) } func logErr(w http.ResponseWriter, r *http.Request, err error) { contextForRequest(r).Errorf("Error: %v", err) w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w, "Error: ", err) } func contextForRequest(r *http.Request) appengine.Context { return dashboardForRequest(r).Context(appengine.NewContext(r)) } ./dashboard/app/build/init.go0000644000014500017510000000156012246613010015575 0ustar michaelstaff// Copyright 2012 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 build import ( "fmt" "net/http" "appengine" "appengine/datastore" "cache" ) func initHandler(w http.ResponseWriter, r *http.Request) { d := dashboardForRequest(r) c := d.Context(appengine.NewContext(r)) defer cache.Tick(c) for _, p := range d.Packages { err := datastore.Get(c, p.Key(c), new(Package)) if _, ok := err.(*datastore.ErrFieldMismatch); ok { // Some fields have been removed, so it's okay to ignore this error. err = nil } if err == nil { continue } else if err != datastore.ErrNoSuchEntity { logErr(w, r, err) return } if _, err := datastore.Put(c, p.Key(c), p); err != nil { logErr(w, r, err) return } } fmt.Fprint(w, "OK") } ./dashboard/app/build/ui.go0000644000014500017510000001634312246613010015254 0ustar michaelstaff// 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. // TODO(adg): packages at weekly/release // TODO(adg): some means to register new packages // +build appengine package build import ( "bytes" "errors" "html/template" "net/http" "regexp" "sort" "strconv" "strings" "appengine" "appengine/datastore" "cache" ) func init() { for _, d := range dashboards { http.HandleFunc(d.RelPath, uiHandler) } } // uiHandler draws the build status page. func uiHandler(w http.ResponseWriter, r *http.Request) { d := dashboardForRequest(r) c := d.Context(appengine.NewContext(r)) now := cache.Now(c) const key = "build-ui" page, _ := strconv.Atoi(r.FormValue("page")) if page < 0 { page = 0 } // Used cached version of front page, if available. if page == 0 { var b []byte if cache.Get(r, now, key, &b) { w.Write(b) return } } commits, err := dashCommits(c, page) if err != nil { logErr(w, r, err) return } builders := commitBuilders(commits, "") var tipState *TagState if page == 0 { // only show sub-repo state on first page tipState, err = TagStateByName(c, "tip") if err != nil { logErr(w, r, err) return } } p := &Pagination{} if len(commits) == commitsPerPage { p.Next = page + 1 } if page > 0 { p.Prev = page - 1 p.HasPrev = true } data := &uiTemplateData{d, commits, builders, tipState, p} var buf bytes.Buffer if err := uiTemplate.Execute(&buf, data); err != nil { logErr(w, r, err) return } // Cache the front page. if page == 0 { cache.Set(r, now, key, buf.Bytes()) } buf.WriteTo(w) } type Pagination struct { Next, Prev int HasPrev bool } // dashCommits gets a slice of the latest Commits to the current dashboard. // If page > 0 it paginates by commitsPerPage. func dashCommits(c appengine.Context, page int) ([]*Commit, error) { q := datastore.NewQuery("Commit"). Ancestor((&Package{}).Key(c)). Order("-Num"). Limit(commitsPerPage). Offset(page * commitsPerPage) var commits []*Commit _, err := q.GetAll(c, &commits) return commits, err } // commitBuilders returns the names of the builders that provided // Results for the provided commits. func commitBuilders(commits []*Commit, goHash string) []string { builders := make(map[string]bool) for _, commit := range commits { for _, r := range commit.Results(goHash) { builders[r.Builder] = true } } return keys(builders) } func keys(m map[string]bool) (s []string) { for k := range m { s = append(s, k) } sort.Strings(s) return } // TagState represents the state of all Packages at a Tag. type TagState struct { Tag *Commit Packages []*PackageState } // PackageState represents the state of a Package at a Tag. type PackageState struct { Package *Package Commit *Commit } // TagStateByName fetches the results for all Go subrepos at the specified Tag. func TagStateByName(c appengine.Context, name string) (*TagState, error) { tag, err := GetTag(c, name) if err != nil { return nil, err } pkgs, err := Packages(c, "subrepo") if err != nil { return nil, err } var st TagState for _, pkg := range pkgs { com, err := pkg.LastCommit(c) if err != nil { c.Warningf("%v: no Commit found: %v", pkg, err) continue } st.Packages = append(st.Packages, &PackageState{pkg, com}) } st.Tag, err = tag.Commit(c) if err != nil { return nil, err } return &st, nil } type uiTemplateData struct { Dashboard *Dashboard Commits []*Commit Builders []string TipState *TagState Pagination *Pagination } var uiTemplate = template.Must( template.New("ui.html").Funcs(tmplFuncs).ParseFiles("build/ui.html"), ) var tmplFuncs = template.FuncMap{ "builderOS": builderOS, "builderArch": builderArch, "builderArchShort": builderArchShort, "builderArchChar": builderArchChar, "builderTitle": builderTitle, "builderSpans": builderSpans, "buildDashboards": buildDashboards, "repoURL": repoURL, "shortDesc": shortDesc, "shortHash": shortHash, "shortUser": shortUser, "tail": tail, } func splitDash(s string) (string, string) { i := strings.Index(s, "-") if i >= 0 { return s[:i], s[i+1:] } return s, "" } // builderOS returns the os tag for a builder string func builderOS(s string) string { os, _ := splitDash(s) return os } // builderArch returns the arch tag for a builder string func builderArch(s string) string { _, arch := splitDash(s) arch, _ = splitDash(arch) // chop third part return arch } // builderArchShort returns a short arch tag for a builder string func builderArchShort(s string) string { if strings.Contains(s+"-", "-race-") { return "race" } arch := builderArch(s) switch arch { case "amd64": return "x64" } return arch } // builderArchChar returns the architecture letter for a builder string func builderArchChar(s string) string { arch := builderArch(s) switch arch { case "386": return "8" case "amd64": return "6" case "arm": return "5" } return arch } type builderSpan struct { N int OS string } // builderSpans creates a list of tags showing // the builder's operating system names, spanning // the appropriate number of columns. func builderSpans(s []string) []builderSpan { var sp []builderSpan for len(s) > 0 { i := 1 os := builderOS(s[0]) for i < len(s) && builderOS(s[i]) == os { i++ } sp = append(sp, builderSpan{i, os}) s = s[i:] } return sp } // builderTitle formats "linux-amd64-foo" as "linux amd64 foo". func builderTitle(s string) string { return strings.Replace(s, "-", " ", -1) } // buildDashboards returns the known public dashboards. func buildDashboards() []*Dashboard { return dashboards } // shortDesc returns the first line of a description. func shortDesc(desc string) string { if i := strings.Index(desc, "\n"); i != -1 { desc = desc[:i] } return desc } // shortHash returns a short version of a hash. func shortHash(hash string) string { if len(hash) > 12 { hash = hash[:12] } return hash } // shortUser returns a shortened version of a user string. func shortUser(user string) string { if i, j := strings.Index(user, "<"), strings.Index(user, ">"); 0 <= i && i < j { user = user[i+1 : j] } if i := strings.Index(user, "@"); i >= 0 { return user[:i] } return user } // repoRe matches Google Code repositories and subrepositories (without paths). var repoRe = regexp.MustCompile(`^code\.google\.com/p/([a-z0-9\-]+)(\.[a-z0-9\-]+)?$`) // repoURL returns the URL of a change at a Google Code repository or subrepo. func repoURL(dashboard, hash, packagePath string) (string, error) { if packagePath == "" { if dashboard == "Gccgo" { return "https://code.google.com/p/gofrontend/source/detail?r=" + hash, nil } return "https://code.google.com/p/go/source/detail?r=" + hash, nil } m := repoRe.FindStringSubmatch(packagePath) if m == nil { return "", errors.New("unrecognized package: " + packagePath) } url := "https://code.google.com/p/" + m[1] + "/source/detail?r=" + hash if len(m) > 2 { url += "&repo=" + m[2][1:] } return url, nil } // tail returns the trailing n lines of s. func tail(n int, s string) string { lines := strings.Split(s, "\n") if len(lines) < n { return s } return strings.Join(lines[len(lines)-n:], "\n") } ./dashboard/app/build/key.go0000644000014500017510000000260512246613010015423 0ustar michaelstaff// 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 appengine package build import ( "sync" "appengine" "appengine/datastore" ) var theKey struct { sync.RWMutex BuilderKey } type BuilderKey struct { Secret string } func (k *BuilderKey) Key(c appengine.Context) *datastore.Key { return datastore.NewKey(c, "BuilderKey", "root", 0, nil) } func secretKey(c appengine.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 } ./dashboard/app/build/notify.txt0000644000014500017510000000037712246613010016361 0ustar michaelstaffChange {{shortHash .Commit.Hash}} broke the {{.Builder}} build: http://{{.Hostname}}/log/{{.Result.LogHash}} {{.Commit.Desc}} http://code.google.com/p/go/source/detail?r={{shortHash .Commit.Hash}} $ tail -200 < log {{printf "%s" .Log.Text | tail 200}} ./dashboard/app/build/build.go0000644000014500017510000002136512246613010015736 0ustar michaelstaff// 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 appengine package build import ( "bytes" "compress/gzip" "crypto/sha1" "errors" "fmt" "io" "io/ioutil" "strings" "time" "appengine" "appengine/datastore" ) const maxDatastoreStringLen = 500 // A Package describes a package that is listed on the dashboard. type Package struct { Kind string // "subrepo", "external", or empty for the main Go tree Name string Path string // (empty for the main Go tree) NextNum int // Num of the next head Commit } func (p *Package) String() string { return fmt.Sprintf("%s: %q", p.Path, p.Name) } func (p *Package) Key(c appengine.Context) *datastore.Key { key := p.Path if key == "" { key = "go" } return datastore.NewKey(c, "Package", key, 0, nil) } // LastCommit returns the most recent Commit for this Package. func (p *Package) LastCommit(c appengine.Context) (*Commit, error) { var commits []*Commit _, err := datastore.NewQuery("Commit"). Ancestor(p.Key(c)). Order("-Time"). Limit(1). GetAll(c, &commits) if _, ok := err.(*datastore.ErrFieldMismatch); ok { // Some fields have been removed, so it's okay to ignore this error. err = nil } if err != nil { return nil, err } if len(commits) != 1 { return nil, datastore.ErrNoSuchEntity } return commits[0], nil } // GetPackage fetches a Package by path from the datastore. func GetPackage(c appengine.Context, path string) (*Package, error) { p := &Package{Path: path} err := datastore.Get(c, p.Key(c), p) if err == datastore.ErrNoSuchEntity { return nil, fmt.Errorf("package %q not found", path) } if _, ok := err.(*datastore.ErrFieldMismatch); ok { // Some fields have been removed, so it's okay to ignore this error. err = nil } return p, err } // A Commit describes an individual commit in a package. // // Each Commit entity is a descendant of its associated Package entity. // In other words, all Commits with the same PackagePath belong to the same // datastore entity group. type Commit struct { PackagePath string // (empty for main repo commits) Hash string ParentHash string Num int // Internal monotonic counter unique to this package. User string Desc string `datastore:",noindex"` Time time.Time // ResultData is the Data string of each build Result for this Commit. // For non-Go commits, only the Results for the current Go tip, weekly, // and release Tags are stored here. This is purely de-normalized data. // The complete data set is stored in Result entities. ResultData []string `datastore:",noindex"` FailNotificationSent bool } func (com *Commit) Key(c appengine.Context) *datastore.Key { if com.Hash == "" { panic("tried Key on Commit with empty Hash") } p := Package{Path: com.PackagePath} key := com.PackagePath + "|" + com.Hash return datastore.NewKey(c, "Commit", key, 0, p.Key(c)) } func (c *Commit) Valid() error { if !validHash(c.Hash) { return errors.New("invalid Hash") } if c.ParentHash != "" && !validHash(c.ParentHash) { // empty is OK return errors.New("invalid ParentHash") } return nil } // each result line is approx 105 bytes. This constant is a tradeoff between // build history and the AppEngine datastore limit of 1mb. const maxResults = 1000 // AddResult adds the denormalized Result data to the Commit's Result field. // It must be called from inside a datastore transaction. func (com *Commit) AddResult(c appengine.Context, r *Result) error { if err := datastore.Get(c, com.Key(c), com); err != nil { return fmt.Errorf("getting Commit: %v", err) } com.ResultData = trim(append(com.ResultData, r.Data()), maxResults) if _, err := datastore.Put(c, com.Key(c), com); err != nil { return fmt.Errorf("putting Commit: %v", err) } return nil } func trim(s []string, n int) []string { l := min(len(s), n) return s[len(s)-l:] } func min(a, b int) int { if a < b { return a } return b } // Result returns the build Result for this Commit for the given builder/goHash. func (c *Commit) Result(builder, goHash string) *Result { for _, r := range c.ResultData { p := strings.SplitN(r, "|", 4) if len(p) != 4 || p[0] != builder || p[3] != goHash { continue } return partsToHash(c, p) } return nil } // Results returns the build Results for this Commit for the given goHash. func (c *Commit) Results(goHash string) (results []*Result) { for _, r := range c.ResultData { p := strings.SplitN(r, "|", 4) if len(p) != 4 || p[3] != goHash { continue } results = append(results, partsToHash(c, p)) } return } // partsToHash converts a Commit and ResultData substrings to a Result. func partsToHash(c *Commit, p []string) *Result { return &Result{ Builder: p[0], Hash: c.Hash, PackagePath: c.PackagePath, GoHash: p[3], OK: p[1] == "true", LogHash: p[2], } } // A Result describes a build result for a Commit on an OS/architecture. // // Each Result entity is a descendant of its associated Package entity. type Result struct { Builder string // "os-arch[-note]" Hash string PackagePath string // (empty for Go commits) // The Go Commit this was built against (empty for Go commits). GoHash string OK bool Log string `datastore:"-"` // for JSON unmarshaling only LogHash string `datastore:",noindex"` // Key to the Log record. RunTime int64 // time to build+test in nanoseconds } func (r *Result) Key(c appengine.Context) *datastore.Key { p := Package{Path: r.PackagePath} key := r.Builder + "|" + r.PackagePath + "|" + r.Hash + "|" + r.GoHash return datastore.NewKey(c, "Result", key, 0, p.Key(c)) } func (r *Result) Valid() error { if !validHash(r.Hash) { return errors.New("invalid Hash") } if r.PackagePath != "" && !validHash(r.GoHash) { return errors.New("invalid GoHash") } return nil } // Data returns the Result in string format // to be stored in Commit's ResultData field. func (r *Result) Data() string { return fmt.Sprintf("%v|%v|%v|%v", r.Builder, r.OK, r.LogHash, r.GoHash) } // A Log is a gzip-compressed log file stored under the SHA1 hash of the // uncompressed log text. type Log struct { CompressedLog []byte } func (l *Log) Text() ([]byte, error) { d, err := gzip.NewReader(bytes.NewBuffer(l.CompressedLog)) if err != nil { return nil, fmt.Errorf("reading log data: %v", err) } b, err := ioutil.ReadAll(d) if err != nil { return nil, fmt.Errorf("reading log data: %v", err) } return b, nil } func PutLog(c appengine.Context, text string) (hash string, err error) { h := sha1.New() io.WriteString(h, text) b := new(bytes.Buffer) z, _ := gzip.NewWriterLevel(b, gzip.BestCompression) io.WriteString(z, text) z.Close() hash = fmt.Sprintf("%x", h.Sum(nil)) key := datastore.NewKey(c, "Log", hash, 0, nil) _, err = datastore.Put(c, key, &Log{b.Bytes()}) return } // A Tag is used to keep track of the most recent Go weekly and release tags. // Typically there will be one Tag entity for each kind of hg tag. type Tag struct { Kind string // "weekly", "release", or "tip" Name string // the tag itself (for example: "release.r60") Hash string } func (t *Tag) Key(c appengine.Context) *datastore.Key { p := &Package{} return datastore.NewKey(c, "Tag", t.Kind, 0, p.Key(c)) } func (t *Tag) Valid() error { if t.Kind != "weekly" && t.Kind != "release" && t.Kind != "tip" { return errors.New("invalid Kind") } if !validHash(t.Hash) { return errors.New("invalid Hash") } return nil } // Commit returns the Commit that corresponds with this Tag. func (t *Tag) Commit(c appengine.Context) (*Commit, error) { com := &Commit{Hash: t.Hash} err := datastore.Get(c, com.Key(c), com) return com, err } // GetTag fetches a Tag by name from the datastore. func GetTag(c appengine.Context, tag string) (*Tag, error) { t := &Tag{Kind: tag} if err := datastore.Get(c, t.Key(c), t); err != nil { if err == datastore.ErrNoSuchEntity { return nil, errors.New("tag not found: " + tag) } return nil, err } if err := t.Valid(); err != nil { return nil, err } return t, nil } // Packages returns packages of the specified kind. // Kind must be one of "external" or "subrepo". func Packages(c appengine.Context, kind string) ([]*Package, error) { switch kind { case "external", "subrepo": default: return nil, errors.New(`kind must be one of "external" or "subrepo"`) } var pkgs []*Package q := datastore.NewQuery("Package").Filter("Kind=", kind) for t := q.Run(c); ; { pkg := new(Package) _, err := t.Next(pkg) if _, ok := err.(*datastore.ErrFieldMismatch); ok { // Some fields have been removed, so it's okay to ignore this error. err = nil } if err == datastore.Done { break } else if err != nil { return nil, err } if pkg.Path != "" { pkgs = append(pkgs, pkg) } } return pkgs, nil } ./dashboard/app/build/notify.go0000644000014500017510000001103212246613010016135 0ustar michaelstaff// 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 appengine package build import ( "appengine" "appengine/datastore" "appengine/delay" "appengine/mail" "bytes" "encoding/gob" "fmt" "text/template" ) const ( mailFrom = "builder@golang.org" // use this for sending any mail failMailTo = "golang-dev@googlegroups.com" domain = "build.golang.org" ) // failIgnore is a set of builders that we don't email about because // they're too flaky. var failIgnore = map[string]bool{ "netbsd-386-bsiegert": true, "netbsd-amd64-bsiegert": true, } // notifyOnFailure checks whether the supplied Commit or the subsequent // Commit (if present) breaks the build for this builder. // If either of those commits break the build an email notification is sent // from a delayed task. (We use a task because this way the mail won't be // sent if the enclosing datastore transaction fails.) // // This must be run in a datastore transaction, and the provided *Commit must // have been retrieved from the datastore within that transaction. func notifyOnFailure(c appengine.Context, com *Commit, builder string) error { if failIgnore[builder] { return nil } // TODO(adg): implement notifications for packages if com.PackagePath != "" { return nil } p := &Package{Path: com.PackagePath} var broken *Commit cr := com.Result(builder, "") if cr == nil { return fmt.Errorf("no result for %s/%s", com.Hash, builder) } q := datastore.NewQuery("Commit").Ancestor(p.Key(c)) if cr.OK { // This commit is OK. Notify if next Commit is broken. next := new(Commit) q = q.Filter("ParentHash=", com.Hash) if err := firstMatch(c, q, next); err != nil { if err == datastore.ErrNoSuchEntity { // OK at tip, no notification necessary. return nil } return err } if nr := next.Result(builder, ""); nr != nil && !nr.OK { c.Debugf("commit ok: %#v\nresult: %#v", com, cr) c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr) broken = next } } else { // This commit is broken. Notify if the previous Commit is OK. prev := new(Commit) q = q.Filter("Hash=", com.ParentHash) if err := firstMatch(c, q, prev); err != nil { if err == datastore.ErrNoSuchEntity { // No previous result, let the backfill of // this result trigger the notification. return nil } return err } if pr := prev.Result(builder, ""); pr != nil && pr.OK { c.Debugf("commit broken: %#v\nresult: %#v", com, cr) c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr) broken = com } } var err error if broken != nil && !broken.FailNotificationSent { c.Infof("%s is broken commit; notifying", broken.Hash) sendFailMailLater.Call(c, broken, builder) // add task to queue broken.FailNotificationSent = true _, err = datastore.Put(c, broken.Key(c), broken) } return err } // firstMatch executes the query q and loads the first entity into v. func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error { t := q.Limit(1).Run(c) _, err := t.Next(v) if err == datastore.Done { err = datastore.ErrNoSuchEntity } return err } var ( sendFailMailLater = delay.Func("sendFailMail", sendFailMail) sendFailMailTmpl = template.Must( template.New("notify.txt"). Funcs(template.FuncMap(tmplFuncs)). ParseFiles("build/notify.txt"), ) ) func init() { gob.Register(&Commit{}) // for delay } // sendFailMail sends a mail notification that the build failed on the // provided commit and builder. func sendFailMail(c appengine.Context, com *Commit, builder string) { // TODO(adg): handle packages // get Result r := com.Result(builder, "") if r == nil { c.Errorf("finding result for %q: %+v", builder, com) return } // get Log k := datastore.NewKey(c, "Log", r.LogHash, 0, nil) l := new(Log) if err := datastore.Get(c, k, l); err != nil { c.Errorf("finding Log record %v: %v", r.LogHash, err) return } // prepare mail message var body bytes.Buffer err := sendFailMailTmpl.Execute(&body, map[string]interface{}{ "Builder": builder, "Commit": com, "Result": r, "Log": l, "Hostname": domain, }) if err != nil { c.Errorf("rendering mail template: %v", err) return } subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc)) msg := &mail.Message{ Sender: mailFrom, To: []string{failMailTo}, ReplyTo: failMailTo, Subject: subject, Body: body.String(), } // send mail if err := mail.Send(c, msg); err != nil { c.Errorf("sending mail: %v", err) } } ./dashboard/app/build/ui.html0000644000014500017510000001327312246613010015612 0ustar michaelstaff {{$.Dashboard.Name}} Build Dashboard

{{$.Dashboard.Name}} Build Status

{{if $.Commits}}
{{range $.Builders | builderSpans}} {{end}} {{range $.Builders | builderSpans}} {{end}} {{range $.Builders}} {{end}} {{range $c := $.Commits}} {{range $.Builders}} {{end}} {{end}}
 {{.OS}}
 {{builderArchShort .}}
{{shortHash .Hash}} {{with $c.Result . ""}} {{if .OK}} ok {{else}} fail {{end}} {{else}}   {{end}} {{shortUser .User}} {{.Time.Format "Mon 02 Jan 15:04"}} {{shortDesc .Desc}}
{{with $.Pagination}} {{end}} {{else}}

No commits to display. Hm.

{{end}} {{with $.TipState}} {{$goHash := .Tag.Hash}} {{if .Packages}}

Sub-repositories at tip ({{shortHash .Tag.Hash}})

{{range $.Builders | builderSpans}} {{end}} {{range $.Builders | builderSpans}} {{end}} {{range $.Builders}} {{end}} {{range $pkg := .Packages}} {{range $.Builders}} {{end}} {{with $pkg.Commit}} {{end}} {{end}}
{{.OS}}
{{builderArchShort .}}
{{.Package.Name}} {{$h := $pkg.Commit.Hash}} {{shortHash $h}} {{with $pkg.Commit.Result . $goHash}} {{if .OK}} ok {{else}} fail {{end}} {{else}}   {{end}} {{shortUser .User}} {{.Time.Format "Mon 02 Jan 15:04"}} {{shortDesc .Desc}}
{{end}} {{end}} ./dashboard/app/build/dash.go0000644000014500017510000000437112246613010015554 0ustar michaelstaff// 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 appengine package build import ( "net/http" "strings" "appengine" ) // Dashboard describes a unique build dashboard. type Dashboard struct { Name string // This dashboard's name and namespace RelPath string // The relative url path Packages []*Package // The project's packages to build } // dashboardForRequest returns the appropriate dashboard for a given URL path. func dashboardForRequest(r *http.Request) *Dashboard { if strings.HasPrefix(r.URL.Path, gccgoDash.RelPath) { return gccgoDash } return goDash } // Context returns a namespaced context for this dashboard, or panics if it // fails to create a new context. func (d *Dashboard) Context(c appengine.Context) appengine.Context { // No namespace needed for the original Go dashboard. if d.Name == "Go" { return c } n, err := appengine.Namespace(c, d.Name) if err != nil { panic(err) } return n } // the currently known dashboards. var dashboards = []*Dashboard{goDash, gccgoDash} // goDash is the dashboard for the main go repository. var goDash = &Dashboard{ Name: "Go", RelPath: "/", Packages: goPackages, } // goPackages is a list of all of the packages built by the main go repository. var goPackages = []*Package{ { Kind: "go", Name: "Go", }, { Kind: "subrepo", Name: "go.blog", Path: "code.google.com/p/go.blog", }, { Kind: "subrepo", Name: "go.codereview", Path: "code.google.com/p/go.codereview", }, { Kind: "subrepo", Name: "go.crypto", Path: "code.google.com/p/go.crypto", }, { Kind: "subrepo", Name: "go.exp", Path: "code.google.com/p/go.exp", }, { Kind: "subrepo", Name: "go.image", Path: "code.google.com/p/go.image", }, { Kind: "subrepo", Name: "go.net", Path: "code.google.com/p/go.net", }, { Kind: "subrepo", Name: "go.talks", Path: "code.google.com/p/go.talks", }, { Kind: "subrepo", Name: "go.tools", Path: "code.google.com/p/go.tools", }, } // gccgoDash is the dashboard for gccgo. var gccgoDash = &Dashboard{ Name: "Gccgo", RelPath: "/gccgo/", Packages: []*Package{ { Kind: "gccgo", Name: "Gccgo", }, }, } ./dashboard/app/static/0000755000014500017510000000000012246613010014471 5ustar michaelstaff./dashboard/app/static/status_good.gif0000644000014500017510000000051012246613010017507 0ustar michaelstaffGIF89a9UZ]3=:kS|bgiO>z|dMfP:j6??O@>z4.:6b0;8߇S`M=H@bK4>;ya`O~A8*䑰~wZ;M=6e3:AD-47!9,epH,g8osn8oFe |3Ll ˱ E܊Ad )7F7"$1'N &V9-(%6*x98EA;./dashboard/app/static/status_alert.gif0000644000014500017510000000107212246613010017672 0ustar michaelstaffGIF89aM{>V`,,?9%UV(]qyAC34-DF%' )& ʥΨ/!;˧. sl%ڲ! =$ Ȟ ri'5'I!/84:+;+9D*!M,MM JJ K,K *6G %>) CLL"/. ;  3I27 @!8&-:5E"+string(data)+""), &logStruct) if err != nil { return fmt.Errorf("unmarshal %s log: %v", r.Master.VCS, err) } return nil }) if err != nil { return nil, err } return logStruct.Log, nil } // FullHash returns the full hash for the given Mercurial revision. func (r *Repo) FullHash(rev string) (string, error) { r.Lock() defer r.Unlock() var hash string err := timeout(*cmdTimeout, func() error { data, err := r.Master.VCS.LogAtRev(r.Path, rev, "{node}") if err != nil { return err } s := strings.TrimSpace(string(data)) if s == "" { return fmt.Errorf("cannot find revision") } if len(s) != 40 { return fmt.Errorf("%s returned invalid hash: %s", r.Master.VCS, s) } hash = s return nil }) if err != nil { return "", err } return hash, nil } // HgLog represents a single Mercurial revision. type HgLog struct { Hash string Author string Date string Desc string Parent string // Internal metadata added bool } // xmlLogTemplate is a template to pass to Mercurial to make // hg log print the log in valid XML for parsing with xml.Unmarshal. const xmlLogTemplate = ` {node|escape} {parent|escape} {author|escape} {date|rfc3339date} {desc|escape} ` ./dashboard/builder/http.go0000644000014500017510000000771312246613010015366 0ustar michaelstaff// 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. package main import ( "bytes" "encoding/json" "errors" "fmt" "io" "log" "net/http" "net/url" "time" ) type obj map[string]interface{} // dash runs the given method and command on the dashboard. // If args is non-nil it is encoded as the URL query string. // If req is non-nil it is JSON-encoded and passed as the body of the HTTP POST. // If resp is non-nil the server's response is decoded into the value pointed // to by resp (resp must be a pointer). func dash(meth, cmd string, args url.Values, req, resp interface{}) error { var r *http.Response var err error if *verbose { log.Println("dash <-", meth, cmd, args, req) } cmd = "http://" + *dashboard + "/" + cmd if len(args) > 0 { cmd += "?" + args.Encode() } switch meth { case "GET": if req != nil { log.Panicf("%s to %s with req", meth, cmd) } r, err = http.Get(cmd) case "POST": var body io.Reader if req != nil { b, err := json.Marshal(req) if err != nil { return err } body = bytes.NewBuffer(b) } r, err = http.Post(cmd, "text/json", body) default: log.Panicf("%s: invalid method %q", cmd, meth) panic("invalid method: " + meth) } if err != nil { return err } defer r.Body.Close() if r.StatusCode != http.StatusOK { return fmt.Errorf("bad http response: %v", r.Status) } body := new(bytes.Buffer) if _, err := body.ReadFrom(r.Body); err != nil { return err } // Read JSON-encoded Response into provided resp // and return an error if present. var result = struct { Response interface{} Error string }{ // Put the provided resp in here as it can be a pointer to // some value we should unmarshal into. Response: resp, } if err = json.Unmarshal(body.Bytes(), &result); err != nil { log.Printf("json unmarshal %#q: %s\n", body.Bytes(), err) return err } if *verbose { log.Println("dash ->", result) } if result.Error != "" { return errors.New(result.Error) } return nil } // todo returns the next hash to build. func (b *Builder) todo(kind, pkg, goHash string) (rev string, err error) { args := url.Values{ "kind": {kind}, "builder": {b.name}, "packagePath": {pkg}, "goHash": {goHash}, } var resp *struct { Kind string Data struct { Hash string } } if err = dash("GET", "todo", args, nil, &resp); err != nil { return "", err } if resp == nil { return "", nil } if kind != resp.Kind { return "", fmt.Errorf("expecting Kind %q, got %q", kind, resp.Kind) } return resp.Data.Hash, nil } // recordResult sends build results to the dashboard func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string, runTime time.Duration) error { req := obj{ "Builder": b.name, "PackagePath": pkg, "Hash": hash, "GoHash": goHash, "OK": ok, "Log": buildLog, "RunTime": runTime, } args := url.Values{"key": {b.key}, "builder": {b.name}} return dash("POST", "result", args, req, nil) } func postCommit(key, pkg string, l *HgLog) error { t, err := time.Parse(time.RFC3339, l.Date) if err != nil { return fmt.Errorf("parsing %q: %v", l.Date, t) } return dash("POST", "commit", url.Values{"key": {key}}, obj{ "PackagePath": pkg, "Hash": l.Hash, "ParentHash": l.Parent, "Time": t.Format(time.RFC3339), "User": l.Author, "Desc": l.Desc, }, nil) } func dashboardCommit(pkg, hash string) bool { err := dash("GET", "commit", url.Values{ "packagePath": {pkg}, "hash": {hash}, }, nil, nil) return err == nil } func dashboardPackages(kind string) []string { args := url.Values{"kind": []string{kind}} var resp []struct { Path string } if err := dash("GET", "packages", args, nil, &resp); err != nil { log.Println("dashboardPackages:", err) return nil } var pkgs []string for _, r := range resp { pkgs = append(pkgs, r.Path) } return pkgs } ./oracle/0000755000014500017510000000000012246613010011740 5ustar michaelstaff./oracle/describe.go0000644000014500017510000006505212246613010014057 0ustar michaelstaff// 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 oracle import ( "bytes" "fmt" "go/ast" "go/token" "os" "sort" "strconv" "strings" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) // describe describes the syntax node denoted by the query position, // including: // - its syntactic category // - the location of the definition of its referent (for identifiers) // - its type and method set (for an expression or type expression) // - its points-to set (for a pointer-like expression) // - 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 describe(o *Oracle, qpos *QueryPos) (queryResult, error) { if false { // debugging o.fprintf(os.Stderr, qpos.path[0], "you selected: %s %s", importer.NodeDescription(qpos.path[0]), pathToString2(qpos.path)) } path, action := findInterestingNode(qpos.info, qpos.path) switch action { case actionExpr: return describeValue(o, qpos, path) case actionType: return describeType(o, qpos, path) case actionPackage: return describePackage(o, qpos, path) case actionStmt: return describeStmt(o, qpos, path) case actionUnknown: return &describeUnknownResult{path[0]}, nil default: panic(action) // unreachable } } type describeUnknownResult struct { node ast.Node } func (r *describeUnknownResult) display(printf printfFunc) { // Nothing much to say about misc syntax. printf(r.node, "%s", importer.NodeDescription(r.node)) } func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) { res.Describe = &serial.Describe{ Desc: importer.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 *importer.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: if pkginfo.ObjectOf(n.Sel) == nil { // 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: // For f in 'interface {f()}', return the interface type, for now. if _, ok := path[1].(*ast.Field); ok { _ = path[2].(*ast.FieldList) // assertion if _, ok := path[3].(*ast.InterfaceType); ok { return path[3:], actionType } } return path, actionExpr case *types.Builtin: // For reference to built-in function, return enclosing call. path = path[1:] // ascend to enclosing function call continue } // 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 (go/types bug?) // or y in "switch y := x.(type)" (go/types bug?) fmt.Printf("unknown reference %s in %T\n", n, path[1]) return path, actionUnknown } case *ast.StarExpr: if pkginfo.IsType(n) { 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 } // ---- VALUE ------------------------------------------------------------ // 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. // ssaValueForIdent may return a nil Value without an error to // indicate the pointer analysis is not appropriate. // func ssaValueForIdent(prog *ssa.Program, qinfo *importer.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) { if obj, ok := obj.(*types.Var); ok { pkg := prog.Package(qinfo.Pkg) pkg.Build() if v, addr := prog.VarValue(obj, pkg, path); v != nil { // Don't run pointer analysis on a ref to a const expression. if _, ok := v.(*ssa.Const); ok { return } return v, addr, nil } return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) } // Don't run pointer analysis on const/func objects. return } // ssaValueForExpr returns the ssa.Value of the non-ast.Ident // expression whose path to the root of the AST is path. It may // return a nil Value without an error to indicate the pointer // analysis is not appropriate. // func ssaValueForExpr(prog *ssa.Program, qinfo *importer.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) } func describeValue(o *Oracle, 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: // Is this reachable? return nil, fmt.Errorf("unexpected AST for expr: %T", n) } typ := qpos.info.TypeOf(expr) constVal := qpos.info.ValueOf(expr) // From this point on, we cannot fail with an error. // Failure to run the pointer analysis will be reported later. // // Our disposition to pointer analysis may be one of the following: // - ok: ssa.Value was const or func. // - error: no ssa.Value for expr (e.g. trivially dead code) // - ok: ssa.Value is non-pointerlike // - error: no Pointer for ssa.Value (e.g. analytically unreachable) // - ok: Pointer has empty points-to set // - ok: Pointer has non-empty points-to set // ptaErr is non-nil only in the "error:" cases. var ptaErr error var ptrs []pointerResult // Only run pointer analysis on pointerlike expression types. if pointer.CanPoint(typ) { // Determine the ssa.Value for the expression. var value ssa.Value var isAddr bool if obj != nil { // def/ref of func/var/const object value, isAddr, ptaErr = ssaValueForIdent(o.prog, qpos.info, obj, path) } else { // any other expression if qpos.info.ValueOf(path[0].(ast.Expr)) == nil { // non-constant? value, isAddr, ptaErr = ssaValueForExpr(o.prog, qpos.info, path) } } if value != nil { ptrs, ptaErr = describePointer(o, value, isAddr) } } return &describeValueResult{ qpos: qpos, expr: expr, typ: typ, constVal: constVal, obj: obj, ptaErr: ptaErr, ptrs: ptrs, }, nil } // describePointer runs the pointer analysis of the selected SSA value. func describePointer(o *Oracle, v ssa.Value, indirect bool) (ptrs []pointerResult, err error) { buildSSA(o) // TODO(adonovan): don't run indirect pointer analysis on non-ptr-ptrlike types. o.config.Queries = map[ssa.Value]pointer.Indirect{v: pointer.Indirect(indirect)} ptares := ptrAnalysis(o) // Combine the PT sets from all contexts. pointers := ptares.Queries[v] if pointers == nil { return nil, fmt.Errorf("PTA did not encounter this expression (dead code?)") } pts := pointer.PointsToCombined(pointers) if pointer.CanHaveDynamicTypes(v.Type()) { // Show concrete types for interface/reflect.Value expression. if concs := pts.DynamicTypes(); concs.Len() > 0 { concs.Iterate(func(conc types.Type, pta interface{}) { combined := pointer.PointsToCombined(pta.([]pointer.Pointer)) labels := combined.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{v.Type(), 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 } 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 ptaErr error // reason why pointer analysis couldn't be run, or failed ptrs []pointerResult // pointer info (typ is concrete => len==1) } func (r *describeValueResult) display(printf printfFunc) { var prefix, suffix string if r.constVal != nil { suffix = fmt.Sprintf(" of constant value %s", 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 := importer.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)) } } // pointer analysis could not be run if r.ptaErr != nil { printf(r.expr, "no points-to information: %s", r.ptaErr) return } if r.ptrs == nil { return // PTA was not invoked (not an error) } // Display the results of pointer analysis. 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, "value may point to these labels:") printLabels(printf, ptr.labels, "\t") } else { printf(r.qpos, "value cannot point to anything.") } } } func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { var value, objpos, ptaerr string if r.constVal != nil { value = r.constVal.String() } if r.obj != nil { objpos = fset.Position(r.obj.Pos()).String() } if r.ptaErr != nil { ptaerr = r.ptaErr.Error() } var pts []*serial.DescribePointer 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.DescribePTALabel for _, l := range ptr.labels { labels = append(labels, serial.DescribePTALabel{ Pos: fset.Position(l.Pos()).String(), Desc: l.String(), }) } pts = append(pts, &serial.DescribePointer{ Type: r.qpos.TypeString(ptr.typ), NamePos: namePos, Labels: labels, }) } res.Describe = &serial.Describe{ Desc: importer.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, PTAErr: ptaerr, PTS: 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) } } // ---- TYPE ------------------------------------------------------------ func describeType(o *Oracle, 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: // TODO(adonovan): use o.imp.Config().TypeChecker.Sizes when // we add the Config() method (needs some thought). szs := types.StdSizes{8, 8} 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 { 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(o *Oracle, qpos *QueryPos, path []ast.Node) (*describePackageResult, error) { var description string var pkg *types.Package switch n := path[0].(type) { case *ast.ImportSpec: // Most ImportSpecs have no .Name Ident so we can't // use ObjectOf. // We could use the types.Info.Implicits mechanism, // but it's easier just to look it up by name. description = "import of package " + n.Path.Value importPath, _ := strconv.Unquote(n.Path.Value) pkg = o.prog.ImportedPackage(importPath).Object 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).Pkg() 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{o.prog.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", meth) } } } func formatMember(obj types.Object, maxname int) string { 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.Pkg(), obj.Type()), obj.Val().String()) case *types.Func: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type())) 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.Pkg(), obj.Type().Underlying())) } else { fmt.Fprintf(&buf, " %s", abbrev) } case *types.Var: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type())) } 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 = mem.Val().String() 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" } panic(o) } // ---- STATEMENT ------------------------------------------------------------ func describeStmt(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeStmtResult, error) { var description string switch n := path[0].(type) { case *ast.Ident: if qpos.info.ObjectOf(n).Pos() == n.Pos() { description = "labelled statement" } else { description = "reference to labelled statement" } default: // Nothing much to say about statements. description = importer.NodeDescription(n) } return &describeStmtResult{o.prog.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 pathToString2(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 ssa.IntuitiveMethodSet(t) { 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 { var jmethods []serial.DescribeMethod for _, meth := range methods { jmethods = append(jmethods, serial.DescribeMethod{ Name: types.SelectionString(this, meth), Pos: fset.Position(meth.Obj().Pos()).String(), }) } return jmethods } ./oracle/referrers.go0000644000014500017510000000473112246613010014273 0ustar michaelstaff// 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 oracle import ( "fmt" "go/ast" "go/token" "sort" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/oracle/serial" ) // Referrers reports all identifiers that resolve to the same object // as the queried identifier, within any package in the analysis scope. // func referrers(o *Oracle, qpos *QueryPos) (queryResult, error) { id, _ := qpos.path[0].(*ast.Ident) if id == nil { return nil, fmt.Errorf("no identifier here") } obj := qpos.info.ObjectOf(id) if obj == nil { // Happens for y in "switch y := x.(type)", but I think that's all. return nil, fmt.Errorf("no object for identifier") } // Iterate over all go/types' resolver facts for the entire program. var refs []token.Pos for _, info := range o.typeInfo { for id2, obj2 := range info.Objects { if sameObj(obj, obj2) { if id2.NamePos == obj.Pos() { continue // skip defining ident } refs = append(refs, id2.NamePos) } } } sort.Sort(byPos(refs)) return &referrersResult{ query: id.NamePos, obj: obj, refs: refs, }, nil } // same reports whether x and y are identical, or both are PkgNames // referring to the same Package. // func sameObj(x, y types.Object) bool { if x == y { return true } if _, ok := x.(*types.PkgName); ok { if _, ok := y.(*types.PkgName); ok { return x.Pkg() == y.Pkg() } } return false } type referrersResult struct { query token.Pos // identifer of query obj types.Object // object it denotes refs []token.Pos // set of all other references to it } func (r *referrersResult) display(printf printfFunc) { if r.query != r.obj.Pos() { printf(r.query, "reference to %s", r.obj.Name()) } // TODO(adonovan): pretty-print object using same logic as // (*describeValueResult).display. printf(r.obj, "defined here as %s", r.obj) for _, ref := range r.refs { if r.query != ref { printf(ref, "referenced here") } } } func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) { referrers := &serial.Referrers{ Pos: fset.Position(r.query).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).String()) } res.Referrers = referrers } ./oracle/callees.go0000644000014500017510000000724312246613010013705 0ustar michaelstaff// 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 oracle import ( "fmt" "go/ast" "go/token" "sort" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/ssa" ) // Callees reports the possible callees of the function call site // identified by the specified source location. // // TODO(adonovan): if a callee is a wrapper, show the callee's callee. // func callees(o *Oracle, qpos *QueryPos) (queryResult, error) { pkg := o.prog.Package(qpos.info.Pkg) if pkg == nil { return nil, fmt.Errorf("no SSA package") } // 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 nil, 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.IsType(e.Fun) { return nil, fmt.Errorf("this is a type conversion, not a function call") } // Reject calls to built-ins. if id, ok := unparen(e.Fun).(*ast.Ident); ok { if b, ok := qpos.info.ObjectOf(id).(*types.Builtin); ok { return nil, fmt.Errorf("this is a call to the built-in '%s' operator", b.Name()) } } buildSSA(o) // Ascertain calling function and call site. callerFn := ssa.EnclosingFunction(pkg, qpos.path) if callerFn == nil { return nil, fmt.Errorf("no SSA function built for this location (dead code?)") } o.config.BuildCallGraph = true callgraph := ptrAnalysis(o).CallGraph // Find the call site and all edges from it. var site ssa.CallInstruction calleesMap := make(map[*ssa.Function]bool) for _, n := range callgraph.Nodes() { if n.Func() == callerFn { if site == nil { // First node for callerFn: identify the site. for _, s := range n.Sites() { if s.Pos() == e.Lparen { site = s break } } if site == nil { return nil, fmt.Errorf("this call site is unreachable in this analysis") } } for _, edge := range n.Edges() { if edge.Site == site { calleesMap[edge.Callee.Func()] = true } } } } if site == nil { return nil, fmt.Errorf("this function is unreachable in this analysis") } // Discard context, de-duplicate and sort. funcs := make([]*ssa.Function, 0, len(calleesMap)) for f := range calleesMap { funcs = append(funcs, f) } sort.Sort(byFuncPos(funcs)) return &calleesResult{ site: site, funcs: funcs, }, nil } type calleesResult struct { site ssa.CallInstruction funcs []*ssa.Function } func (r *calleesResult) 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 *calleesResult) 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 } 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] } ./oracle/serial/0000755000014500017510000000000012246613010013217 5ustar michaelstaff./oracle/serial/serial.go0000644000014500017510000002314112246613010015026 0ustar michaelstaff// 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 serial defines the oracle's schema for structured data // serialization using JSON, XML, etc. package serial // All 'pos' strings are of the form "file:line:col". // TODO(adonovan): improve performance by sharing filename strings. // TODO(adonovan): improve precision by providing the start/end // interval when available. // // TODO(adonovan): consider richer encodings of types, functions, // methods, etc. // A Peers is the result of a 'peers' query. // If Allocs is empty, the selected channel can't point to anything. type Peers struct { Pos string `json:"pos"` // location of the selected channel op (<-) Type string `json:"type"` // type of the selected channel Allocs []string `json:"allocs,omitempty"` // locations of aliased make(chan) ops Sends []string `json:"sends,omitempty"` // locations of aliased ch<-x ops Receives []string `json:"receives,omitempty"` // locations of aliased <-ch ops } // A Referrers is the result of a 'referrers' query. type Referrers struct { Pos string `json:"pos"` // location of the query reference ObjPos string `json:"objpos,omitempty"` // location of the definition Desc string `json:"desc"` // description of the denoted object Refs []string `json:"refs,omitempty"` // locations of all references } type CalleesItem struct { Name string `json:"name"` // full name of called function Pos string `json:"pos"` // location of called function } // A Callees is the result of a 'callees' query. // // Callees is nonempty unless the call was a dynamic call on a // provably nil func or interface value. type Callees struct { Pos string `json:"pos"` // location of selected call site Desc string `json:"desc"` // description of call site Callees []*CalleesItem `json:"callees,omitempty"` // set of possible call targets } // A Caller is one element of the slice returned by a 'callers' query. // (Callstack also contains a similar slice.) // // The root of the callgraph has an unspecified "Caller" string. type Caller struct { Pos string `json:"pos,omitempty"` // location of the calling function Desc string `json:"desc"` // description of call site Caller string `json:"caller"` // full name of calling function } // A CallGraph is one element of the slice returned by a 'callgraph' query. // The index of each item in the slice is used to identify it in the // Callers adjacency list. // // Multiple nodes may have the same Name due to context-sensitive // treatment of some functions. // // TODO(adonovan): perhaps include edge labels (i.e. callsites). type CallGraph struct { Name string `json:"name"` // full name of function Pos string `json:"pos"` // location of function Children []int `json:"children,omitempty"` // indices of child nodes in callgraph list } // A CallStack is the result of a 'callstack' query. // It indicates an arbitrary path from the root of the callgraph to // the query function. // // If the Callers slice is empty, the function was unreachable in this // analysis scope. type CallStack struct { Pos string `json:"pos"` // location of the selected function Target string `json:"target"` // the selected function Callers []Caller `json:"callers"` // enclosing calls, innermost first. } // A FreeVar is one element of the slice returned by a 'freevars' // query. Each one identifies an expression referencing a local // identifier defined outside the selected region. type FreeVar struct { Pos string `json:"pos"` // location of the identifier's definition Kind string `json:"kind"` // one of {var,func,type,const,label} Ref string `json:"ref"` // referring expression (e.g. "x" or "x.y.z") Type string `json:"type"` // type of the expression } // An Implements is one element of the result of an 'implements' query. // Each one indicates a row in the "implements" relation over // package-level named types defined by the package containing the // selection. type Implements struct { I string `json:"i"` // full name of the interface type IPos string `json:"ipos"` // location of its definition C string `json:"c"` // full name of the concrete type CPos string `json:"cpos"` // location of its definition } // A DescribePTALabel describes a pointer analysis label. // // A "label" is an object that may be pointed to by a pointer, map, // channel, 'func', slice or interface. Labels include: // - functions // - globals // - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s)) // - stack- and heap-allocated variables (including composite literals) // - arrays allocated by append() // - channels, maps and arrays created by make() // - and their subelements, e.g. "alloc.y[*].z" // type DescribePTALabel struct { Pos string `json:"pos"` // location of syntax that allocated the object Desc string `json:"desc"` // description of the label } // A DescribePointer describes a single pointer: its type and the // set of "labels" it points to. // type DescribePointer struct { Type string `json:"type"` // (concrete) type of the pointer NamePos string `json:"namepos,omitempty"` // location of type defn, if Named Labels []DescribePTALabel `json:"labels,omitempty"` // pointed-to objects } // A DescribeValue is the additional result of a 'describe' query // if the selection indicates a value or expression. // // If the described value is an interface, it will have one PTS entry // describing each concrete type that it may contain. For each // concrete type that is a pointer, the PTS entry describes the labels // it may point to. The same is true for reflect.Values, except the // dynamic types needn't be concrete. // type DescribeValue struct { Type string `json:"type"` // type of the expression Value string `json:"value,omitempty"` // value of the expression, if constant ObjPos string `json:"objpos,omitempty"` // location of the definition, if an Ident PTAErr string `json:"ptaerr,omitempty"` // reason pointer analysis wasn't attempted PTS []*DescribePointer `json:"pts,omitempty"` // points-to set; an interface may have many } type DescribeMethod struct { Name string `json:"name"` // method name, as defined by types.Selection.String() Pos string `json:"pos"` // location of the method's definition } // A DescribeType is the additional result of a 'describe' query // if the selection indicates a type. type DescribeType struct { Type string `json:"type"` // the string form of the type NamePos string `json:"namepos,omitempty"` // location of definition of type, if named NameDef string `json:"namedef,omitempty"` // underlying definition of type, if named Methods []DescribeMethod `json:"methods,omitempty"` // methods of the type } type DescribeMember struct { Name string `json:"name"` // name of member Type string `json:"type,omitempty"` // type of member (underlying, if 'type') Value string `json:"value,omitempty"` // value of member (if 'const') Pos string `json:"pos"` // location of definition of member Kind string `json:"kind"` // one of {var,const,func,type} Methods []DescribeMethod `json:"methods,omitempty"` // methods (if member is a type) } // A DescribePackage is the additional result of a 'describe' if // the selection indicates a package. type DescribePackage struct { Path string `json:"path"` // import path of the package Members []*DescribeMember `json:"members,omitempty"` // accessible members of the package } // A Describe is the result of a 'describe' query. // It may contain an element describing the selected semantic entity // in detail. type Describe struct { Desc string `json:"desc"` // description of the selected syntax node Pos string `json:"pos"` // location of the selected syntax node Detail string `json:"detail,omitempty"` // one of {package, type, value}, or "". // At most one of the following fields is populated: // the one specified by 'detail'. Package *DescribePackage `json:"package,omitempty"` Type *DescribeType `json:"type,omitempty"` Value *DescribeValue `json:"value,omitempty"` } type PTAWarning struct { Pos string `json:"pos"` // location associated with warning Message string `json:"message"` // warning message } // A Result is the common result of any oracle query. // It contains a query-specific result element. // // TODO(adonovan): perhaps include other info such as: analysis scope, // raw query position, stack of ast nodes, query package, etc. type Result struct { Mode string `json:"mode"` // mode of the query // Exactly one of the following fields is populated: // the one specified by 'mode'. Callees *Callees `json:"callees,omitempty"` Callers []Caller `json:"callers,omitempty"` Callgraph []CallGraph `json:"callgraph,omitempty"` Callstack *CallStack `json:"callstack,omitempty"` Describe *Describe `json:"describe,omitempty"` Freevars []*FreeVar `json:"freevars,omitempty"` Implements []*Implements `json:"implements,omitempty"` Peers *Peers `json:"peers,omitempty"` Referrers *Referrers `json:"referrers,omitempty"` Warnings []PTAWarning `json:"warnings,omitempty"` // warnings from pointer analysis } ./oracle/callstack.go0000644000014500017510000000531212246613010014231 0ustar michaelstaff// 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 oracle import ( "fmt" "go/token" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/ssa" ) // Callstack displays an arbitrary path from a root of the callgraph // to the function at the current position. // // The information may be misleading in a context-insensitive // analysis. e.g. the call path X->Y->Z might be infeasible if Y never // calls Z when it is called from X. TODO(adonovan): think about UI. // // TODO(adonovan): permit user to specify a starting point other than // the analysis root. // func callstack(o *Oracle, qpos *QueryPos) (queryResult, error) { pkg := o.prog.Package(qpos.info.Pkg) if pkg == nil { return nil, fmt.Errorf("no SSA package") } if !ssa.HasEnclosingFunction(pkg, qpos.path) { return nil, fmt.Errorf("this position is not inside a function") } buildSSA(o) target := ssa.EnclosingFunction(pkg, qpos.path) if target == nil { return nil, fmt.Errorf("no SSA function built for this location (dead code?)") } // Run the pointer analysis and build the complete call graph. o.config.BuildCallGraph = true callgraph := ptrAnalysis(o).CallGraph // Search for an arbitrary path from a root to the target function. isEnd := func(n call.GraphNode) bool { return n.Func() == target } callpath := call.PathSearch(callgraph.Root(), isEnd) if callpath != nil { callpath = callpath[1:] // remove synthetic edge from } return &callstackResult{ qpos: qpos, target: target, callpath: callpath, }, nil } type callstackResult struct { qpos *QueryPos target *ssa.Function callpath []call.Edge } func (r *callstackResult) display(printf printfFunc) { if r.callpath != nil { printf(r.qpos, "Found a call path from root to %s", r.target) printf(r.target, "%s", r.target) for i := len(r.callpath) - 1; i >= 0; i-- { edge := r.callpath[i] printf(edge.Site, "%s from %s", edge.Site.Common().Description(), edge.Caller.Func()) } } else { printf(r.target, "%s is unreachable in this analysis scope", r.target) } } func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) { var callers []serial.Caller for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first) edge := r.callpath[i] callers = append(callers, serial.Caller{ Pos: fset.Position(edge.Site.Pos()).String(), Caller: edge.Caller.Func().String(), Desc: edge.Site.Common().Description(), }) } res.Callstack = &serial.CallStack{ Pos: fset.Position(r.target.Pos()).String(), Target: r.target.String(), Callers: callers, } } ./oracle/implements.go0000644000014500017510000000615412246613010014452 0ustar michaelstaff// 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 oracle import ( "go/token" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/oracle/serial" ) // Implements displays the 'implements" relation among all // package-level named types in the package containing the query // position. // // TODO(adonovan): more features: // - should we include pairs of types belonging to // different packages in the 'implements' relation? // - should we restrict the query to the type declaration identified // by the query position, if any, and use all types in the package // otherwise? // - should we show types that are local to functions? // They can only have methods via promotion. // - abbreviate the set of concrete types implementing the empty // interface. // - should we scan the instruction stream for MakeInterface // instructions and report which concrete->interface conversions // actually occur, with examples? (NB: this is not a conservative // answer due to ChangeInterface, i.e. subtyping among interfaces.) // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { pkg := qpos.info.Pkg // Compute set of named interface/concrete types at package level. var interfaces, concretes []*types.Named scope := pkg.Scope() for _, name := range scope.Names() { mem := scope.Lookup(name) if t, ok := mem.(*types.TypeName); ok { nt := t.Type().(*types.Named) if _, ok := nt.Underlying().(*types.Interface); ok { interfaces = append(interfaces, nt) } else { concretes = append(concretes, nt) } } } // For each interface, show the concrete types that implement it. var facts []implementsFact for _, iface := range interfaces { fact := implementsFact{iface: iface} for _, conc := range concretes { if types.IsAssignableTo(conc, iface) { fact.conc = conc } else if ptr := types.NewPointer(conc); types.IsAssignableTo(ptr, iface) { fact.conc = ptr } else { continue } facts = append(facts, fact) } } // TODO(adonovan): sort facts to ensure test nondeterminism. return &implementsResult{o.prog.Fset, facts}, nil } type implementsFact struct { iface *types.Named conc types.Type // Named or Pointer(Named) } type implementsResult struct { fset *token.FileSet facts []implementsFact // facts are grouped by interface } func (r *implementsResult) display(printf printfFunc) { var prevIface *types.Named for _, fact := range r.facts { if fact.iface != prevIface { printf(fact.iface.Obj(), "\tInterface %s:", fact.iface) prevIface = fact.iface } printf(deref(fact.conc).(*types.Named).Obj(), "\t\t%s", fact.conc) } } func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) { var facts []*serial.Implements for _, fact := range r.facts { facts = append(facts, &serial.Implements{ I: fact.iface.String(), IPos: fset.Position(fact.iface.Obj().Pos()).String(), C: fact.conc.String(), CPos: fset.Position(deref(fact.conc).(*types.Named).Obj().Pos()).String(), }) } res.Implements = facts } ./oracle/callers.go0000644000014500017510000000471212246613010013720 0ustar michaelstaff// 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 oracle import ( "fmt" "go/token" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/ssa" ) // Callers reports the possible callers of the function // immediately enclosing the specified source location. // // TODO(adonovan): if a caller is a wrapper, show the caller's caller. // func callers(o *Oracle, qpos *QueryPos) (queryResult, error) { pkg := o.prog.Package(qpos.info.Pkg) if pkg == nil { return nil, fmt.Errorf("no SSA package") } if !ssa.HasEnclosingFunction(pkg, qpos.path) { return nil, fmt.Errorf("this position is not inside a function") } buildSSA(o) target := ssa.EnclosingFunction(pkg, qpos.path) if target == nil { return nil, fmt.Errorf("no SSA function built for this location (dead code?)") } // Run the pointer analysis, recording each // call found to originate from target. o.config.BuildCallGraph = true callgraph := ptrAnalysis(o).CallGraph var edges []call.Edge call.GraphVisitEdges(callgraph, func(edge call.Edge) error { if edge.Callee.Func() == target { edges = append(edges, edge) } return nil }) // TODO(adonovan): sort + dedup calls to ensure test determinism. return &callersResult{ target: target, callgraph: callgraph, edges: edges, }, nil } type callersResult struct { target *ssa.Function callgraph call.Graph edges []call.Edge } func (r *callersResult) display(printf printfFunc) { root := r.callgraph.Root() if r.edges == nil { printf(r.target, "%s is not reachable in this program.", r.target) } else { printf(r.target, "%s is called from these %d sites:", r.target, len(r.edges)) for _, edge := range r.edges { if edge.Caller == root { printf(r.target, "the root of the call graph") } else { printf(edge.Site, "\t%s from %s", edge.Site.Common().Description(), edge.Caller.Func()) } } } } func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) { root := r.callgraph.Root() var callers []serial.Caller for _, edge := range r.edges { var c serial.Caller c.Caller = edge.Caller.Func().String() if edge.Caller == root { c.Desc = "synthetic call" } else { c.Pos = fset.Position(edge.Site.Pos()).String() c.Desc = edge.Site.Common().Description() } callers = append(callers, c) } res.Callers = callers } ./oracle/callgraph.go0000644000014500017510000000674212246613010014235 0ustar michaelstaff// 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 oracle import ( "go/token" "sort" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/ssa" ) // callgraph displays the entire callgraph of the current program. // // Nodes may be seem to appear multiple times due to (limited) // context sensitivity. // // TODO(adonovan): add options for restricting the display to a region // of interest: function, package, subgraph, dirtree, goroutine, etc. // // TODO(adonovan): add an option to project away context sensitivity. // The callgraph API should provide this feature. // // TODO(adonovan): add an option to partition edges by call site. // // TODO(adonovan): elide nodes for synthetic functions? // func callgraph(o *Oracle, _ *QueryPos) (queryResult, error) { buildSSA(o) // Run the pointer analysis and build the complete callgraph. o.config.BuildCallGraph = true ptares := ptrAnalysis(o) return &callgraphResult{ callgraph: ptares.CallGraph, }, nil } type callgraphResult struct { callgraph call.Graph } func (r *callgraphResult) display(printf printfFunc) { printf(nil, ` Below is a call graph of the entire program. The numbered nodes form a spanning tree. Non-numbered nodes indicate back- or cross-edges to the node whose number follows in parentheses. `) root := r.callgraph.Root() // context-insensitive (CI) call graph. ci := make(map[*ssa.Function]map[*ssa.Function]bool) // 1. Visit the CS call graph and build the CI call graph. visited := make(map[call.GraphNode]bool) var visit func(caller call.GraphNode) visit = func(caller call.GraphNode) { if !visited[caller] { visited[caller] = true cicallees := ci[caller.Func()] if cicallees == nil { cicallees = make(map[*ssa.Function]bool) ci[caller.Func()] = cicallees } for _, e := range caller.Edges() { cicallees[e.Callee.Func()] = true visit(e.Callee) } } } visit(root) // 2. Print the CI callgraph. printed := make(map[*ssa.Function]int) var print func(caller *ssa.Function, indent int) print = func(caller *ssa.Function, indent int) { if num, ok := printed[caller]; !ok { num = len(printed) printed[caller] = num // Sort the children into name order for deterministic* output. // (*mostly: anon funcs' names are not globally unique.) var funcs funcsByName for callee := range ci[caller] { funcs = append(funcs, callee) } sort.Sort(funcs) printf(caller, "%d\t%*s%s", num, 4*indent, "", caller) for _, callee := range funcs { print(callee, indent+1) } } else { printf(caller, "\t%*s%s (%d)", 4*indent, "", caller, num) } } print(root.Func(), 0) } type funcsByName []*ssa.Function func (s funcsByName) Len() int { return len(s) } func (s funcsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s funcsByName) Less(i, j int) bool { return s[i].String() < s[j].String() } func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) { nodes := r.callgraph.Nodes() numbering := make(map[call.GraphNode]int) for i, n := range nodes { numbering[n] = i } cg := make([]serial.CallGraph, len(nodes)) for i, n := range nodes { j := &cg[i] fn := n.Func() j.Name = fn.String() j.Pos = fset.Position(fn.Pos()).String() for callee := range call.CalleesOf(n) { j.Children = append(j.Children, numbering[callee]) } } res.Callgraph = cg } ./oracle/freevars.go0000644000014500017510000001125112246613010014104 0ustar michaelstaff// 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 oracle import ( "go/ast" "go/token" "sort" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.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(o *Oracle, qpos *QueryPos) (queryResult, error) { 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.ObjectOf(n) if obj == nil { return nil // TODO(adonovan): fix: this fails for *types.Label. panic("no types.Object for ast.Ident") } if _, ok := obj.(*types.PkgName); ok { return nil // imported package } if n.Pos() == obj.Pos() { return nil // this ident is the definition, not a reference } 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, o.printNode(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)) return &freevarsResult{ qpos: qpos, fset: o.prog.Fset, refs: refs, }, nil } type freevarsResult struct { qpos *QueryPos fset *token.FileSet 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:") for _, ref := range r.refs { printf(ref.obj, "%s %s %s", ref.kind, ref.ref, ref.typ) } } } 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] } ./oracle/peers.go0000644000014500017510000001300012246613010013377 0ustar michaelstaff// 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 oracle import ( "fmt" "go/ast" "go/token" "sort" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) // 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}. // 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(o *Oracle, qpos *QueryPos) (queryResult, error) { arrowPos := findArrow(qpos) if arrowPos == token.NoPos { return nil, fmt.Errorf("there is no send/receive here") } buildSSA(o) var queryOp chanOp // the originating send or receive operation var ops []chanOp // all sends/receives of opposite direction // Look at all send/receive instructions in the whole ssa.Program. // Build a list of those of same type to query. allFuncs := ssa.AllFunctions(o.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 == arrowPos { queryOp = op // we found the query op } } } } } if queryOp.ch == nil { return nil, 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() channels := map[ssa.Value]pointer.Indirect{queryOp.ch: false} i := 0 for _, op := range ops { if types.IsIdentical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) { channels[op.ch] = false ops[i] = op i++ } } ops = ops[:i] // Run the pointer analysis. o.config.Queries = channels ptares := ptrAnalysis(o) // Combine the PT sets from all contexts. queryChanPts := pointer.PointsToCombined(ptares.Queries[queryOp.ch]) // Ascertain which make(chan) labels the query's channel can alias. var makes []token.Pos for _, label := range queryChanPts.Labels() { makes = append(makes, label.Pos()) } sort.Sort(byPos(makes)) // Ascertain which send/receive operations can alias the same make(chan) labels. var sends, receives []token.Pos for _, op := range ops { for _, ptr := range ptares.Queries[op.ch] { if ptr != nil && ptr.PointsTo().Intersects(queryChanPts) { if op.dir == ast.SEND { sends = append(sends, op.pos) } else { receives = append(receives, op.pos) } } } } sort.Sort(byPos(sends)) sort.Sort(byPos(receives)) return &peersResult{ queryPos: arrowPos, queryType: queryType, makes: makes, sends: sends, receives: receives, }, nil } // findArrow returns the position of the enclosing send/receive op // (<-) for the query position, or token.NoPos if not found. // func findArrow(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 } } return token.NoPos } // chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState. type chanOp struct { ch ssa.Value dir ast.ChanDir 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} too. var ops []chanOp switch instr := instr.(type) { case *ssa.UnOp: if instr.Op == token.ARROW { ops = append(ops, chanOp{instr.X, ast.RECV, instr.Pos()}) } case *ssa.Send: ops = append(ops, chanOp{instr.Chan, ast.SEND, instr.Pos()}) case *ssa.Select: for _, st := range instr.States { ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos}) } } return ops } type peersResult struct { queryPos token.Pos // of queried '<-' token queryType types.Type // type of queried channel makes, sends, receives []token.Pos // positions of alisaed makechan/send/receive 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") } } 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()) } res.Peers = peers } // -------- utils -------- 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] } ./oracle/oracle.go0000644000014500017510000003746212246613010013550 0ustar michaelstaff// 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 oracle contains the implementation of the oracle tool whose // command-line is provided by code.google.com/p/go.tools/cmd/oracle. // // http://golang.org/s/oracle-design // http://golang.org/s/oracle-user-manual // package oracle // This file defines oracle.Query, the entry point for the oracle tool. // The actual executable is defined in cmd/oracle. // TODO(adonovan): new query: show all statements that may update the // selected lvalue (local, global, field, etc). import ( "bytes" "fmt" "go/ast" "go/build" "go/printer" "go/token" "io" "os" "path/filepath" "strconv" "strings" "time" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) // An Oracle holds the program state required for one or more queries. type Oracle struct { out io.Writer // standard output prog *ssa.Program // the SSA program [only populated if need&SSA] config pointer.Config // pointer analysis configuration [TODO rename ptaConfig] // need&AllTypeInfo typeInfo map[*types.Package]*importer.PackageInfo // type info for all ASTs in the program timers map[string]time.Duration // phase timing information } // A set of bits indicating the analytical requirements of each mode. // // Typed ASTs for the whole program are always constructed // transiently; they are retained only for the queried package unless // needAllTypeInfo is set. const ( needPos = 1 << iota // needs a position needExactPos // needs an exact AST selection; implies needPos needAllTypeInfo // needs to retain type info for all ASTs in the program needSSA // needs ssa.Packages for whole program needSSADebug // needs debug info for ssa.Packages needPTA = needSSA // needs pointer analysis needAll = -1 // needs everything (e.g. a sequence of queries) ) type modeInfo struct { name string needs int impl func(*Oracle, *QueryPos) (queryResult, error) } var modes = []*modeInfo{ {"callees", needPTA | needExactPos, callees}, {"callers", needPTA | needPos, callers}, {"callgraph", needPTA, callgraph}, {"callstack", needPTA | needPos, callstack}, {"describe", needPTA | needSSADebug | needExactPos, describe}, {"freevars", needPos, freevars}, {"implements", needPos, implements}, {"peers", needPTA | needSSADebug | needPos, peers}, {"referrers", needAllTypeInfo | needPos, referrers}, } func findMode(mode string) *modeInfo { for _, m := range modes { if m.name == mode { return m } } return nil } 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 { start, end token.Pos // source extent of query info *importer.PackageInfo // type info for the queried package path []ast.Node // AST path from query node to root of ast.File } // TypeString prints type T relative to the query position. func (qpos *QueryPos) TypeString(T types.Type) string { return types.TypeString(qpos.info.Pkg, T) } // ObjectString prints object obj relative to the query position. func (qpos *QueryPos) ObjectString(obj types.Object) string { return types.ObjectString(qpos.info.Pkg, obj) } // SelectionString prints selection sel relative to the query position. func (qpos *QueryPos) SelectionString(sel *types.Selection) string { return types.SelectionString(qpos.info.Pkg, sel) } // A Result encapsulates the result of an oracle.Query. type Result struct { fset *token.FileSet // fprintf is a closure over the oracle's fileset and start/end position. fprintf func(w io.Writer, pos interface{}, format string, args ...interface{}) q queryResult // the query-specific result mode string // query mode warnings []pointer.Warning // pointer analysis warnings } // 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 (res *Result) Serial() *serial.Result { resj := &serial.Result{Mode: res.mode} res.q.toSerial(resj, res.fset) for _, w := range res.warnings { resj.Warnings = append(resj.Warnings, serial.PTAWarning{ Pos: res.fset.Position(w.Pos).String(), Message: w.Message, }) } return resj } // Query runs a single oracle query. // // args specify the main package in importer.LoadInitialPackages syntax. // mode is the query mode ("callers", etc). // ptalog is the (optional) pointer-analysis log file. // buildContext is the go/build configuration for locating packages. // reflection determines whether to model reflection soundly (currently slow). // // Clients that intend to perform multiple queries against the same // analysis scope should use this pattern instead: // // imp := importer.New(&importer.Config{Build: buildContext}) // o, err := oracle.New(imp, args, nil) // if err != nil { ... } // for ... { // qpos, err := oracle.ParseQueryPos(imp, pos, needExact) // if err != nil { ... } // // res, err := o.Query(mode, qpos) // if err != nil { ... } // // // use res // } // // TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos // depends on the query mode; how should we expose this? // func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context, reflection bool) (*Result, error) { minfo := findMode(mode) if minfo == nil { return nil, fmt.Errorf("invalid mode type: %q", mode) } imp := importer.New(&importer.Config{Build: buildContext}) o, err := New(imp, args, ptalog, reflection) if err != nil { return nil, err } // Phase timing diagnostics. // TODO(adonovan): needs more work. // if false { // defer func() { // fmt.Println() // for name, duration := range o.timers { // fmt.Printf("# %-30s %s\n", name, duration) // } // }() // } var qpos *QueryPos if minfo.needs&(needPos|needExactPos) != 0 { var err error qpos, err = ParseQueryPos(imp, pos, minfo.needs&needExactPos != 0) if err != nil { return nil, err } } // SSA is built and we have the QueryPos. // Release the other ASTs and type info to the GC. imp = nil return o.query(minfo, qpos) } // New constructs a new Oracle that can be used for a sequence of queries. // // imp will be used to load source code for imported packages. // It must not yet have loaded any packages. // // args specify the main package in importer.LoadInitialPackages syntax. // // ptalog is the (optional) pointer-analysis log file. // reflection determines whether to model reflection soundly (currently slow). // func New(imp *importer.Importer, args []string, ptalog io.Writer, reflection bool) (*Oracle, error) { return newOracle(imp, args, ptalog, needAll, reflection) } func newOracle(imp *importer.Importer, args []string, ptalog io.Writer, needs int, reflection bool) (*Oracle, error) { o := &Oracle{ prog: ssa.NewProgram(imp.Fset, 0), timers: make(map[string]time.Duration), } o.config.Log = ptalog o.config.Reflection = reflection // Load/parse/type-check program from args. start := time.Now() initialPkgInfos, args, err := imp.LoadInitialPackages(args) if err != nil { return nil, err // I/O or parser error } if len(args) > 0 { return nil, fmt.Errorf("surplus arguments: %q", args) } o.timers["load/parse/type"] = time.Since(start) // Retain type info for all ASTs in the program. if needs&needAllTypeInfo != 0 { m := make(map[*types.Package]*importer.PackageInfo) for _, p := range imp.AllPackages() { m[p.Pkg] = p } o.typeInfo = m } // Create SSA package for the initial packages and their dependencies. if needs&needSSA != 0 { start = time.Now() // Create SSA packages. if err := o.prog.CreatePackages(imp); err != nil { return nil, err } // 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 []*ssa.Package for _, info := range initialPkgInfos { initialPkg := o.prog.Package(info.Pkg) // Add package to the pointer analysis scope. if initialPkg.Func("main") != nil { o.config.Mains = append(o.config.Mains, initialPkg) } else { testPkgs = append(testPkgs, initialPkg) } } if testPkgs != nil { if p := o.prog.CreateTestMainPackage(testPkgs...); p != nil { o.config.Mains = append(o.config.Mains, p) } } if o.config.Mains == nil { return nil, fmt.Errorf("analysis scope has no main and no tests") } if needs&needSSADebug != 0 { for _, pkg := range o.prog.AllPackages() { pkg.SetDebugMode(true) } } o.timers["SSA-create"] = time.Since(start) } return o, nil } // Query runs the query of the specified mode and selection. func (o *Oracle) Query(mode string, qpos *QueryPos) (*Result, error) { minfo := findMode(mode) if minfo == nil { return nil, fmt.Errorf("invalid mode type: %q", mode) } return o.query(minfo, qpos) } func (o *Oracle) query(minfo *modeInfo, qpos *QueryPos) (*Result, error) { res := &Result{ mode: minfo.name, fset: o.prog.Fset, fprintf: o.fprintf, // captures o.prog, o.{start,end}Pos for later printing } var err error res.q, err = minfo.impl(o, qpos) if err != nil { return nil, err } return res, nil } // ParseQueryPos parses the source query position pos. // If needExact, it must identify a single AST subtree. // func ParseQueryPos(imp *importer.Importer, pos string, needExact bool) (*QueryPos, error) { start, end, err := parseQueryPos(imp.Fset, pos) if err != nil { return nil, err } info, path, exact := imp.PathEnclosingInterval(start, end) if path == nil { return nil, fmt.Errorf("no syntax here") } if needExact && !exact { return nil, fmt.Errorf("ambiguous selection within %s", importer.NodeDescription(path[0])) } return &QueryPos{start, end, info, path}, nil } // WriteTo writes the oracle query result res to out in a compiler diagnostic format. func (res *Result) WriteTo(out io.Writer) { printf := func(pos interface{}, format string, args ...interface{}) { res.fprintf(out, pos, format, args...) } res.q.display(printf) // Print warnings after the main output. if res.warnings != nil { fmt.Fprintln(out, "\nPointer analysis warnings:") for _, w := range res.warnings { printf(w.Pos, "warning: "+w.Message) } } } // ---------- Utilities ---------- // buildSSA constructs the SSA representation of Go-source function bodies. // Not needed in simpler modes, e.g. freevars. // func buildSSA(o *Oracle) { start := time.Now() o.prog.BuildAll() o.timers["SSA-build"] = time.Since(start) } // ptrAnalysis runs the pointer analysis and returns its result. func ptrAnalysis(o *Oracle) *pointer.Result { start := time.Now() result := pointer.Analyze(&o.config) o.timers["pointer analysis"] = time.Since(start) return result } // parseOctothorpDecimal returns the numeric value if s matches "#%d", // otherwise -1. func parseOctothorpDecimal(s string) int { if s != "" && s[0] == '#' { if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil { return int(s) } } return -1 } // parseQueryPos parses a string of the form "file:pos" or // file:start,end" where pos, start, end match #%d and represent byte // offsets, and returns the extent to which it refers. // // (Numbers without a '#' prefix are reserved for future use, // e.g. to indicate line/column positions.) // func parseQueryPos(fset *token.FileSet, queryPos string) (start, end token.Pos, err error) { if queryPos == "" { err = fmt.Errorf("no source position specified (-pos flag)") return } colon := strings.LastIndex(queryPos, ":") if colon < 0 { err = fmt.Errorf("invalid source position -pos=%q", queryPos) return } filename, offset := queryPos[:colon], queryPos[colon+1:] startOffset := -1 endOffset := -1 if hyphen := strings.Index(offset, ","); hyphen < 0 { // e.g. "foo.go:#123" startOffset = parseOctothorpDecimal(offset) endOffset = startOffset } else { // e.g. "foo.go:#123,#456" startOffset = parseOctothorpDecimal(offset[:hyphen]) endOffset = parseOctothorpDecimal(offset[hyphen+1:]) } if startOffset < 0 || endOffset < 0 { err = fmt.Errorf("invalid -pos offset %q", offset) return } var file *token.File fset.Iterate(func(f *token.File) bool { if sameFile(filename, f.Name()) { // (f.Name() is absolute) file = f return false // done } return true // continue }) if file == nil { err = fmt.Errorf("couldn't find file containing position -pos=%q", queryPos) return } // Range check [start..end], inclusive of both end-points. if 0 <= startOffset && startOffset <= file.Size() { start = file.Pos(int(startOffset)) } else { err = fmt.Errorf("start position is beyond end of file -pos=%q", queryPos) return } if 0 <= endOffset && endOffset <= file.Size() { end = file.Pos(int(endOffset)) } else { err = fmt.Errorf("end position is beyond end of file -pos=%q", queryPos) return } return } // sameFile returns true if x and y have the same basename and denote // the same file. // func sameFile(x, y string) bool { 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 } // unparen returns e with any enclosing parentheses stripped. func unparen(e ast.Expr) ast.Expr { for { p, ok := e.(*ast.ParenExpr) if !ok { break } e = p.X } return 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 (o *Oracle) fprintf(w io.Writer, 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 := o.prog.Fset.Position(start); start == end { // (prints "-: " for token.NoPos) fmt.Fprintf(w, "%s: ", sp) } else { ep := o.prog.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") } // printNode returns the pretty-printed syntax of n. func (o *Oracle) printNode(n ast.Node) string { var buf bytes.Buffer printer.Fprint(&buf, o.prog.Fset, n) return buf.String() } ./oracle/testdata/0000755000014500017510000000000012246613010013551 5ustar michaelstaff./oracle/testdata/src/0000755000014500017510000000000012246613010014340 5ustar michaelstaff./oracle/testdata/src/lib/0000755000014500017510000000000012246613010015106 5ustar michaelstaff./oracle/testdata/src/lib/lib.go0000644000014500017510000000017112246613010016202 0ustar michaelstaffpackage lib type Type int func (Type) Method(x *int) *int { return x } func Func() { } const Const = 3 var Var = 0 ./oracle/testdata/src/main/0000755000014500017510000000000012246613010015264 5ustar michaelstaff./oracle/testdata/src/main/callgraph-json.go0000644000014500017510000000133412246613010020520 0ustar michaelstaffpackage main // Tests of call-graph queries, -format=json. // See go.tools/oracle/oracle_test.go for explanation. // See callgraph-json.golden for expected query results. func A() {} func B() {} // call is not (yet) treated context-sensitively. func call(f func()) { f() } // nop *is* treated context-sensitively. func nop() {} func call2(f func()) { f() f() } func main() { call(A) call(B) nop() nop() call2(func() { // called twice from main.call2, // but call2 is not context sensitive (yet). }) print("builtin") _ = string("type conversion") call(nil) if false { main() } var nilFunc func() nilFunc() var i interface { f() } i.f() } func deadcode() { main() } // @callgraph callgraph "^" ./oracle/testdata/src/main/implements.golden0000644000014500017510000000021512246613010020631 0ustar michaelstaff-------- @implements impl -------- Interface main.E: main.C main.D Interface main.F: *main.C main.D Interface main.FG: *main.D ./oracle/testdata/src/main/calls-json.go0000644000014500017510000000052112246613010017656 0ustar michaelstaffpackage main // Tests of call-graph queries, -format=json. // See go.tools/oracle/oracle_test.go for explanation. // See calls-json.golden for expected query results. func call(f func()) { f() // @callees @callees-f "f" } func main() { call(func() { // @callers callers-main.anon "^" // @callstack callstack-main.anon "^" }) } ./oracle/testdata/src/main/reflection.go0000644000014500017510000000117312246613010017747 0ustar michaelstaffpackage reflection // This is a test of 'describe', but we split it into a separate file // so that describe.go doesn't have to import "reflect" each time. import "reflect" var a int var b bool func main() { m := make(map[*int]*bool) m[&a] = &b mrv := reflect.ValueOf(m) if a > 0 { mrv = reflect.ValueOf(&b) } if a > 0 { mrv = reflect.ValueOf(&a) } _ = mrv // @describe mrv "mrv" p1 := mrv.Interface() // @describe p1 "p1" p2 := mrv.MapKeys() // @describe p2 "p2" p3 := p2[0] // @describe p3 "p3" p4 := reflect.TypeOf(p1) // @describe p4 "p4" _, _, _, _ = p1, p2, p3, p4 } ./oracle/testdata/src/main/describe.go0000644000014500017510000000505212246613010017375 0ustar michaelstaffpackage describe // @describe pkgdecl "describe" // Tests of 'describe' query. // See go.tools/oracle/oracle_test.go for explanation. // See describe.golden for expected query results. // TODO(adonovan): more coverage of the (extensive) logic. type cake float64 // @describe type-ref-builtin "float64" const c = iota // @describe const-ref-iota "iota" const pi = 3.141 // @describe const-def-pi "pi" const pie = cake(pi) // @describe const-def-pie "pie" const _ = pi // @describe const-ref-pi "pi" var global = new(string) // NB: ssa.Global is indirect, i.e. **string func main() { // @describe func-def-main "main" // func objects _ = main // @describe func-ref-main "main" _ = (*C).f // @describe func-ref-*C.f "..C..f" _ = D.f // @describe func-ref-D.f "D.f" _ = I.f // @describe func-ref-I.f "I.f" var d D // @describe type-D "D" var i I // @describe type-I "I" _ = d.f // @describe func-ref-d.f "d.f" _ = i.f // @describe func-ref-i.f "i.f" // var objects anon := func() { _ = d // @describe ref-lexical-d "d" } _ = anon // @describe ref-anon "anon" _ = global // @describe ref-global "global" // SSA affords some local flow sensitivity. var a, b int var x = &a // @describe var-def-x-1 "x" _ = x // @describe var-ref-x-1 "x" x = &b // @describe var-def-x-2 "x" _ = x // @describe var-ref-x-2 "x" i = new(C) // @describe var-ref-i-C "i" if i != nil { i = D{} // @describe var-ref-i-D "i" } print(i) // @describe var-ref-i "\\bi\\b" // const objects const localpi = 3.141 // @describe const-local-pi "localpi" const localpie = cake(pi) // @describe const-local-pie "localpie" const _ = localpi // @describe const-ref-localpi "localpi" // type objects type T int // @describe type-def-T "T" var three T = 3 // @describe type-ref-T "T" _ = three print(1 + 2*3) // @describe const-expr " 2.3" print(real(1+2i) - 3) // @describe const-expr2 "real.*3" m := map[string]*int{"a": &a} mapval, _ := m["a"] // @describe map-lookup,ok "m..a.." _ = mapval // @describe mapval "mapval" _ = m // @describe m "m" defer main() // @describe defer-stmt "defer" go main() // @describe go-stmt "go" panic(3) // @describe builtin-ref-panic "panic" } func deadcode() { var a int // @describe var-decl-stmt "var a int" // Pointer analysis can't run on dead code. var b = &a // @describe b "b" _ = b } type I interface { // @describe def-iface-I "I" f() // @describe def-imethod-I.f "f" } type C int type D struct{} func (c *C) f() {} func (d D) f() {} ./oracle/testdata/src/main/describe-json.go0000644000014500017510000000113512246613010020342 0ustar michaelstaffpackage describe // @describe pkgdecl "describe" // @implements implements "^" // Tests of 'describe' and 'implements' queries, -format=json. // See go.tools/oracle/oracle_test.go for explanation. // See describe-json.golden for expected query results. func main() { // var s struct{ x [3]int } p := &s.x[0] // @describe desc-val-p "p" _ = p var i I = C(0) if i == nil { i = new(D) } print(i) // @describe desc-val-i "\\bi\\b" go main() // @describe desc-stmt "go" } type I interface { f() } type C int // @describe desc-type-C "C" type D struct{} func (c C) f() {} func (d *D) f() {} ./oracle/testdata/src/main/referrers-json.golden0000644000014500017510000000261212246613010021425 0ustar michaelstaff-------- @referrers ref-package -------- { "mode": "referrers", "referrers": { "pos": "testdata/src/main/referrers-json.go:14:8", "objpos": "testdata/src/main/referrers-json.go:7:8", "desc": "package lib", "refs": [ "testdata/src/main/referrers-json.go:14:8", "testdata/src/main/referrers-json.go:14:19" ] } }-------- @referrers ref-method -------- { "mode": "referrers", "referrers": { "pos": "testdata/src/main/referrers-json.go:15:8", "objpos": "testdata/src/lib/lib.go:5:13", "desc": "func (lib.Type).Method(x *int) *int", "refs": [ "testdata/src/main/referrers-json.go:15:8", "testdata/src/main/referrers-json.go:16:8" ] } }-------- @referrers ref-local -------- { "mode": "referrers", "referrers": { "pos": "testdata/src/main/referrers-json.go:17:2", "objpos": "testdata/src/main/referrers-json.go:14:6", "desc": "var v lib.Type", "refs": [ "testdata/src/main/referrers-json.go:15:6", "testdata/src/main/referrers-json.go:16:6", "testdata/src/main/referrers-json.go:17:2", "testdata/src/main/referrers-json.go:18:2" ] } }-------- @referrers ref-field -------- { "mode": "referrers", "referrers": { "pos": "testdata/src/main/referrers-json.go:20:10", "objpos": "testdata/src/main/referrers-json.go:10:2", "desc": "field f int", "refs": [ "testdata/src/main/referrers-json.go:20:10", "testdata/src/main/referrers-json.go:23:5" ] } }./oracle/testdata/src/main/imports.golden0000644000014500017510000000176412246613010020163 0ustar michaelstaff-------- @describe ref-pkg-import -------- import of package "lib" const Const untyped integer = 3 func Func func() type Type int method (lib.Type) Method(x *int) *int var Var int -------- @describe ref-const -------- reference to const lib.Const untyped integer defined here -------- @describe ref-func -------- reference to func lib.Func() defined here -------- @describe ref-var -------- reference to var lib.Var int defined here -------- @describe ref-type -------- reference to type lib.Type (size 8, align 8) defined as int Method set: method (lib.Type) Method(x *int) *int -------- @describe ref-method -------- reference to method func (lib.Type).Method(x *int) *int defined here -------- @describe p -------- reference to var p *int defined here value may point to these labels: imports.a -------- @describe ref-pkg -------- reference to package "lib" const Const untyped integer = 3 func Func func() type Type int method (lib.Type) Method(x *int) *int var Var int ./oracle/testdata/src/main/describe.golden0000644000014500017510000001064212246613010020241 0ustar michaelstaff-------- @describe pkgdecl -------- definition of package "describe" type C int method (*describe.C) f() type D struct{} method (describe.D) f() type I interface{f()} method (describe.I) f() const c untyped integer = 0 type cake float64 func deadcode func() var global *string func main func() const pi untyped float = 3141/1000 const pie cake = 1768225803696341/562949953421312 -------- @describe type-ref-builtin -------- reference to built-in type float64 -------- @describe const-ref-iota -------- reference to const iota untyped integer of constant value 0 -------- @describe const-def-pi -------- definition of const pi untyped float -------- @describe const-def-pie -------- definition of const pie cake -------- @describe const-ref-pi -------- reference to const pi untyped float of constant value 3141/1000 defined here -------- @describe func-def-main -------- definition of func main() -------- @describe func-ref-main -------- reference to func main() defined here -------- @describe func-ref-*C.f -------- reference to method func (*C).f() defined here -------- @describe func-ref-D.f -------- reference to method func (D).f() defined here -------- @describe func-ref-I.f -------- reference to interface method func (I).f() defined here -------- @describe type-D -------- reference to type D (size 0, align 1) defined as struct{} Method set: method (D) f() -------- @describe type-I -------- reference to type I (size 16, align 8) defined as interface{f()} Method set: method (I) f() -------- @describe func-ref-d.f -------- reference to method func (D).f() defined here -------- @describe func-ref-i.f -------- reference to interface method func (I).f() defined here -------- @describe ref-lexical-d -------- reference to var d D defined here -------- @describe ref-anon -------- reference to var anon func() defined here value may point to these labels: func@31.10 -------- @describe ref-global -------- reference to var global *string defined here value may point to these labels: new -------- @describe var-def-x-1 -------- definition of var x *int value may point to these labels: a -------- @describe var-ref-x-1 -------- reference to var x *int defined here value may point to these labels: a -------- @describe var-def-x-2 -------- reference to var x *int defined here value may point to these labels: b -------- @describe var-ref-x-2 -------- reference to var x *int defined here value may point to these labels: b -------- @describe var-ref-i-C -------- reference to var i I defined here this I may contain these dynamic types: *C, may point to: new -------- @describe var-ref-i-D -------- reference to var i I defined here this I may contain these dynamic types: D -------- @describe var-ref-i -------- reference to var i I defined here this I may contain these dynamic types: *C, may point to: new D -------- @describe const-local-pi -------- definition of const localpi untyped float -------- @describe const-local-pie -------- definition of const localpie cake -------- @describe const-ref-localpi -------- reference to const localpi untyped float of constant value 3141/1000 defined here -------- @describe type-def-T -------- definition of type T (size 8, align 8) No methods. -------- @describe type-ref-T -------- reference to type T (size 8, align 8) defined as int No methods. -------- @describe const-expr -------- binary * operation of constant value 6 -------- @describe const-expr2 -------- binary - operation of constant value -2 -------- @describe map-lookup,ok -------- index expression of type (*int, bool) -------- @describe mapval -------- reference to var mapval *int defined here value may point to these labels: a -------- @describe m -------- reference to var m map[string]*int defined here value may point to these labels: makemap -------- @describe defer-stmt -------- defer statement -------- @describe go-stmt -------- go statement -------- @describe builtin-ref-panic -------- function call (or conversion) of type () -------- @describe var-decl-stmt -------- definition of var a int -------- @describe b -------- definition of var b *int no points-to information: PTA did not encounter this expression (dead code?) -------- @describe def-iface-I -------- definition of type I (size 16, align 8) Method set: method (I) f() -------- @describe def-imethod-I.f -------- type interface{f()} Method set: method (interface{f()}) f() ./oracle/testdata/src/main/peers-json.go0000644000014500017510000000042312246613010017677 0ustar michaelstaffpackage peers // Tests of channel 'peers' query, -format=json. // See go.tools/oracle/oracle_test.go for explanation. // See peers-json.golden for expected query results. func main() { chA := make(chan *int) <-chA select { case <-chA: // @peers peer-recv-chA "<-" } } ./oracle/testdata/src/main/callgraph2.golden0000644000014500017510000000046412246613010020501 0ustar michaelstaff-------- @callgraph callgraph -------- Below is a call graph of the entire program. The numbered nodes form a spanning tree. Non-numbered nodes indicate back- or cross-edges to the node whose number follows in parentheses. 0 1 main.init 2 reflect.init 3 main.main 4 main.f ./oracle/testdata/src/main/callgraph-json.golden0000644000014500017510000000176512246613010021373 0ustar michaelstaff-------- @callgraph callgraph -------- { "mode": "callgraph", "callgraph": [ { "name": "\u003croot\u003e", "pos": "-", "children": [ 1, 2 ] }, { "name": "main.init", "pos": "-" }, { "name": "main.main", "pos": "testdata/src/main/callgraph-json.go:24:6", "children": [ 3, 6, 7, 8 ] }, { "name": "main.call", "pos": "testdata/src/main/callgraph-json.go:12:6", "children": [ 4, 5 ] }, { "name": "main.A", "pos": "testdata/src/main/callgraph-json.go:7:6" }, { "name": "main.B", "pos": "testdata/src/main/callgraph-json.go:9:6" }, { "name": "main.nop", "pos": "testdata/src/main/callgraph-json.go:17:6" }, { "name": "main.nop", "pos": "testdata/src/main/callgraph-json.go:17:6" }, { "name": "main.call2", "pos": "testdata/src/main/callgraph-json.go:19:6", "children": [ 9 ] }, { "name": "func@31.8", "pos": "testdata/src/main/callgraph-json.go:31:8" } ] }./oracle/testdata/src/main/calls.go0000644000014500017510000000425212246613010016714 0ustar michaelstaffpackage main // Tests of call-graph queries. // See go.tools/oracle/oracle_test.go for explanation. // See calls.golden for expected query results. func A(x *int) { // @describe describe-A-x "x" // @callers callers-A "^" // @callstack callstack-A "^" } func B(x *int) { // @describe describe-B-x "x" // @callers callers-B "^" } // apply is not (yet) treated context-sensitively. func apply(f func(x *int), x *int) { f(x) // @callees callees-apply "f" // @callers callers-apply "^" } // store *is* treated context-sensitively, // so the points-to sets for pc, pd are precise. func store(ptr **int, value *int) { *ptr = value // @callers callers-store "^" } func call(f func() *int) { // Result points to anon function. f() // @describe describe-result-f "f" // Target of call is anon function. f() // @callees callees-main.call-f "f" // @callers callers-main.call "^" } func main() { var a, b int apply(A, &a) // @callees callees-main-apply1 "app" apply(B, &b) var c, d int var pc, pd *int // @describe describe-pc "pc" store(&pc, &c) store(&pd, &d) _ = pd // @describe describe-pd "pd" call(func() *int { // We are called twice from main.call // @callers callers-main.anon "^" return &a }) // Errors _ = "no function call here" // @callees callees-err-no-call "no" print("builtin") // @callees callees-err-builtin "builtin" _ = string("type conversion") // @callees callees-err-conversion "str" call(nil) // @callees callees-err-bad-selection "call\\(nil" if false { main() // @callees callees-err-deadcode1 "main" } var nilFunc func() nilFunc() // @callees callees-err-nil-func "nilFunc" var i interface { f() } i.f() // @callees callees-err-nil-interface "i.f" } func deadcode() { main() // @callees callees-err-deadcode2 "main" // @callers callers-err-deadcode "^" // @callstack callstack-err-deadcode "^" } // This code belongs to init. var global = 123 // @callers callers-global "global" // The package initializer may be called by other packages' inits, or // in this case, the root of the callgraph. The source-level init functions // are in turn called by it. func init() { // @callstack callstack-init "^" } ./oracle/testdata/src/main/peers.golden0000644000014500017510000000273512246613010017603 0ustar michaelstaff-------- @describe describe-chA -------- reference to var chA chan *int defined here value may point to these labels: makechan makechan -------- @describe describe-chA2 -------- reference to var chA2 chan *int defined here value may point to these labels: makechan -------- @describe describe-chB -------- reference to var chB chan *int defined here value may point to these labels: makechan -------- @peers peer-recv-chA -------- This channel of type chan *int may be: allocated here allocated here sent to, here sent to, here received from, here received from, here received from, here received from, here received from, here -------- @describe describe-rA -------- reference to var rA *int defined here value may point to these labels: peers.a2 a1 -------- @peers peer-recv-chB -------- This channel of type chan *int may be: allocated here sent to, here received from, here received from, here -------- @describe describe-rB -------- reference to var rB *int defined here value may point to these labels: b -------- @peers peer-recv-chA' -------- This channel of type chan *int may be: allocated here allocated here sent to, here sent to, here received from, here received from, here received from, here received from, here received from, here -------- @peers peer-send-chA' -------- This channel of type chan *int may be: allocated here sent to, here received from, here received from, here received from, here received from, here received from, here ./oracle/testdata/src/main/calls-json.golden0000644000014500017510000000127212246613010020525 0ustar michaelstaff-------- @callees @callees-f -------- { "mode": "callees", "callees": { "pos": "testdata/src/main/calls-json.go:8:3", "desc": "dynamic function call", "callees": [ { "name": "func@12.7", "pos": "testdata/src/main/calls-json.go:12:7" } ] } }-------- @callstack callstack-main.anon -------- { "mode": "callstack", "callstack": { "pos": "testdata/src/main/calls-json.go:12:7", "target": "func@12.7", "callers": [ { "pos": "testdata/src/main/calls-json.go:8:3", "desc": "dynamic function call", "caller": "main.call" }, { "pos": "testdata/src/main/calls-json.go:12:6", "desc": "static function call", "caller": "main.main" } ] } }./oracle/testdata/src/main/implements.go0000644000014500017510000000057412246613010017776 0ustar michaelstaffpackage main // Tests of 'implements' query. // See go.tools/oracle/oracle_test.go for explanation. // See implements.golden for expected query results. // @implements impl "" func main() { } type E interface{} type F interface { f() } type FG interface { f() g() int } type C int type D struct{} func (c *C) f() {} func (d D) f() {} func (d *D) g() int { return 0 } ./oracle/testdata/src/main/callgraph.go0000644000014500017510000000131112246613010017544 0ustar michaelstaffpackage main // Tests of call-graph queries. // See go.tools/oracle/oracle_test.go for explanation. // See callgraph.golden for expected query results. func A() {} func B() {} // call is not (yet) treated context-sensitively. func call(f func()) { f() } // nop *is* treated context-sensitively. func nop() {} func call2(f func()) { f() f() } func main() { call(A) call(B) nop() nop() call2(func() { // called twice from main.call2, // but call2 is not context sensitive (yet). }) print("builtin") _ = string("type conversion") call(nil) if false { main() } var nilFunc func() nilFunc() var i interface { f() } i.f() } func deadcode() { main() } // @callgraph callgraph "^" ./oracle/testdata/src/main/freevars.go0000644000014500017510000000136212246613010017432 0ustar michaelstaffpackage main // Tests of 'freevars' query. // See go.tools/oracle/oracle_test.go for explanation. // See freevars.golden for expected query results. // TODO(adonovan): it's hard to test this query in a single line of gofmt'd code. type T struct { a, b int } type S struct { x int t T } func f(int) {} func main() { type C int x := 1 const exp = 6 if y := 2; x+y+int(C(3)) != exp { // @freevars fv1 "if.*{" panic("expected 6") } var s S for x, y := range "foo" { println(s.x + s.t.a + s.t.b + x + int(y)) // @freevars fv2 "print.*y." } f(x) // @freevars fv3 "f.x." // TODO(adonovan): enable when go/types supports labels. loop: // #@freevars fv-def-label "loop:" for { break loop // #@freevars fv-ref-label "break loop" } } ./oracle/testdata/src/main/peers.go0000644000014500017510000000137012246613010016732 0ustar michaelstaffpackage peers // Tests of channel 'peers' query. // See go.tools/oracle/oracle_test.go for explanation. // See peers.golden for expected query results. var a2 int func main() { chA := make(chan *int) a1 := 1 chA <- &a1 chA2 := make(chan *int, 2) if a2 == 0 { chA = chA2 } chB := make(chan *int) b := 3 chB <- &b <-chA // @describe describe-chA "chA" <-chA2 // @describe describe-chA2 "chA2" <-chB // @describe describe-chB "chB" select { case rA := <-chA: // @peers peer-recv-chA "<-" _ = rA // @describe describe-rA "rA" case rB := <-chB: // @peers peer-recv-chB "<-" _ = rB // @describe describe-rB "rB" case <-chA: // @peers peer-recv-chA' "<-" case chA2 <- &a2: // @peers peer-send-chA' "<-" } for _ = range chA { } } ./oracle/testdata/src/main/freevars.golden0000644000014500017510000000040712246613010020274 0ustar michaelstaff-------- @freevars fv1 -------- Free identifiers: type C main.C const exp int var x int -------- @freevars fv2 -------- Free identifiers: var s.t.a int var s.t.b int var s.x int var x int var y int32 -------- @freevars fv3 -------- Free identifiers: var x int ./oracle/testdata/src/main/referrers-json.go0000644000014500017510000000070012246613010020556 0ustar michaelstaffpackage referrers // Tests of 'referrers' query. // See go.tools/oracle/oracle_test.go for explanation. // See referrers.golden for expected query results. import "lib" type s struct { f int } func main() { var v lib.Type = lib.Const // @referrers ref-package "lib" _ = v.Method // @referrers ref-method "Method" _ = v.Method v++ //@referrers ref-local "v" v++ _ = s{}.f // @referrers ref-field "f" var s2 s s2.f = 1 } ./oracle/testdata/src/main/peers-json.golden0000644000014500017510000000047012246613010020544 0ustar michaelstaff-------- @peers peer-recv-chA -------- { "mode": "peers", "peers": { "pos": "testdata/src/main/peers-json.go:11:7", "type": "chan *int", "allocs": [ "testdata/src/main/peers-json.go:8:13" ], "receives": [ "testdata/src/main/peers-json.go:9:2", "testdata/src/main/peers-json.go:11:7" ] } }./oracle/testdata/src/main/calls.golden0000644000014500017510000000530512246613010017557 0ustar michaelstaff-------- @describe describe-A-x -------- definition of var x *int value may point to these labels: a b -------- @callstack callstack-A -------- Found a call path from root to main.A main.A dynamic function call from main.apply static function call from main.main -------- @describe describe-B-x -------- definition of var x *int value may point to these labels: a b -------- @callers callers-B -------- main.B is called from these 1 sites: dynamic function call from main.apply -------- @callees callees-apply -------- this dynamic function call dispatches to: main.A main.B -------- @callers callers-apply -------- main.apply is called from these 2 sites: static function call from main.main static function call from main.main -------- @callers callers-store -------- main.store is called from these 2 sites: static function call from main.main static function call from main.main -------- @describe describe-result-f -------- reference to var f func() *int defined here value may point to these labels: func@50.7 -------- @callees callees-main.call-f -------- this dynamic function call dispatches to: func@50.7 -------- @callers callers-main.call -------- main.call is called from these 2 sites: static function call from main.main static function call from main.main -------- @callees callees-main-apply1 -------- this static function call dispatches to: main.apply -------- @describe describe-pc -------- definition of var pc *int value may point to these labels: c -------- @describe describe-pd -------- reference to var pd *int defined here value may point to these labels: d -------- @callees callees-err-no-call -------- Error: there is no function call here -------- @callees callees-err-builtin -------- Error: this is a call to the built-in 'print' operator -------- @callees callees-err-conversion -------- Error: this is a type conversion, not a function call -------- @callees callees-err-bad-selection -------- Error: ambiguous selection within function call (or conversion) -------- @callees callees-err-deadcode1 -------- Error: this call site is unreachable in this analysis -------- @callees callees-err-nil-func -------- dynamic function call on nil value -------- @callees callees-err-nil-interface -------- dynamic method call on nil value -------- @callees callees-err-deadcode2 -------- Error: this function is unreachable in this analysis -------- @callstack callstack-err-deadcode -------- main.deadcode is unreachable in this analysis scope -------- @callers callers-global -------- main.init is called from these 1 sites: the root of the call graph -------- @callstack callstack-init -------- Found a call path from root to main.init$1 main.init$1 static function call from main.init ./oracle/testdata/src/main/callgraph2.go0000644000014500017510000000055612246613010017640 0ustar michaelstaffpackage main // Tests of call-graph queries. // See go.tools/oracle/oracle_test.go for explanation. // See callgraph2.golden for expected query results. // (Regression test for pointer analysis: programs that use reflection // create some cgnodes before the root of the callgraph.) import _ "reflect" func f() {} func main() { f() } // @callgraph callgraph "^" ./oracle/testdata/src/main/multi.go0000644000014500017510000000020312246613010016740 0ustar michaelstaffpackage multi func g(x int) { } func f() { x := 1 g(x) // "g(x)" is the selection for multiple queries } func main() { f() } ./oracle/testdata/src/main/callgraph.golden0000644000014500017510000000061212246613010020412 0ustar michaelstaff-------- @callgraph callgraph -------- Below is a call graph of the entire program. The numbered nodes form a spanning tree. Non-numbered nodes indicate back- or cross-edges to the node whose number follows in parentheses. 0 1 main.init 2 main.main 3 main.call 4 main.A 5 main.B 6 main.call2 7 func@31.8 8 main.nop ./oracle/testdata/src/main/describe-json.golden0000644000014500017510000000623412246613010021212 0ustar michaelstaff-------- @describe pkgdecl -------- { "mode": "describe", "describe": { "desc": "definition of package \"describe\"", "pos": "testdata/src/main/describe-json.go:1:9", "detail": "package", "package": { "path": "describe", "members": [ { "name": "C", "type": "int", "pos": "testdata/src/main/describe-json.go:27:6", "kind": "type", "methods": [ { "name": "method (C) f()", "pos": "testdata/src/main/describe-json.go:30:12" } ] }, { "name": "D", "type": "struct{}", "pos": "testdata/src/main/describe-json.go:28:6", "kind": "type", "methods": [ { "name": "method (*D) f()", "pos": "testdata/src/main/describe-json.go:31:13" } ] }, { "name": "I", "type": "interface{f()}", "pos": "testdata/src/main/describe-json.go:23:6", "kind": "type", "methods": [ { "name": "method (I) f()", "pos": "testdata/src/main/describe-json.go:24:2" } ] }, { "name": "main", "type": "func()", "pos": "testdata/src/main/describe-json.go:9:6", "kind": "func" } ] } } }-------- @implements implements -------- { "mode": "implements", "implements": [ { "i": "describe.I", "ipos": "testdata/src/main/describe-json.go:23:6", "c": "describe.C", "cpos": "testdata/src/main/describe-json.go:27:6" }, { "i": "describe.I", "ipos": "testdata/src/main/describe-json.go:23:6", "c": "*describe.D", "cpos": "testdata/src/main/describe-json.go:28:6" } ] }-------- @describe desc-val-p -------- { "mode": "describe", "describe": { "desc": "identifier", "pos": "testdata/src/main/describe-json.go:11:2", "detail": "value", "value": { "type": "*int", "objpos": "testdata/src/main/describe-json.go:11:2", "pts": [ { "type": "*int", "labels": [ { "pos": "testdata/src/main/describe-json.go:10:6", "desc": "s.x[*]" } ] } ] } } }-------- @describe desc-val-i -------- { "mode": "describe", "describe": { "desc": "identifier", "pos": "testdata/src/main/describe-json.go:18:8", "detail": "value", "value": { "type": "I", "objpos": "testdata/src/main/describe-json.go:14:6", "pts": [ { "type": "*D", "namepos": "testdata/src/main/describe-json.go:28:6", "labels": [ { "pos": "testdata/src/main/describe-json.go:16:10", "desc": "new" } ] }, { "type": "C", "namepos": "testdata/src/main/describe-json.go:27:6" } ] } } }-------- @describe desc-stmt -------- { "mode": "describe", "describe": { "desc": "go statement", "pos": "testdata/src/main/describe-json.go:20:2", "detail": "unknown" } }-------- @describe desc-type-C -------- { "mode": "describe", "describe": { "desc": "definition of type C (size 8, align 8)", "pos": "testdata/src/main/describe-json.go:27:6", "detail": "type", "type": { "type": "C", "namepos": "testdata/src/main/describe-json.go:27:6", "namedef": "int", "methods": [ { "name": "method (C) f()", "pos": "testdata/src/main/describe-json.go:30:12" } ] } } }./oracle/testdata/src/main/reflection.golden0000644000014500017510000000166512246613010020620 0ustar michaelstaff-------- @describe mrv -------- reference to var mrv reflect.Value defined here this reflect.Value may contain these dynamic types: *bool, may point to: reflection.b *int, may point to: reflection.a map[*int]*bool, may point to: makemap -------- @describe p1 -------- definition of var p1 interface{} this interface{} may contain these dynamic types: *bool, may point to: reflection.b *int, may point to: reflection.a map[*int]*bool, may point to: makemap -------- @describe p2 -------- definition of var p2 []reflect.Value value may point to these labels: -------- @describe p3 -------- definition of var p3 reflect.Value this reflect.Value may contain these dynamic types: *int, may point to: reflection.a -------- @describe p4 -------- definition of var p4 reflect.Type this reflect.Type may contain these dynamic types: *reflect.rtype, may point to: *bool *int map[*int]*bool ./oracle/testdata/src/main/imports.go0000644000014500017510000000134512246613010017313 0ustar michaelstaffpackage imports import ( "lib" // @describe ref-pkg-import "lib" ) // Tests that import another package. (To make the tests run quickly, // we avoid using imports in all the other tests. Remember, each // query causes parsing and typechecking of the whole program.) // // See go.tools/oracle/oracle_test.go for explanation. // See imports.golden for expected query results. var a int func main() { const c = lib.Const // @describe ref-const "Const" lib.Func() // @describe ref-func "Func" lib.Var++ // @describe ref-var "Var" var t lib.Type // @describe ref-type "Type" p := t.Method(&a) // @describe ref-method "Method" print(*p + 1) // @describe p "p " var _ lib.Type // @describe ref-pkg "lib" } ./oracle/oracle_test.go0000644000014500017510000001720612246613010014601 0ustar michaelstaff// 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 oracle_test // This file defines a test framework for oracle queries. // // The files beneath testdata/src/main contain Go programs containing // query annotations of the form: // // @verb id "select" // // where verb is the query mode (e.g. "callers"), id is a unique name // for this query, and "select" is a regular expression matching the // substring of the current line that is the query's input selection. // // The expected output for each query is provided in the accompanying // .golden file. // // (Location information is not included because it's too fragile to // display as text. TODO(adonovan): think about how we can test its // correctness, since it is critical information.) // // Run this test with: // % go test code.google.com/p/go.tools/oracle -update // to update the golden files. import ( "bytes" "encoding/json" "flag" "fmt" "go/build" "go/parser" "go/token" "io" "io/ioutil" "os" "os/exec" "regexp" "runtime" "strconv" "strings" "testing" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/oracle" ) var updateFlag = flag.Bool("update", false, "Update the golden files.") type query struct { id string // unique id verb string // query mode, e.g. "callees" posn token.Position // position of of query filename string start, end int // selection of file to pass to oracle } func parseRegexp(text string) (*regexp.Regexp, error) { pattern, err := strconv.Unquote(text) if err != nil { return nil, fmt.Errorf("can't unquote %s", text) } return regexp.Compile(pattern) } // parseQueries parses and returns the queries in the named file. func parseQueries(t *testing.T, filename string) []*query { filedata, err := ioutil.ReadFile(filename) if err != nil { t.Fatal(err) } // Parse the file once to discover the test queries. var fset token.FileSet f, err := parser.ParseFile(&fset, filename, filedata, parser.ParseComments) if err != nil { t.Fatal(err) } lines := bytes.Split(filedata, []byte("\n")) var queries []*query queriesById := make(map[string]*query) // Find all annotations of these forms: expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp" for _, c := range f.Comments { text := strings.TrimSpace(c.Text()) if text == "" || text[0] != '@' { continue } posn := fset.Position(c.Pos()) // @verb id "regexp" match := expectRe.FindStringSubmatch(text) if match == nil { t.Errorf("%s: ill-formed query: %s", posn, text) continue } id := match[2] if prev, ok := queriesById[id]; ok { t.Errorf("%s: duplicate id %s", posn, id) t.Errorf("%s: previously used here", prev.posn) continue } selectRe, err := parseRegexp(match[3]) if err != nil { t.Errorf("%s: %s", posn, err) continue } // Find text of the current line, sans query. // (Queries must be // not /**/ comments.) line := lines[posn.Line-1][:posn.Column-1] // Apply regexp to current line to find input selection. loc := selectRe.FindIndex(line) if loc == nil { t.Errorf("%s: selection pattern %s doesn't match line %q", posn, match[3], string(line)) continue } // Assumes ASCII. TODO(adonovan): test on UTF-8. linestart := posn.Offset - (posn.Column - 1) // Compute the file offsets q := &query{ id: id, verb: match[1], posn: posn, filename: filename, start: linestart + loc[0], end: linestart + loc[1], } queries = append(queries, q) queriesById[id] = q } // Return the slice, not map, for deterministic iteration. return queries } // WriteResult writes res (-format=plain) to w, stripping file locations. func WriteResult(w io.Writer, res *oracle.Result) { capture := new(bytes.Buffer) // capture standard output res.WriteTo(capture) for _, line := range strings.Split(capture.String(), "\n") { // Remove a "file:line: " prefix. if i := strings.Index(line, ": "); i >= 0 { line = line[i+2:] } fmt.Fprintf(w, "%s\n", line) } } // doQuery poses query q to the oracle and writes its response and // error (if any) to out. func doQuery(out io.Writer, q *query, useJson bool) { fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id) var buildContext = build.Default buildContext.GOPATH = "testdata" res, err := oracle.Query([]string{q.filename}, q.verb, fmt.Sprintf("%s:#%d,#%d", q.filename, q.start, q.end), nil, // ptalog, &buildContext, true) // reflection if err != nil { fmt.Fprintf(out, "\nError: %s\n", err) return } if useJson { // JSON output b, err := json.MarshalIndent(res.Serial(), "", "\t") if err != nil { fmt.Fprintf(out, "JSON error: %s\n", err.Error()) return } out.Write(b) } else { // "plain" (compiler diagnostic format) output WriteResult(out, res) } } func TestOracle(t *testing.T) { switch runtime.GOOS { case "windows": t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS) } for _, filename := range []string{ "testdata/src/main/calls.go", "testdata/src/main/callgraph.go", "testdata/src/main/callgraph2.go", "testdata/src/main/describe.go", "testdata/src/main/freevars.go", "testdata/src/main/implements.go", "testdata/src/main/imports.go", "testdata/src/main/peers.go", "testdata/src/main/reflection.go", // JSON: "testdata/src/main/callgraph-json.go", "testdata/src/main/calls-json.go", "testdata/src/main/peers-json.go", "testdata/src/main/describe-json.go", "testdata/src/main/referrers-json.go", } { useJson := strings.HasSuffix(filename, "-json.go") queries := parseQueries(t, filename) golden := filename + "lden" got := filename + "t" gotfh, err := os.Create(got) if err != nil { t.Errorf("Create(%s) failed: %s", got, err) continue } defer gotfh.Close() // Run the oracle on each query, redirecting its output // and error (if any) to the foo.got file. for _, q := range queries { doQuery(gotfh, q, useJson) } // Compare foo.got with foo.golden. cmd := exec.Command("/usr/bin/diff", "-u", golden, got) // assumes POSIX buf := new(bytes.Buffer) cmd.Stdout = buf if err := cmd.Run(); err != nil { t.Errorf("Oracle 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) } } } } } func TestMultipleQueries(t *testing.T) { // Importer var buildContext = build.Default buildContext.GOPATH = "testdata" imp := importer.New(&importer.Config{Build: &buildContext}) // Oracle filename := "testdata/src/main/multi.go" o, err := oracle.New(imp, []string{filename}, nil, true) if err != nil { t.Fatalf("oracle.New failed: %s", err) } // QueryPos pos := filename + ":#54,#58" qpos, err := oracle.ParseQueryPos(imp, pos, true) if err != nil { t.Fatalf("oracle.ParseQueryPos(%q) failed: %s", pos, err) } // SSA is built and we have the QueryPos. // Release the other ASTs and type info to the GC. imp = nil // Run different query modes on same scope and selection. out := new(bytes.Buffer) for _, mode := range [...]string{"callers", "describe", "freevars"} { res, err := o.Query(mode, qpos) if err != nil { t.Errorf("(*oracle.Oracle).Query(%q) failed: %s", pos, err) } WriteResult(out, res) } want := `multi.f is called from these 1 sites: static function call from multi.main function call (or conversion) of type () Free identifiers: var x int ` if got := out.String(); got != want { t.Errorf("Query output differs; want <<%s>>, got <<%s>>\n", want, got) } } ./playground/0000755000014500017510000000000012246613010012657 5ustar michaelstaff./playground/appengine.go0000644000014500017510000000067512246613010015164 0ustar michaelstaff// Copyright 2012 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 playground import ( "net/http" "appengine" "appengine/urlfetch" ) func client(r *http.Request) *http.Client { return urlfetch.Client(appengine.NewContext(r)) } func report(r *http.Request, err error) { appengine.NewContext(r).Errorf("%v", err) } ./playground/local.go0000644000014500017510000000056212246613010014303 0ustar michaelstaff// Copyright 2012 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 playground import ( "log" "net/http" ) func client(r *http.Request) *http.Client { return http.DefaultClient } func report(r *http.Request, err error) { log.Println(err) } ./playground/socket/0000755000014500017510000000000012246613010014147 5ustar michaelstaff./playground/socket/socket.go0000644000014500017510000002261512246613010015774 0ustar michaelstaff// Copyright 2012 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 socket implements an WebSocket-based playground backend. // Clients connect to a websocket handler and send run/kill commands, and // the server sends the output and exit status of the running processes. // Multiple clients running multiple processes may be served concurrently. // The wire format is JSON and is described by the Message type. // // This will not run on App Engine as WebSockets are not supported there. package socket import ( "bytes" "encoding/json" "errors" "go/parser" "go/token" "io" "io/ioutil" "log" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "sync" "time" "unicode/utf8" "code.google.com/p/go.net/websocket" ) // Handler implements a WebSocket handler for a client connection. var Handler = websocket.Handler(socketHandler) // Environ provides an environment when a binary, such as the go tool, is // invoked. var Environ func() []string = os.Environ const ( // The maximum number of messages to send per session (avoid flooding). msgLimit = 1000 // Batch messages sent in this interval and send as a single message. msgDelay = 10 * time.Millisecond ) // Message is the wire format for the websocket connection to the browser. // It is used for both sending output messages and receiving commands, as // distinguished by the Kind field. type Message struct { Id string // client-provided unique id for the process Kind string // in: "run", "kill" out: "stdout", "stderr", "end" Body string Options *Options `json:",omitempty"` } // Options specify additional message options. type Options struct { Race bool // use -race flag when building code (for "run" only) } // socketHandler handles the websocket connection for a given present session. // It handles transcoding Messages to and from JSON format, and starting // and killing processes. func socketHandler(c *websocket.Conn) { in, out := make(chan *Message), make(chan *Message) errc := make(chan error, 1) // Decode messages from client and send to the in channel. go func() { dec := json.NewDecoder(c) for { var m Message if err := dec.Decode(&m); err != nil { errc <- err return } in <- &m } }() // Receive messages from the out channel and encode to the client. go func() { enc := json.NewEncoder(c) for m := range out { if err := enc.Encode(m); err != nil { errc <- err return } } }() // Start and kill processes and handle errors. proc := make(map[string]*process) for { select { case m := <-in: switch m.Kind { case "run": proc[m.Id].Kill() lOut := limiter(in, out) proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options) case "kill": proc[m.Id].Kill() } case err := <-errc: if err != io.EOF { // A encode or decode has failed; bail. log.Println(err) } // Shut down any running processes. for _, p := range proc { p.Kill() } return } } } // process represents a running process. type process struct { id string out chan<- *Message done chan struct{} // closed when wait completes run *exec.Cmd bin string } // 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{}), } var err error if path, args := shebang(body); path != "" { err = p.startProcess(path, args, body) } else { err = p.start(body, opt) } if err != nil { p.end(err) return nil } go p.wait() return p } // Kill stops the process if it is running and waits for it to exit. func (p *process) Kill() { if p == nil { return } p.run.Process.Kill() <-p.done // block until process exits } // shebang looks for a shebang ('#!') at the beginning of the passed string. // If found, it returns the path and args after the shebang. func shebang(body string) (path string, args []string) { body = strings.TrimSpace(body) if !strings.HasPrefix(body, "#!") { return "", nil } if i := strings.Index(body, "\n"); i >= 0 { body = body[:i] } fs := strings.Fields(body[2:]) return fs[0], fs[1:] } // startProcess starts a given program given its path and passing the given body // to the command standard input. func (p *process) startProcess(path string, args []string, body string) error { cmd := &exec.Cmd{ 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}, } if err := cmd.Start(); err != nil { return err } p.run = cmd return nil } // start builds and starts the given program, sending its output to p.out, // and stores the running *exec.Cmd in the run field. func (p *process) start(body string, opt *Options) error { // We "go build" and then exec the binary so that the // resultant *exec.Cmd is a handle to the user's program // (rather than the go tool process). // This makes Kill work. bin := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq)) src := bin + ".go" if runtime.GOOS == "windows" { bin += ".exe" } // write body to x.go defer os.Remove(src) err := ioutil.WriteFile(src, []byte(body), 0666) if err != nil { return err } // build x.go, creating x p.bin = bin // to be removed by p.end dir, file := filepath.Split(src) args := []string{"go", "build", "-tags", "OMIT"} if opt != nil && opt.Race { p.out <- &Message{ Id: p.id, Kind: "stderr", Body: "Running with race detector.\n", } args = append(args, "-race") } args = append(args, "-o", bin, file) cmd := p.cmd(dir, args...) cmd.Stdout = cmd.Stderr // send compiler output to stderr if err := cmd.Run(); err != nil { return err } // run x cmd = p.cmd("", bin) if opt != nil && opt.Race { cmd.Env = append(cmd.Env, "GOMAXPROCS=2") } if err := cmd.Start(); err != nil { // If we failed to exec, that might be because they built // a non-main package instead of an executable. // Check and report that. if name, err := packageName(body); err == nil && name != "main" { return errors.New(`executable programs must use "package main"`) } return err } p.run = cmd 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(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} return cmd } func packageName(body string) (string, error) { f, err := parser.ParseFile(token.NewFileSet(), "prog.go", strings.NewReader(body), parser.PackageClauseOnly) if err != nil { return "", err } return f.Name.String(), nil } // 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 } 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() 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) { return string(b) } var buf bytes.Buffer for len(b) > 0 { r, size := utf8.DecodeRune(b) b = b[size:] buf.WriteRune(r) } 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() { // find real path to temporary directory var err error tmpdir, err = filepath.EvalSymlinks(os.TempDir()) if err != nil { log.Fatal(err) } } var uniq = make(chan int) // a source of numbers for naming temporary files func init() { go func() { for i := 0; ; i++ { uniq <- i } }() } ./playground/common.go0000644000014500017510000000222312246613010014475 0ustar michaelstaff// 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 playground registers HTTP handlers at "/compile" and "/share" that // proxy requests to the golang.org playground service. // This package may be used unaltered on App Engine. package playground import ( "bytes" "fmt" "io" "net/http" ) const baseURL = "http://play.golang.org" func init() { http.HandleFunc("/compile", bounce) http.HandleFunc("/share", bounce) } func bounce(w http.ResponseWriter, r *http.Request) { b := new(bytes.Buffer) if err := passThru(b, r); err != nil { http.Error(w, "Server error.", http.StatusInternalServerError) report(r, err) return } io.Copy(w, b) } func passThru(w io.Writer, req *http.Request) error { defer req.Body.Close() url := baseURL + req.URL.Path r, err := client(req).Post(url, req.Header.Get("Content-type"), req.Body) if err != nil { return fmt.Errorf("making POST request: %v", err) } defer r.Body.Close() if _, err := io.Copy(w, r.Body); err != nil { return fmt.Errorf("copying response Body: %v", err) } return nil } ./importer/0000755000014500017510000000000012246613010012334 5ustar michaelstaff./importer/source_test.go0000644000014500017510000001600612246613010015225 0ustar michaelstaff// 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 importer_test // This file defines tests of source utilities. // TODO(adonovan): exhaustive tests that run over the whole input // tree, not just handcrafted examples. import ( "bytes" "fmt" "go/ast" "go/parser" "go/token" "strings" "testing" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" ) // 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() } // 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, 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)) } // Common input for following tests. const input = ` // Hello. package main import "fmt" func f() {} func main() { z := (x + y) // add them f() // NB: ExprStmt and its CallExpr have same Pos/End } ` func TestPathEnclosingInterval_Exact(t *testing.T) { // For the exact tests, we check that a substring is mapped to // the canonical string for the node it denotes. tests := []struct { substr string // first occurrence of this string indicates interval node string // complete text of expected containing node }{ {"package", input[11 : len(input)-1]}, {"\npack", input[11 : len(input)-1]}, {"main", "main"}, {"import", "import \"fmt\""}, {"\"fmt\"", "\"fmt\""}, {"\nfunc f() {}\n", "func f() {}"}, {"x ", "x"}, {" y", "y"}, {"z", "z"}, {" + ", "x + y"}, {" :=", "z := (x + y)"}, {"x + y", "x + y"}, {"(x + y)", "(x + y)"}, {" (x + y) ", "(x + y)"}, {" (x + y) // add", "(x + y)"}, {"func", "func f() {}"}, {"func f() {}", "func f() {}"}, {"\nfun", "func f() {}"}, {" f", "f"}, } for _, test := range tests { f, start, end := findInterval(t, new(token.FileSet), input, test.substr) if f == nil { continue } path, exact := importer.PathEnclosingInterval(f, start, end) if !exact { t.Errorf("PathEnclosingInterval(%q) not exact", test.substr) continue } if len(path) == 0 { if test.node != "" { t.Errorf("PathEnclosingInterval(%q).path: got [], want %q", test.substr, test.node) } continue } if got := input[path[0].Pos():path[0].End()]; got != test.node { t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)", test.substr, got, test.node, pathToString(path)) continue } } } func TestPathEnclosingInterval_Paths(t *testing.T) { // For these tests, we check only the path of the enclosing // node, but not its complete text because it's often quite // large when !exact. tests := []struct { substr string // first occurrence of this string indicates interval path string // the pathToString(),exact of the expected path }{ {"// add", "[BlockStmt FuncDecl File],false"}, {"(x + y", "[ParenExpr AssignStmt BlockStmt FuncDecl File],false"}, {"x +", "[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"}, {"z := (x", "[AssignStmt BlockStmt FuncDecl File],false"}, {"func f", "[FuncDecl File],false"}, {"func f()", "[FuncDecl File],false"}, {" f()", "[FuncDecl File],false"}, {"() {}", "[FuncDecl File],false"}, {"// Hello", "[File],false"}, {" f", "[Ident FuncDecl File],true"}, {"func ", "[FuncDecl File],true"}, {"mai", "[Ident File],true"}, {"f() // NB", "[CallExpr ExprStmt BlockStmt FuncDecl File],true"}, } for _, test := range tests { f, start, end := findInterval(t, new(token.FileSet), input, test.substr) if f == nil { continue } path, exact := importer.PathEnclosingInterval(f, start, end) if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path { t.Errorf("PathEnclosingInterval(%q): got %q, want %q", test.substr, got, test.path) continue } } } // -------- Tests of source.go ----------------------------------------- // TODO(adonovan): move this test into ssa. 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", "func@2.24"}, // Doubly nested {`package main func f() { println(func() { print(func() { print(350) })})}`, "350", "func@2.39"}, // 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", "func@2.27"}, } for _, test := range tests { imp := importer.New(new(importer.Config)) // (NB: no go/build.Config) f, start, end := findInterval(t, imp.Fset, test.input, test.substr) if f == nil { continue } path, exact := importer.PathEnclosingInterval(f, start, end) if !exact { t.Errorf("EnclosingFunction(%q) not exact", test.substr) continue } mainInfo := imp.CreatePackage("main", f) prog := ssa.NewProgram(imp.Fset, 0) if err := prog.CreatePackages(imp); err != nil { t.Error(err) continue } pkg := prog.Package(mainInfo.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 } } } ./importer/source.go0000644000014500017510000004110612246613010014165 0ustar michaelstaff// 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 importer // This file defines utilities for working with source positions. import ( "fmt" "go/ast" "go/token" "sort" ) // PathEnclosingInterval returns the node that encloses the source // interval [start, end), and all its ancestors up to the AST root. // // The definition of "enclosing" used by this function considers // additional whitespace abutting a node to be enclosed by it. // In this example: // // z := x + y // add them // <-A-> // <----B-----> // // the ast.BinaryExpr(+) node is considered to enclose interval B // even though its [Pos()..End()) is actually only interval A. // This behaviour makes user interfaces more tolerant of imperfect // input. // // This function treats tokens as nodes, though they are not included // in the result. e.g. PathEnclosingInterval("+") returns the // enclosing ast.BinaryExpr("x + y"). // // If start==end, the 1-char interval following start is used instead. // // The 'exact' result is true if the interval contains only path[0] // and perhaps some adjacent whitespace. It is false if the interval // overlaps multiple children of path[0], or if it contains only // interior whitespace of path[0]. // In this example: // // z := x + y // add them // <--C--> <---E--> // ^ // D // // intervals C, D and E are inexact. C is contained by the // z-assignment statement, because it spans three of its children (:=, // x, +). So too is the 1-char interval D, because it contains only // interior whitespace of the assignment. E is considered interior // whitespace of the BlockStmt containing the assignment. // // Precondition: [start, end) both lie within the same file as root. // TODO(adonovan): return (nil, false) in this case and remove precond. // Requires FileSet; see tokenFileContainsPos. // // Postcondition: path is never nil; it always contains at least 'root'. // func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) { // fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging // Precondition: node.[Pos..End) and adjoining whitespace contain [start, end). var visit func(node ast.Node) bool visit = func(node ast.Node) bool { path = append(path, node) nodePos := node.Pos() nodeEnd := node.End() // fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging // Intersect [start, end) with interval of node. if start < nodePos { start = nodePos } if end > nodeEnd { end = nodeEnd } // Find sole child that contains [start, end). children := childrenOf(node) l := len(children) for i, child := range children { // [childPos, childEnd) is unaugmented interval of child. childPos := child.Pos() childEnd := child.End() // [augPos, augEnd) is whitespace-augmented interval of child. augPos := childPos augEnd := childEnd if i > 0 { augPos = children[i-1].End() // start of preceding whitespace } if i < l-1 { nextChildPos := children[i+1].Pos() // Does [start, end) lie between child and next child? if start >= augEnd && end <= nextChildPos { return false // inexact match } augEnd = nextChildPos // end of following whitespace } // fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n", // i, augPos, augEnd, start, end) // debugging // Does augmented child strictly contain [start, end)? if augPos <= start && end <= augEnd { _, isToken := child.(tokenNode) return isToken || visit(child) } // Does [start, end) overlap multiple children? // i.e. left-augmented child contains start // but LR-augmented child does not contain end. if start < childEnd && end > augEnd { break } } // No single child contained [start, end), // so node is the result. Is it exact? // (It's tempting to put this condition before the // child loop, but it gives the wrong result in the // case where a node (e.g. ExprStmt) and its sole // child have equal intervals.) if start == nodePos && end == nodeEnd { return true // exact match } return false // inexact: overlaps multiple children } if start > end { start, end = end, start } if start < root.End() && end > root.Pos() { if start == end { end = start + 1 // empty interval => interval of size 1 } exact = visit(root) // Reverse the path: for i, l := 0, len(path); i < l/2; i++ { path[i], path[l-1-i] = path[l-1-i], path[i] } } else { // Selection lies within whitespace preceding the // first (or following the last) declaration in the file. // The result nonetheless always includes the ast.File. path = append(path, root) } return } // tokenNode is a dummy implementation of ast.Node for a single token. // They are used transiently by PathEnclosingInterval but never escape // this package. // type tokenNode struct { pos token.Pos end token.Pos } func (n tokenNode) Pos() token.Pos { return n.pos } func (n tokenNode) End() token.Pos { return n.end } func tok(pos token.Pos, len int) ast.Node { return tokenNode{pos, pos + token.Pos(len)} } // childrenOf returns the direct non-nil children of ast.Node n. // It may include fake ast.Node implementations for bare tokens. // it is not safe to call (e.g.) ast.Walk on such nodes. // func childrenOf(n ast.Node) []ast.Node { var children []ast.Node // First add nodes for all true subtrees. ast.Inspect(n, func(node ast.Node) bool { if node == n { // push n return true // recur } if node != nil { // push child children = append(children, node) } return false // no recursion }) // Then add fake Nodes for bare tokens. switch n := n.(type) { case *ast.ArrayType: children = append(children, tok(n.Lbrack, len("[")), tok(n.Elt.End(), len("]"))) case *ast.AssignStmt: children = append(children, tok(n.TokPos, len(n.Tok.String()))) case *ast.BasicLit: children = append(children, tok(n.ValuePos, len(n.Value))) case *ast.BinaryExpr: children = append(children, tok(n.OpPos, len(n.Op.String()))) case *ast.BlockStmt: children = append(children, tok(n.Lbrace, len("{")), tok(n.Rbrace, len("}"))) case *ast.BranchStmt: children = append(children, tok(n.TokPos, len(n.Tok.String()))) case *ast.CallExpr: children = append(children, tok(n.Lparen, len("(")), tok(n.Rparen, len(")"))) if n.Ellipsis != 0 { children = append(children, tok(n.Ellipsis, len("..."))) } case *ast.CaseClause: if n.List == nil { children = append(children, tok(n.Case, len("default"))) } else { children = append(children, tok(n.Case, len("case"))) } children = append(children, tok(n.Colon, len(":"))) case *ast.ChanType: switch n.Dir { case ast.RECV: children = append(children, tok(n.Begin, len("<-chan"))) case ast.SEND: children = append(children, tok(n.Begin, len("chan<-"))) case ast.RECV | ast.SEND: children = append(children, tok(n.Begin, len("chan"))) } case *ast.CommClause: if n.Comm == nil { children = append(children, tok(n.Case, len("default"))) } else { children = append(children, tok(n.Case, len("case"))) } children = append(children, tok(n.Colon, len(":"))) case *ast.Comment: // nop case *ast.CommentGroup: // nop case *ast.CompositeLit: children = append(children, tok(n.Lbrace, len("{")), tok(n.Rbrace, len("{"))) case *ast.DeclStmt: // nop case *ast.DeferStmt: children = append(children, tok(n.Defer, len("defer"))) case *ast.Ellipsis: children = append(children, tok(n.Ellipsis, len("..."))) case *ast.EmptyStmt: // nop case *ast.ExprStmt: // nop case *ast.Field: // TODO(adonovan): Field.{Doc,Comment,Tag}? case *ast.FieldList: children = append(children, tok(n.Opening, len("(")), tok(n.Closing, len(")"))) case *ast.File: // TODO test: Doc children = append(children, tok(n.Package, len("package"))) case *ast.ForStmt: children = append(children, tok(n.For, len("for"))) case *ast.FuncDecl: // TODO(adonovan): FuncDecl.Comment? // Uniquely, FuncDecl breaks the invariant that // preorder traversal yields tokens in lexical order: // in fact, FuncDecl.Recv precedes FuncDecl.Type.Func. // // As a workaround, we inline the case for FuncType // here and order things correctly. // children = nil // discard ast.Walk(FuncDecl) info subtrees children = append(children, tok(n.Type.Func, len("func"))) if n.Recv != nil { children = append(children, n.Recv) } children = append(children, n.Name) if n.Type.Params != nil { children = append(children, n.Type.Params) } if n.Type.Results != nil { children = append(children, n.Type.Results) } if n.Body != nil { children = append(children, n.Body) } case *ast.FuncLit: // nop case *ast.FuncType: if n.Func != 0 { children = append(children, tok(n.Func, len("func"))) } case *ast.GenDecl: children = append(children, tok(n.TokPos, len(n.Tok.String()))) if n.Lparen != 0 { children = append(children, tok(n.Lparen, len("(")), tok(n.Rparen, len(")"))) } case *ast.GoStmt: children = append(children, tok(n.Go, len("go"))) case *ast.Ident: children = append(children, tok(n.NamePos, len(n.Name))) case *ast.IfStmt: children = append(children, tok(n.If, len("if"))) case *ast.ImportSpec: // TODO(adonovan): ImportSpec.{Doc,EndPos}? case *ast.IncDecStmt: children = append(children, tok(n.TokPos, len(n.Tok.String()))) case *ast.IndexExpr: children = append(children, tok(n.Lbrack, len("{")), tok(n.Rbrack, len("}"))) case *ast.InterfaceType: children = append(children, tok(n.Interface, len("interface"))) case *ast.KeyValueExpr: children = append(children, tok(n.Colon, len(":"))) case *ast.LabeledStmt: children = append(children, tok(n.Colon, len(":"))) case *ast.MapType: children = append(children, tok(n.Map, len("map"))) case *ast.ParenExpr: children = append(children, tok(n.Lparen, len("(")), tok(n.Rparen, len(")"))) case *ast.RangeStmt: children = append(children, tok(n.For, len("for")), tok(n.TokPos, len(n.Tok.String()))) case *ast.ReturnStmt: children = append(children, tok(n.Return, len("return"))) case *ast.SelectStmt: children = append(children, tok(n.Select, len("select"))) case *ast.SelectorExpr: // nop case *ast.SendStmt: children = append(children, tok(n.Arrow, len("<-"))) case *ast.SliceExpr: children = append(children, tok(n.Lbrack, len("[")), tok(n.Rbrack, len("]"))) case *ast.StarExpr: children = append(children, tok(n.Star, len("*"))) case *ast.StructType: children = append(children, tok(n.Struct, len("struct"))) case *ast.SwitchStmt: children = append(children, tok(n.Switch, len("switch"))) case *ast.TypeAssertExpr: children = append(children, tok(n.Lparen-1, len(".")), tok(n.Lparen, len("(")), tok(n.Rparen, len(")"))) case *ast.TypeSpec: // TODO(adonovan): TypeSpec.{Doc,Comment}? case *ast.TypeSwitchStmt: children = append(children, tok(n.Switch, len("switch"))) case *ast.UnaryExpr: children = append(children, tok(n.OpPos, len(n.Op.String()))) case *ast.ValueSpec: // TODO(adonovan): ValueSpec.{Doc,Comment}? default: // Includes *ast.BadDecl, *ast.BadExpr, *ast.BadStmt. panic(fmt.Sprintf("unexpected node type %T", n)) } // TODO(adonovan): opt: merge the logic of ast.Inspect() into // the switch above so we can make interleaved callbacks for // both Nodes and Tokens in the right order and avoid the need // to sort. sort.Sort(byPos(children)) return children } type byPos []ast.Node func (sl byPos) Len() int { return len(sl) } func (sl byPos) Less(i, j int) bool { return sl[i].Pos() < sl[j].Pos() } func (sl byPos) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] } // NodeDescription returns a description of the concrete type of n suitable // for a user interface. // // TODO(adonovan): in some cases (e.g. Field, FieldList, Ident, // StarExpr) we could be much more specific given the path to the AST // root. Perhaps we should do that. // func NodeDescription(n ast.Node) string { switch n := n.(type) { case *ast.ArrayType: return "array type" case *ast.AssignStmt: return "assignment" case *ast.BadDecl: return "bad declaration" case *ast.BadExpr: return "bad expression" case *ast.BadStmt: return "bad statement" case *ast.BasicLit: return "basic literal" case *ast.BinaryExpr: return fmt.Sprintf("binary %s operation", n.Op) case *ast.BlockStmt: return "block" case *ast.BranchStmt: switch n.Tok { case token.BREAK: return "break statement" case token.CONTINUE: return "continue statement" case token.GOTO: return "goto statement" case token.FALLTHROUGH: return "fall-through statement" } case *ast.CallExpr: return "function call (or conversion)" case *ast.CaseClause: return "case clause" case *ast.ChanType: return "channel type" case *ast.CommClause: return "communication clause" case *ast.Comment: return "comment" case *ast.CommentGroup: return "comment group" case *ast.CompositeLit: return "composite literal" case *ast.DeclStmt: return NodeDescription(n.Decl) + " statement" case *ast.DeferStmt: return "defer statement" case *ast.Ellipsis: return "ellipsis" case *ast.EmptyStmt: return "empty statement" case *ast.ExprStmt: return "expression statement" case *ast.Field: // Can be any of these: // struct {x, y int} -- struct field(s) // struct {T} -- anon struct field // interface {I} -- interface embedding // interface {f()} -- interface method // func (A) func(B) C -- receiver, param(s), result(s) return "field/method/parameter" case *ast.FieldList: return "field/method/parameter list" case *ast.File: return "source file" case *ast.ForStmt: return "for loop" case *ast.FuncDecl: return "function declaration" case *ast.FuncLit: return "function literal" case *ast.FuncType: return "function type" case *ast.GenDecl: switch n.Tok { case token.IMPORT: return "import declaration" case token.CONST: return "constant declaration" case token.TYPE: return "type declaration" case token.VAR: return "variable declaration" } case *ast.GoStmt: return "go statement" case *ast.Ident: return "identifier" case *ast.IfStmt: return "if statement" case *ast.ImportSpec: return "import specification" case *ast.IncDecStmt: if n.Tok == token.INC { return "increment statement" } return "derement statement" case *ast.IndexExpr: return "index expression" case *ast.InterfaceType: return "interface type" case *ast.KeyValueExpr: return "key/value association" case *ast.LabeledStmt: return "statement label" case *ast.MapType: return "map type" case *ast.Package: return "package" case *ast.ParenExpr: return "parenthesized " + NodeDescription(n.X) case *ast.RangeStmt: return "range loop" case *ast.ReturnStmt: return "return statement" case *ast.SelectStmt: return "select statement" case *ast.SelectorExpr: return "selector" case *ast.SendStmt: return "channel send" case *ast.SliceExpr: return "slice expression" case *ast.StarExpr: return "*-operation" // load/store expr or pointer type case *ast.StructType: return "struct type" case *ast.SwitchStmt: return "switch statement" case *ast.TypeAssertExpr: return "type assertion" case *ast.TypeSpec: return "type specification" case *ast.TypeSwitchStmt: return "type switch" case *ast.UnaryExpr: return fmt.Sprintf("unary %s operation", n.Op) case *ast.ValueSpec: return "value specification" } panic(fmt.Sprintf("unexpected node type: %T", n)) } // TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos) func tokenFileContainsPos(f *token.File, pos token.Pos) bool { p := int(pos) base := f.Base() return base <= p && p < base+f.Size() } // 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 the // Importer imp. exact is defined as for standalone // PathEnclosingInterval. // // The result is (nil, nil, false) if not found. // func (imp *Importer) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) { for _, info := range imp.allPackages { for _, f := range info.Files { if !tokenFileContainsPos(imp.Fset.File(f.Package), start) { continue } if path, exact := PathEnclosingInterval(f, start, end); path != nil { return info, path, exact } } } return nil, nil, false } ./importer/importer_test.go0000644000014500017510000000412212246613010015562 0ustar michaelstaff// 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 importer_test import ( "fmt" "go/build" "testing" "code.google.com/p/go.tools/importer" ) func TestLoadInitialPackages(t *testing.T) { ctxt := &importer.Config{Build: &build.Default} // Failed load: bad first import path causes parsePackageFiles to fail. args := []string{"nosuchpkg", "errors"} if _, _, err := importer.New(ctxt).LoadInitialPackages(args); err == nil { t.Errorf("LoadInitialPackages(%q) succeeded, want failure", args) } else { // cannot find package: ok. } // Failed load: bad second import path proceeds to doImport0, which fails. args = []string{"errors", "nosuchpkg"} if _, _, err := importer.New(ctxt).LoadInitialPackages(args); err == nil { t.Errorf("LoadInitialPackages(%q) succeeded, want failure", args) } else { // cannot find package: ok } // Successful load. args = []string{"fmt", "errors", "testdata/a.go,testdata/b.go", "--", "surplus"} imp := importer.New(ctxt) infos, rest, err := imp.LoadInitialPackages(args) if err != nil { t.Errorf("LoadInitialPackages(%q) failed: %s", args, err) return } if got, want := fmt.Sprint(rest), "[surplus]"; got != want { t.Errorf("LoadInitialPackages(%q) rest: got %s, want %s", got, want) } // Check list of initial packages. var pkgnames []string for _, info := range infos { pkgnames = append(pkgnames, info.Pkg.Path()) } // Only the first import path (currently) contributes tests. if got, want := fmt.Sprint(pkgnames), "[fmt fmt_test errors P]"; got != want { t.Errorf("InitialPackages: got %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. all := map[string]struct{}{} for _, info := range imp.AllPackages() { all[info.Pkg.Path()] = struct{}{} } want := []string{"strings", "time", "runtime", "testing", "unicode"} for _, w := range want { if _, ok := all[w]; !ok { t.Errorf("AllPackages: want element %s, got set %v", w, all) } } } ./importer/pkginfo.go0000644000014500017510000000523412246613010014324 0ustar michaelstaff// 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 importer // TODO(gri): absorb this into go/types. import ( "fmt" "go/ast" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" ) // PackageInfo holds the ASTs and facts derived by the type-checker // for a single package. // // Not mutated once constructed. // type PackageInfo struct { Pkg *types.Package Importable bool // true if 'import "Pkg.Path()"' would resolve to this Err error // non-nil if the package had static errors Files []*ast.File // abstract syntax for the package's files types.Info // type-checker deductions. } func (info *PackageInfo) String() string { return fmt.Sprintf("PackageInfo(%s)", info.Pkg.Path()) } // TypeOf returns the type of expression e. // Precondition: e belongs to the package's ASTs. // func (info *PackageInfo) TypeOf(e ast.Expr) types.Type { if t, ok := info.Types[e]; ok { return t } // Defining ast.Idents (id := expr) get only Ident callbacks // but not Expr callbacks. if id, ok := e.(*ast.Ident); ok { return info.ObjectOf(id).Type() } panic("no type for expression") } // ValueOf returns the value of expression e if it is a constant, nil // otherwise. // Precondition: e belongs to the package's ASTs. // func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value { return info.Values[e] } // ObjectOf returns the typechecker object denoted by the specified id. // Precondition: id belongs to the package's ASTs. // func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object { return info.Objects[id] } // IsType returns true iff expression e denotes a type. // Precondition: e belongs to the package's ASTs. // // TODO(gri): move this into go/types. // func (info *PackageInfo) IsType(e ast.Expr) bool { switch e := e.(type) { case *ast.SelectorExpr: // pkg.Type if sel := info.Selections[e]; sel.Kind() == types.PackageObj { _, isType := sel.Obj().(*types.TypeName) return isType } case *ast.StarExpr: // *T return info.IsType(e.X) case *ast.Ident: _, isType := info.ObjectOf(e).(*types.TypeName) return isType case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: return true case *ast.ParenExpr: return info.IsType(e.X) } return false } // TypeCaseVar returns the implicit variable created by a single-type // case clause in a type switch, or nil if not found. // func (info *PackageInfo) TypeCaseVar(cc *ast.CaseClause) *types.Var { if v := info.Implicits[cc]; v != nil { return v.(*types.Var) } return nil } ./importer/util.go0000644000014500017510000000607612246613010013651 0ustar michaelstaff// 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 importer // This file defines various utility functions exposed by the package // and used by it. import ( "go/ast" "go/build" "go/parser" "go/token" "path/filepath" "strconv" "sync" ) // parsePackageFiles enumerates the files belonging to package path, // then loads, parses and returns them. // // 'which' is a list of flags indicating which files to include: // 'g': include non-test *.go source files (GoFiles) // 't': include in-package *_test.go source files (TestGoFiles) // 'x': include external *_test.go source files. (XTestGoFiles) // func parsePackageFiles(ctxt *build.Context, fset *token.FileSet, path string, which string) ([]*ast.File, error) { // Set the "!cgo" go/build tag, preferring (dummy) Go to // native C implementations of net.cgoLookupHost et al. ctxt2 := *ctxt ctxt2.CgoEnabled = false // Import(srcDir="") disables local imports, e.g. import "./foo". bp, err := ctxt2.Import(path, "", 0) if _, ok := err.(*build.NoGoError); ok { return nil, nil // empty directory } if err != nil { return nil, err // import failed } var filenames []string for _, c := range which { var s []string switch c { case 'g': s = bp.GoFiles case 't': s = bp.TestGoFiles case 'x': s = bp.XTestGoFiles default: panic(c) } filenames = append(filenames, s...) } return ParseFiles(fset, bp.Dir, filenames...) } // ParseFiles parses the Go source files files within directory dir // and returns their ASTs, or the first parse error if any. // func ParseFiles(fset *token.FileSet, dir string, files ...string) ([]*ast.File, error) { var wg sync.WaitGroup n := len(files) parsed := make([]*ast.File, n, n) errors := make([]error, n, n) for i, file := range files { if !filepath.IsAbs(file) { file = filepath.Join(dir, file) } wg.Add(1) go func(i int, file string) { parsed[i], errors[i] = parser.ParseFile(fset, file, nil, 0) wg.Done() }(i, file) } wg.Wait() for _, err := range errors { if err != nil { return nil, err } } return parsed, nil } // ---------- Internal helpers ---------- // unparen returns e with any enclosing parentheses stripped. func unparen(e ast.Expr) ast.Expr { for { p, ok := e.(*ast.ParenExpr) if !ok { break } e = p.X } return e } func unreachable() { panic("unreachable") } // importsOf returns the set of paths imported by the specified files. func importsOf(p string, files []*ast.File) map[string]bool { imports := make(map[string]bool) outer: for _, file := range files { for _, decl := range file.Decls { if decl, ok := decl.(*ast.GenDecl); ok { if decl.Tok != token.IMPORT { break outer // stop at the first non-import } for _, spec := range decl.Specs { spec := spec.(*ast.ImportSpec) if path, _ := strconv.Unquote(spec.Path.Value); path != "C" { imports[path] = true } } } else { break outer // stop at the first non-import } } } return imports } ./importer/testdata/0000755000014500017510000000000012246613010014145 5ustar michaelstaff./importer/testdata/b.go0000644000014500017510000000001212246613010014706 0ustar michaelstaffpackage P ./importer/testdata/a.go0000644000014500017510000000001212246613010014705 0ustar michaelstaffpackage P ./importer/importer.go0000644000014500017510000004434312246613010014534 0ustar michaelstaff// 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 importer defines the Importer, which loads, parses and // type-checks packages of Go code plus their transitive closure, and // retains both the ASTs and the derived facts. // // CONCEPTS AND TERMINOLOGY // // 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 src/pkg/net/http/triv.go. // // EXTERNAL TEST packages are those comprised of a set of *_test.go // files all with the same 'package foo_test' declaration, all in the // same directory. (go/build.Package calls these files XTestFiles.) // // An IMPORTABLE package is one that can be referred to by some import // spec. Ad-hoc packages and external test packages are non-importable. // The importer and its clients must be careful not to assume that // the import path of a package may be used for a name-based lookup. // For example, a pointer analysis scope may consist of two initial // (ad-hoc) packages both called "main". // // An AUGMENTED package is an importable package P plus all the // *_test.go files with same 'package foo' declaration as P. // (go/build.Package calls these files TestFiles.) // An external test package may depend upon members of the augmented // package that are not in the unaugmented package, such as functions // that expose internals. (See bufio/export_test.go for an example.) // So, the importer must ensure that for each external test package // it loads, it also augments the corresponding non-test package. // // The import graph over n unaugmented packages must be acyclic; the // import graph over n-1 unaugmented packages plus one augmented // package must also be acyclic. ('go test' relies on this.) But the // import graph over n augmented packages may contain cycles, and // currently, go/types is incapable of handling such inputs, so the // Importer will only augment (and create an external test package // for) the first import path specified on the command-line. // package importer import ( "errors" "fmt" "go/ast" "go/build" "go/token" "os" "strings" "sync" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) // An Importer's exported methods are not thread-safe. type Importer struct { Fset *token.FileSet // position info for all files seen config Config // the client configuration, modified by us importfn types.Importer // client's type import function augment map[string]bool // packages to be augmented by TestFiles when imported allPackagesMu sync.Mutex // guards 'allPackages' during internal concurrency allPackages []*PackageInfo // all packages, including non-importable ones importedMu sync.Mutex // guards 'imported' imported map[string]*importInfo // all imported packages (incl. failures) by import path } // importInfo holds internal information about each import path. type importInfo struct { path string // import path info *PackageInfo // results of typechecking (including type errors) err error // reason for failure to construct a package ready chan struct{} // channel close is notification of ready state } // Config specifies the configuration for the importer. type Config struct { // TypeChecker contains options relating to the type checker. // All callbacks must be thread-safe. TypeChecker types.Config // If Build is non-nil, it is used to satisfy imports. // // If it is nil, binary object files produced by the gc // compiler will be loaded instead of source code for all // imported packages. Such files supply only the types of // package-level declarations and values of constants, but no // code, so this mode will not yield a whole program. It is // intended for analyses that perform intraprocedural analysis // of a single package. Build *build.Context } // New returns a new, empty Importer using configuration options // specified by config. // func New(config *Config) *Importer { importfn := config.TypeChecker.Import if importfn == nil { importfn = gcimporter.Import } imp := &Importer{ Fset: token.NewFileSet(), config: *config, // copy (don't clobber client input) importfn: importfn, augment: make(map[string]bool), imported: make(map[string]*importInfo), } // TODO(adonovan): get typechecker to supply us with a source // position, then pass errors back to the application // (e.g. oracle). if imp.config.TypeChecker.Error == nil { imp.config.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) } } imp.config.TypeChecker.Import = imp.doImport // wraps importfn, effectively return imp } // AllPackages returns a new slice containing all packages loaded by // importer imp. // func (imp *Importer) AllPackages() []*PackageInfo { return append([]*PackageInfo(nil), imp.allPackages...) } func (imp *Importer) addPackage(info *PackageInfo) { imp.allPackagesMu.Lock() imp.allPackages = append(imp.allPackages, info) imp.allPackagesMu.Unlock() } // doImport imports the package denoted by path. // It implements the types.Importer prototype. // // imports is the import map of the importing package, later // accessible as types.Package.Imports(). If non-nil, doImport will // update it to include this import. (It may be nil in recursive // calls for prefetching.) // // 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 and thread-safe. // // // TODO(gri): The imports map (an alias for TypeChecker.Packages) must // not be concurrently accessed. Today, the only (non-test) accesses // of this map are in the client-supplied implementation of the // importer function, i.e. the function below. But this is a fragile // API because if go/types ever starts to access this map, it and its // clients will have to agree to use the same mutex. // Two better ideas: // // (1) require that the importer functions be stateful and have this // map be part of that internal state. // Pro: good encapsulation. // Con: we can't have different importers collaborate, e.g. // we can't use a source importer for some packages and // GcImport for others since they'd each have a distinct map. // // (2) have there be a single map in go/types.Config, but expose // lookup and update behind an interface and pass that interface // to the importer implementations. // Pro: sharing of the map among importers. // // This is idempotent but still doesn't address the issue of // atomicity: when loading packages concurrently, we want to avoid // the benign but suboptimal situation of two goroutines both // importing "fmt", finding it not present, doing all the work, and // double-updating the map. // The interface to the map needs to express the idea that when a // caller requests an import from the map and finds it not present, // then it (and no other goroutine) becomes responsible for loading // the package and updating the map; other goroutines should block // until then. That's exactly what doImport0 below does; I think // some of that logic should migrate into go/types.check.resolveFiles. // func (imp *Importer) doImport(imports map[string]*types.Package, path string) (*types.Package, error) { // Package unsafe is handled specially, and has no PackageInfo. // TODO(adonovan): a fake empty package would make things simpler. if path == "unsafe" { return types.Unsafe, nil } info, err := imp.doImport0(imports, path) if err != nil { return nil, err } if imports != nil { // Update the package's imports map, whether success or failure. // // types.Package.Imports() is used by PackageInfo.Imports and // thence by ssa.builder. // TODO(gri): given that it doesn't specify whether it // contains direct or transitive dependencies, it probably // shouldn't be exposed. This package can make its own // arrangements to implement PackageInfo.Imports(). importsMu.Lock() imports[path] = info.Pkg importsMu.Unlock() } return info.Pkg, nil } var importsMu sync.Mutex // hack; see comment at doImport // Like doImport, but returns a PackageInfo. // Precondition: path != "unsafe". func (imp *Importer) doImport0(imports map[string]*types.Package, path string) (*PackageInfo, error) { imp.importedMu.Lock() ii, ok := imp.imported[path] if !ok { ii = &importInfo{path: path, ready: make(chan struct{})} imp.imported[path] = ii } imp.importedMu.Unlock() if !ok { // Find and create the actual package. if imp.config.Build != nil { imp.importSource(path, ii) } else { imp.importBinary(imports, ii) } if ii.info != nil { ii.info.Importable = true } close(ii.ready) // enter ready state and wake up waiters } else { <-ii.ready // wait for ready condition } // Invariant: ii is ready. return ii.info, ii.err } // importBinary implements package loading from the client-supplied // external source, e.g. object files from the gc compiler. // func (imp *Importer) importBinary(imports map[string]*types.Package, ii *importInfo) { pkg, err := imp.importfn(imports, ii.path) if pkg != nil { ii.info = &PackageInfo{Pkg: pkg} imp.addPackage(ii.info) } else { ii.err = err } } // importSource implements package loading by parsing Go source files // located by go/build. // func (imp *Importer) importSource(path string, ii *importInfo) { which := "g" // load *.go files if imp.augment[path] { which = "gt" // augment package by in-package *_test.go files } if files, err := parsePackageFiles(imp.config.Build, imp.Fset, path, which); err == nil { // Prefetch the imports asynchronously. for path := range importsOf(path, files) { go func(path string) { imp.doImport(nil, path) }(path) } // Type-check the package. ii.info = imp.CreatePackage(path, files...) // We needn't wait for the prefetching goroutines to // finish. Each one either runs quickly and populates // the imported map, in which case the type checker // will wait for the map entry to become ready; or, it // runs slowly, even after we return, but then becomes // just another map waiter, in which case it won't // mutate anything. } else { ii.err = err } } // CreatePackage creates and type-checks a package from the specified // list of parsed files, importing their dependencies. It returns a // PackageInfo containing the resulting types.Package, the ASTs, and // other type information. // // The order of files determines the package initialization order. // // path is the full name under which this package is known, such as // appears in an import declaration. e.g. "sync/atomic". It need not // be unique; for example, it is possible to construct two distinct // packages both named "main". // // The resulting package is accessible via AllPackages() but is not // importable, i.e. no 'import' spec can resolve to it. // // CreatePackage never fails, but the resulting package may contain type // errors; the first of these is recorded in PackageInfo.Err. // func (imp *Importer) CreatePackage(path string, files ...*ast.File) *PackageInfo { info := &PackageInfo{ Files: files, Info: types.Info{ Types: make(map[ast.Expr]types.Type), Values: make(map[ast.Expr]exact.Value), Objects: 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), }, } info.Pkg, info.Err = imp.config.TypeChecker.Check(path, imp.Fset, files, &info.Info) imp.addPackage(info) return info } // InitialPackagesUsage is a partial usage message that client // applications may wish to include in their -help output. const InitialPackagesUsage = ` is a list of arguments denoting a set of initial packages. Each argument may take one of two forms: 1. A comma-separated list of *.go source files. All of the specified files are loaded, parsed and type-checked as a single package. The name of the package is taken from the files' package declarations, which must all be equal. All the files must belong to the same directory. 2. An import path. 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. This behaviour may be disabled by prefixing the import path with "notest:", e.g. "notest:fmt". Due to current limitations in the type-checker, only the first import path of the command line will contribute any tests. A '--' argument terminates the list of packages. ` // LoadInitialPackages interprets args as a set of packages, loads // those packages and their dependencies, and returns them. // // It is intended for use in command-line interfaces that require a // set of initial packages to be specified; see InitialPackagesUsage // message for details. // // The second result parameter returns the list of unconsumed // arguments. // // It is an error to specify no packages. // // Precondition: LoadInitialPackages cannot be called after any // previous calls to Load* on the same importer. // func (imp *Importer) LoadInitialPackages(args []string) ([]*PackageInfo, []string, error) { // The "augmentation" mechanism requires that we mark all // packages to be augmented before we import a single one. if len(imp.allPackages) > 0 { return nil, nil, errors.New("LoadInitialPackages called on non-pristine Importer") } // We use two passes. The first parses the files for each // non-importable package and discovers the set of importable // packages that require augmentation by in-package _test.go // files. The second creates the ad-hoc packages and imports // the importable ones. // // This is necessary to ensure that all packages requiring // augmentation are known before before any package is // imported. // Pass 1: parse the sets of files for each package. var pkgs []*initialPkg for len(args) > 0 { arg := args[0] args = args[1:] if arg == "--" { break // consume "--" and return the remaining args } if strings.HasSuffix(arg, ".go") { // Assume arg is a comma-separated list of *.go files // comprising a single package. pkg, err := initialPackageFromFiles(imp.Fset, arg) if err != nil { return nil, nil, err } pkgs = append(pkgs, pkg) } else { // Assume arg is a directory name denoting a // package, perhaps plus an external test // package unless prefixed by "notest:". path := strings.TrimPrefix(arg, "notest:") if path == "unsafe" { continue // ignore; has no PackageInfo } pkg := &initialPkg{ path: path, importable: true, } pkgs = append(pkgs, pkg) if path != arg { continue // had "notest:" prefix } if imp.config.Build == nil { continue // can't locate *_test.go files } // TODO(adonovan): due to limitations of the current type // checker design, we can augment at most one package. if len(imp.augment) > 0 { continue // don't attempt a second } // Load the external test package. xtestFiles, err := parsePackageFiles(imp.config.Build, imp.Fset, path, "x") if err != nil { return nil, nil, err } if len(xtestFiles) > 0 { pkgs = append(pkgs, &initialPkg{ path: path + "_test", importable: false, files: xtestFiles, }) } // Mark the non-xtest package for augmentation with // in-package *_test.go files when we import it below. imp.augment[pkg.path] = true } } // Pass 2: type-check each set of files to make a package. var infos []*PackageInfo imports := make(map[string]*types.Package) // keep importBinary happy for _, pkg := range pkgs { var info *PackageInfo if pkg.importable { // import package var err error info, err = imp.doImport0(imports, pkg.path) if err != nil { return nil, nil, err // e.g. parse error (but not type error) } } else { // create package info = imp.CreatePackage(pkg.path, pkg.files...) } infos = append(infos, info) } if len(pkgs) == 0 { return nil, nil, errors.New("no *.go source files nor packages were specified") } return infos, args, nil } // LoadPackage loads and type-checks the package whose import path is // path, plus its necessary dependencies. // func (imp *Importer) LoadPackage(path string) (*PackageInfo, error) { imports := make(map[string]*types.Package) // keep importBinary happy return imp.doImport0(imports, path) } type initialPkg struct { path string // the package's import path importable bool // add package to import map false for main and xtests) files []*ast.File // set of files (non-importable packages only) } // initialPackageFromFiles returns an initialPkg, given a // comma-separated list of *.go source files belonging to the same // directory and possessing the same 'package decl'. // func initialPackageFromFiles(fset *token.FileSet, arg string) (*initialPkg, error) { filenames := strings.Split(arg, ",") for _, filename := range filenames { if !strings.HasSuffix(filename, ".go") { return nil, fmt.Errorf("not a *.go source file: %q", filename) } } files, err := ParseFiles(fset, ".", filenames...) if err != nil { return nil, err } // Take the package name from the 'package decl' in each file, // all of which must match. pkgname := files[0].Name.Name for i, file := range files[1:] { if pn := file.Name.Name; pn != pkgname { err := fmt.Errorf("can't load package: found packages %s (%s) and %s (%s)", pkgname, filenames[0], pn, filenames[i]) return nil, err } // TODO(adonovan): check dirnames are equal, like 'go build' does. } return &initialPkg{ path: pkgname, importable: false, files: files, }, nil }