Add generated file

This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
xing-yang
2018-07-12 10:55:15 -07:00
parent 36b1de0341
commit e213d1890d
17729 changed files with 5090889 additions and 0 deletions

187
vendor/golang.org/x/tools/go/ssa/blockopt.go generated vendored Normal file
View File

@@ -0,0 +1,187 @@
// 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
}
// Degenerate &&/|| ops may result in a straight-line CFG
// containing φ-nodes. (Ideally we'd replace such them with
// their sole operand but that requires Referrers, built later.)
if b.hasPhi() {
return false // not sound without further effort
}
// 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.WriteTo(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()
}

2379
vendor/golang.org/x/tools/go/ssa/builder.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

500
vendor/golang.org/x/tools/go/ssa/builder_test.go generated vendored Normal file
View File

@@ -0,0 +1,500 @@
// 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 (
"bytes"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"os"
"reflect"
"sort"
"strings"
"testing"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
// Tests that programs partially loaded from gc object files contain
// functions with no code for the external portions, but are otherwise ok.
func TestBuildPackage(t *testing.T) {
input := `
package main
import (
"bytes"
"io"
"testing"
)
func main() {
var t testing.T
t.Parallel() // static call to external declared method
t.Fail() // static call to promoted external declared method
testing.Short() // static call to external package-level function
var w io.Writer = new(bytes.Buffer)
w.Write(nil) // interface invoke of external declared method
}
`
// Parse the file.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "input.go", input, 0)
if err != nil {
t.Error(err)
return
}
// Build an SSA program from the parsed file.
// Load its dependencies from gc binary export data.
mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
t.Error(err)
return
}
// The main package, its direct and indirect dependencies are loaded.
deps := []string{
// directly imported dependencies:
"bytes", "io", "testing",
// indirect dependencies mentioned by
// the direct imports' export data
"sync", "unicode", "time",
}
prog := mainPkg.Prog
all := prog.AllPackages()
if len(all) <= len(deps) {
t.Errorf("unexpected set of loaded packages: %q", all)
}
for _, path := range deps {
pkg := prog.ImportedPackage(path)
if pkg == nil {
t.Errorf("package not loaded: %q", path)
continue
}
// External packages should have no function bodies (except for wrappers).
isExt := pkg != mainPkg
// init()
if isExt && !isEmpty(pkg.Func("init")) {
t.Errorf("external package %s has non-empty init", pkg)
} else if !isExt && isEmpty(pkg.Func("init")) {
t.Errorf("main package %s has empty init", pkg)
}
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *ssa.Function:
// Functions at package level.
if isExt && !isEmpty(mem) {
t.Errorf("external function %s is non-empty", mem)
} else if !isExt && isEmpty(mem) {
t.Errorf("function %s is empty", mem)
}
case *ssa.Type:
// Methods of named types T.
// (In this test, all exported methods belong to *T not T.)
if !isExt {
t.Fatalf("unexpected name type in main package: %s", mem)
}
mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
m := prog.MethodValue(mset.At(i))
// For external types, only synthetic wrappers have code.
expExt := !strings.Contains(m.Synthetic, "wrapper")
if expExt && !isEmpty(m) {
t.Errorf("external method %s is non-empty: %s",
m, m.Synthetic)
} else if !expExt && isEmpty(m) {
t.Errorf("method function %s is empty: %s",
m, m.Synthetic)
}
}
}
}
}
expectedCallee := []string{
"(*testing.T).Parallel",
"(*testing.common).Fail",
"testing.Short",
"N/A",
}
callNum := 0
for _, b := range mainPkg.Func("main").Blocks {
for _, instr := range b.Instrs {
switch instr := instr.(type) {
case ssa.CallInstruction:
call := instr.Common()
if want := expectedCallee[callNum]; want != "N/A" {
got := call.StaticCallee().String()
if want != got {
t.Errorf("call #%d from main.main: got callee %s, want %s",
callNum, got, want)
}
}
callNum++
}
}
}
if callNum != 4 {
t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
}
}
// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
func TestRuntimeTypes(t *testing.T) {
tests := []struct {
input string
want []string
}{
// An exported package-level type is needed.
{`package A; type T struct{}; func (T) f() {}`,
[]string{"*p.T", "p.T"},
},
// An unexported package-level type is not needed.
{`package B; type t struct{}; func (t) f() {}`,
nil,
},
// Subcomponents of type of exported package-level var are needed.
{`package C; import "bytes"; var V struct {*bytes.Buffer}`,
[]string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
},
// Subcomponents of type of unexported package-level var are not needed.
{`package D; import "bytes"; var v struct {*bytes.Buffer}`,
nil,
},
// Subcomponents of type of exported package-level function are needed.
{`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
},
// Subcomponents of type of unexported package-level function are not needed.
{`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
nil,
},
// Subcomponents of type of exported method of uninstantiated unexported type are not needed.
{`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
nil,
},
// ...unless used by MakeInterface.
{`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
[]string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
},
// Subcomponents of type of unexported method are not needed.
{`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
[]string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
},
// Local types aren't needed.
{`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
nil,
},
// ...unless used by MakeInterface.
{`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
[]string{"*bytes.Buffer", "*p.T", "p.T"},
},
// Types used as operand of MakeInterface are needed.
{`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
},
// MakeInterface is optimized away when storing to a blank.
{`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
nil,
},
}
for _, test := range tests {
// Parse the file.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "input.go", test.input, 0)
if err != nil {
t.Errorf("test %q: %s", test.input[:15], err)
continue
}
// Create a single-file main package.
// Load dependencies from gc binary export data.
ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
t.Errorf("test %q: %s", test.input[:15], err)
continue
}
var typstrs []string
for _, T := range ssapkg.Prog.RuntimeTypes() {
typstrs = append(typstrs, T.String())
}
sort.Strings(typstrs)
if !reflect.DeepEqual(typstrs, test.want) {
t.Errorf("test 'package %s': got %q, want %q",
f.Name.Name, typstrs, test.want)
}
}
}
// TestInit tests that synthesized init functions are correctly formed.
// Bare init functions omit calls to dependent init functions and the use of
// an init guard. They are useful in cases where the client uses a different
// calling convention for init functions, or cases where it is easier for a
// client to analyze bare init functions. Both of these aspects are used by
// the llgo compiler for simpler integration with gccgo's runtime library,
// and to simplify the analysis whereby it deduces which stores to globals
// can be lowered to global initializers.
func TestInit(t *testing.T) {
tests := []struct {
mode ssa.BuilderMode
input, want string
}{
{0, `package A; import _ "errors"; var i int = 42`,
`# Name: A.init
# Package: A
# Synthetic: package initializer
func init():
0: entry P:0 S:2
t0 = *init$guard bool
if t0 goto 2 else 1
1: init.start P:1 S:1
*init$guard = true:bool
t1 = errors.init() ()
*i = 42:int
jump 2
2: init.done P:2 S:0
return
`},
{ssa.BareInits, `package B; import _ "errors"; var i int = 42`,
`# Name: B.init
# Package: B
# Synthetic: package initializer
func init():
0: entry P:0 S:0
*i = 42:int
return
`},
}
for _, test := range tests {
// Create a single-file main package.
var conf loader.Config
f, err := conf.ParseFile("<input>", test.input)
if err != nil {
t.Errorf("test %q: %s", test.input[:15], err)
continue
}
conf.CreateFromFiles(f.Name.Name, f)
lprog, err := conf.Load()
if err != nil {
t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
continue
}
prog := ssautil.CreateProgram(lprog, test.mode)
mainPkg := prog.Package(lprog.Created[0].Pkg)
prog.Build()
initFunc := mainPkg.Func("init")
if initFunc == nil {
t.Errorf("test 'package %s': no init function", f.Name.Name)
continue
}
var initbuf bytes.Buffer
_, err = initFunc.WriteTo(&initbuf)
if err != nil {
t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
continue
}
if initbuf.String() != test.want {
t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
}
}
}
// TestSyntheticFuncs checks that the expected synthetic functions are
// created, reachable, and not duplicated.
func TestSyntheticFuncs(t *testing.T) {
const input = `package P
type T int
func (T) f() int
func (*T) g() int
var (
// thunks
a = T.f
b = T.f
c = (struct{T}).f
d = (struct{T}).f
e = (*T).g
f = (*T).g
g = (struct{*T}).g
h = (struct{*T}).g
// bounds
i = T(0).f
j = T(0).f
k = new(T).g
l = new(T).g
// wrappers
m interface{} = struct{T}{}
n interface{} = struct{T}{}
o interface{} = struct{*T}{}
p interface{} = struct{*T}{}
q interface{} = new(struct{T})
r interface{} = new(struct{T})
s interface{} = new(struct{*T})
t interface{} = new(struct{*T})
)
`
// Parse
var conf loader.Config
f, err := conf.ParseFile("<input>", input)
if err != nil {
t.Fatalf("parse: %v", err)
}
conf.CreateFromFiles(f.Name.Name, f)
// Load
lprog, err := conf.Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
// Create and build SSA
prog := ssautil.CreateProgram(lprog, 0)
prog.Build()
// Enumerate reachable synthetic functions
want := map[string]string{
"(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
"(P.T).f$bound": "bound method wrapper for func (P.T).f() int",
"(*P.T).g$thunk": "thunk for func (*P.T).g() int",
"(P.T).f$thunk": "thunk for func (P.T).f() int",
"(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
"(struct{P.T}).f$thunk": "thunk for func (P.T).f() int",
"(*P.T).f": "wrapper for func (P.T).f() int",
"(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
"(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
"(*struct{P.T}).f": "wrapper for func (P.T).f() int",
"(*struct{P.T}).g": "wrapper for func (*P.T).g() int",
"(struct{*P.T}).f": "wrapper for func (P.T).f() int",
"(struct{*P.T}).g": "wrapper for func (*P.T).g() int",
"(struct{P.T}).f": "wrapper for func (P.T).f() int",
"P.init": "package initializer",
}
for fn := range ssautil.AllFunctions(prog) {
if fn.Synthetic == "" {
continue
}
name := fn.String()
wantDescr, ok := want[name]
if !ok {
t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
continue
}
delete(want, name)
if wantDescr != fn.Synthetic {
t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
}
}
for fn, descr := range want {
t.Errorf("want func: %q: %q", fn, descr)
}
}
// TestPhiElimination ensures that dead phis, including those that
// participate in a cycle, are properly eliminated.
func TestPhiElimination(t *testing.T) {
const input = `
package p
func f() error
func g(slice []int) {
for {
for range slice {
// e should not be lifted to a dead φ-node.
e := f()
h(e)
}
}
}
func h(error)
`
// The SSA code for this function should look something like this:
// 0:
// jump 1
// 1:
// t0 = len(slice)
// jump 2
// 2:
// t1 = phi [1: -1:int, 3: t2]
// t2 = t1 + 1:int
// t3 = t2 < t0
// if t3 goto 3 else 1
// 3:
// t4 = f()
// t5 = h(t4)
// jump 2
//
// But earlier versions of the SSA construction algorithm would
// additionally generate this cycle of dead phis:
//
// 1:
// t7 = phi [0: nil:error, 2: t8] #e
// ...
// 2:
// t8 = phi [1: t7, 3: t4] #e
// ...
// Parse
var conf loader.Config
f, err := conf.ParseFile("<input>", input)
if err != nil {
t.Fatalf("parse: %v", err)
}
conf.CreateFromFiles("p", f)
// Load
lprog, err := conf.Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
// Create and build SSA
prog := ssautil.CreateProgram(lprog, 0)
p := prog.Package(lprog.Package("p").Pkg)
p.Build()
g := p.Func("g")
phis := 0
for _, b := range g.Blocks {
for _, instr := range b.Instrs {
if _, ok := instr.(*ssa.Phi); ok {
phis++
}
}
}
if phis != 1 {
g.WriteTo(os.Stderr)
t.Errorf("expected a single Phi (for the range index), got %d", phis)
}
}

169
vendor/golang.org/x/tools/go/ssa/const.go generated vendored Normal file
View File

@@ -0,0 +1,169 @@
// 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"
exact "go/constant"
"go/token"
"go/types"
"strconv"
)
// NewConst returns a new constant of the specified value and type.
// val must be valid according to the specification of Const.Value.
//
func NewConst(val exact.Value, typ types.Type) *Const {
return &Const{typ, val}
}
// intConst returns an 'int' constant that evaluates to i.
// (i is an int64 in case the host is narrower than the target.)
func intConst(i int64) *Const {
return NewConst(exact.MakeInt64(i), tInt)
}
// nilConst returns a nil constant of the specified type, which may
// be any reference type, including interfaces.
//
func nilConst(typ types.Type) *Const {
return NewConst(nil, typ)
}
// stringConst returns a 'string' constant that evaluates to s.
func stringConst(s string) *Const {
return NewConst(exact.MakeString(s), tString)
}
// zeroConst returns a new "zero" constant of the specified type,
// which must not be an array or struct type: the zero values of
// aggregates are well-defined but cannot be represented by Const.
//
func zeroConst(t types.Type) *Const {
switch t := t.(type) {
case *types.Basic:
switch {
case t.Info()&types.IsBoolean != 0:
return NewConst(exact.MakeBool(false), t)
case t.Info()&types.IsNumeric != 0:
return NewConst(exact.MakeInt64(0), t)
case t.Info()&types.IsString != 0:
return NewConst(exact.MakeString(""), t)
case t.Kind() == types.UnsafePointer:
fallthrough
case t.Kind() == types.UntypedNil:
return nilConst(t)
default:
panic(fmt.Sprint("zeroConst for unexpected type:", t))
}
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
return nilConst(t)
case *types.Named:
return NewConst(zeroConst(t.Underlying()).Value, t)
case *types.Array, *types.Struct, *types.Tuple:
panic(fmt.Sprint("zeroConst applied to aggregate:", t))
}
panic(fmt.Sprint("zeroConst: unexpected ", t))
}
func (c *Const) RelString(from *types.Package) string {
var s string
if c.Value == nil {
s = "nil"
} else if c.Value.Kind() == exact.String {
s = exact.StringVal(c.Value)
const max = 20
// TODO(adonovan): don't cut a rune in half.
if len(s) > max {
s = s[:max-3] + "..." // abbreviate
}
s = strconv.Quote(s)
} else {
s = c.Value.String()
}
return s + ":" + relType(c.Type(), from)
}
func (c *Const) Name() string {
return c.RelString(nil)
}
func (c *Const) String() string {
return c.Name()
}
func (c *Const) Type() types.Type {
return c.typ
}
func (c *Const) Referrers() *[]Instruction {
return nil
}
func (c *Const) Parent() *Function { return nil }
func (c *Const) Pos() token.Pos {
return token.NoPos
}
// IsNil returns true if this constant represents a typed or untyped nil value.
func (c *Const) IsNil() bool {
return c.Value == nil
}
// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
// Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer.
//
func (c *Const) Int64() int64 {
switch x := exact.ToInt(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 := exact.ToInt(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)
}

263
vendor/golang.org/x/tools/go/ssa/create.go generated vendored Normal file
View File

@@ -0,0 +1,263 @@
// 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"
"go/types"
"os"
"sync"
"golang.org/x/tools/go/types/typeutil"
)
// NewProgram returns a new SSA Program.
//
// mode controls diagnostics and checking during SSA construction.
//
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
prog := &Program{
Fset: fset,
imported: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
thunks: make(map[selectionKey]*Function),
bounds: make(map[*types.Func]*Function),
mode: mode,
}
h := typeutil.MakeHasher() // protected by methodsMu, in effect
prog.methodSets.SetHasher(h)
prog.canon.SetHasher(h)
return prog
}
// memberFromObject populates package pkg with a member for the
// typechecker object obj.
//
// For objects from Go source code, syntax is the associated syntax
// tree (for funcs and vars only); it will be used during the build
// phase.
//
func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
name := obj.Name()
switch obj := obj.(type) {
case *types.Builtin:
if pkg.Pkg != types.Unsafe {
panic("unexpected builtin object: " + obj.String())
}
case *types.TypeName:
pkg.Members[name] = &Type{
object: obj,
pkg: pkg,
}
case *types.Const:
c := &NamedConst{
object: obj,
Value: NewConst(obj.Val(), obj.Type()),
pkg: pkg,
}
pkg.values[obj] = c.Value
pkg.Members[name] = c
case *types.Var:
g := &Global{
Pkg: pkg,
name: name,
object: obj,
typ: types.NewPointer(obj.Type()), // address
pos: obj.Pos(),
}
pkg.values[obj] = g
pkg.Members[name] = g
case *types.Func:
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil && name == "init" {
pkg.ninit++
name = fmt.Sprintf("init#%d", pkg.ninit)
}
fn := &Function{
name: name,
object: obj,
Signature: sig,
syntax: syntax,
pos: obj.Pos(),
Pkg: pkg,
Prog: pkg.Prog,
}
if syntax == nil {
fn.Synthetic = "loaded from gc object file"
}
pkg.values[obj] = fn
if sig.Recv() == nil {
pkg.Members[name] = fn // package-level function
}
default: // (incl. *types.Package)
panic("unexpected Object type: " + obj.String())
}
}
// membersFromDecl populates package pkg with members for each
// typechecker object (var, func, const or type) associated with the
// specified decl.
//
func membersFromDecl(pkg *Package, decl ast.Decl) {
switch decl := decl.(type) {
case *ast.GenDecl: // import, const, type or var
switch decl.Tok {
case token.CONST:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], nil)
}
}
}
case token.VAR:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], spec)
}
}
}
case token.TYPE:
for _, spec := range decl.Specs {
id := spec.(*ast.TypeSpec).Name
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], nil)
}
}
}
case *ast.FuncDecl:
id := decl.Name
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], decl)
}
}
}
// CreatePackage constructs and returns an SSA Package from the
// specified type-checked, error-free file ASTs, and populates its
// Members mapping.
//
// importable determines whether this package should be returned by a
// subsequent call to ImportedPackage(pkg.Path()).
//
// The real work of building SSA form for each function is not done
// until a subsequent call to Package.Build().
//
func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
p := &Package{
Prog: prog,
Members: make(map[string]Member),
values: make(map[types.Object]Value),
Pkg: pkg,
info: info, // transient (CREATE and BUILD phases)
files: files, // transient (CREATE and BUILD phases)
}
// Add init() function.
p.init = &Function{
name: "init",
Signature: new(types.Signature),
Synthetic: "package initializer",
Pkg: p,
Prog: prog,
}
p.Members[p.init.name] = p.init
// CREATE phase.
// Allocate all package members: vars, funcs, consts and types.
if len(files) > 0 {
// Go source package.
for _, file := range files {
for _, decl := range file.Decls {
membersFromDecl(p, decl)
}
}
} else {
// GC-compiled binary package (or "unsafe")
// No code.
// No position information.
scope := p.Pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
memberFromObject(p, obj, nil)
if obj, ok := obj.(*types.TypeName); ok {
if named, ok := obj.Type().(*types.Named); ok {
for i, n := 0, named.NumMethods(); i < n; i++ {
memberFromObject(p, named.Method(i), nil)
}
}
}
}
}
if prog.mode&BareInits == 0 {
// Add initializer guard variable.
initguard := &Global{
Pkg: p,
name: "init$guard",
typ: types.NewPointer(tBool),
}
p.Members[initguard.Name()] = initguard
}
if prog.mode&GlobalDebug != 0 {
p.SetDebugMode(true)
}
if prog.mode&PrintPackages != 0 {
printMu.Lock()
p.WriteTo(os.Stdout)
printMu.Unlock()
}
if importable {
prog.imported[p.Pkg.Path()] = p
}
prog.packages[p.Pkg] = p
return p
}
// printMu serializes printing of Packages/Functions to stdout.
var printMu sync.Mutex
// AllPackages returns a new slice containing all packages in the
// program prog in unspecified order.
//
func (prog *Program) AllPackages() []*Package {
pkgs := make([]*Package, 0, len(prog.packages))
for _, pkg := range prog.packages {
pkgs = append(pkgs, pkg)
}
return pkgs
}
// ImportedPackage returns the importable SSA Package whose import
// path is path, or nil if no such SSA package has been created.
//
// Not all packages are importable. For example, no import
// declaration can resolve to the x_test package created by 'go test'
// or the ad-hoc main package created 'go build foo.go'.
//
func (prog *Program) ImportedPackage(path string) *Package {
return prog.imported[path]
}

123
vendor/golang.org/x/tools/go/ssa/doc.go generated vendored Normal file
View File

@@ -0,0 +1,123 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// 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. Higher-level control flow constructs
// such as multi-way branch can be reconstructed as needed; see
// ssautil.Switches() for an example.
//
// To construct an SSA-form program, call ssautil.CreateProgram on a
// loader.Program, a set of type-checked packages created from
// parsed Go source files. The resulting ssa.Program contains all the
// packages and their members, but SSA code is not created for
// function bodies until a subsequent call to (*Package).Build.
//
// 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.
// - Node: a Value or Instruction (emphasizing its membership in the SSA value graph)
//
// 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 ✔ ✔
// *ChangeInterface ✔ ✔
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
// *FieldAddr ✔ ✔
// *FreeVar ✔
// *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.
//
// The ssa/ssautil package provides various utilities that depend only
// on the public API of this package.
//
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
// 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 // import "golang.org/x/tools/go/ssa"

341
vendor/golang.org/x/tools/go/ssa/dom.go generated vendored Normal file
View File

@@ -0,0 +1,341 @@
// 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 (
"bytes"
"fmt"
"math/big"
"os"
"sort"
)
// Idom returns the block that immediately dominates b:
// its parent in the dominator tree, if any.
// Neither the entry node (b.Index==0) nor recover node
// (b==b.Parent().Recover()) have a parent.
//
func (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }
// Dominees returns the list of blocks that b immediately dominates:
// its children in the dominator tree.
//
func (b *BasicBlock) Dominees() []*BasicBlock { return b.dom.children }
// Dominates reports whether b dominates c.
func (b *BasicBlock) Dominates(c *BasicBlock) bool {
return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
}
type byDomPreorder []*BasicBlock
func (a byDomPreorder) Len() int { return len(a) }
func (a byDomPreorder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDomPreorder) Less(i, j int) bool { return a[i].dom.pre < a[j].dom.pre }
// DomPreorder returns a new slice containing the blocks of f in
// dominator tree preorder.
//
func (f *Function) DomPreorder() []*BasicBlock {
n := len(f.Blocks)
order := make(byDomPreorder, n, n)
copy(order, f.Blocks)
sort.Sort(order)
return order
}
// domInfo contains a BasicBlock's dominance information.
type domInfo struct {
idom *BasicBlock // immediate dominator (parent in domtree)
children []*BasicBlock // nodes immediately dominated by this one
pre, post int32 // pre- and post-order numbering within domtree
}
// ltState holds the working state for Lengauer-Tarjan algorithm
// (during which domInfo.pre is repurposed for CFG DFS preorder number).
type ltState struct {
// Each slice is indexed by b.Index.
sdom []*BasicBlock // b's semidominator
parent []*BasicBlock // b's parent in DFS traversal of CFG
ancestor []*BasicBlock // b's ancestor with least sdom
}
// dfs implements the depth-first search part of the LT algorithm.
func (lt *ltState) dfs(v *BasicBlock, i int32, preorder []*BasicBlock) int32 {
preorder[i] = v
v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
i++
lt.sdom[v.Index] = v
lt.link(nil, v)
for _, w := range v.Succs {
if lt.sdom[w.Index] == nil {
lt.parent[w.Index] = v
i = lt.dfs(w, i, preorder)
}
}
return i
}
// eval implements the EVAL part of the LT algorithm.
func (lt *ltState) eval(v *BasicBlock) *BasicBlock {
// TODO(adonovan): opt: do path compression per simple LT.
u := v
for ; lt.ancestor[v.Index] != nil; v = lt.ancestor[v.Index] {
if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
u = v
}
}
return u
}
// link implements the LINK part of the LT algorithm.
func (lt *ltState) link(v, w *BasicBlock) {
lt.ancestor[w.Index] = 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
// reordering is due to Georgiadis.
// Clear any previous domInfo.
for _, b := range f.Blocks {
b.dom = domInfo{}
}
n := len(f.Blocks)
// Allocate space for 5 contiguous [n]*BasicBlock arrays:
// sdom, parent, ancestor, preorder, buckets.
space := make([]*BasicBlock, 5*n, 5*n)
lt := ltState{
sdom: space[0:n],
parent: space[n : 2*n],
ancestor: space[2*n : 3*n],
}
// Step 1. Number vertices by depth-first preorder.
preorder := space[3*n : 4*n]
root := f.Blocks[0]
prenum := lt.dfs(root, 0, preorder)
recover := f.Recover
if recover != nil {
lt.dfs(recover, prenum, preorder)
}
buckets := space[4*n : 5*n]
copy(buckets, preorder)
// In reverse preorder...
for i := int32(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.dom.pre] {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < i {
v.dom.idom = u
} else {
v.dom.idom = w
}
}
// Step 2. Compute the semidominators of all nodes.
lt.sdom[w.Index] = lt.parent[w.Index]
for _, v := range w.Preds {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
lt.sdom[w.Index] = lt.sdom[u.Index]
}
}
lt.link(lt.parent[w.Index], w)
if lt.parent[w.Index] == lt.sdom[w.Index] {
w.dom.idom = lt.parent[w.Index]
} else {
buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
buckets[lt.sdom[w.Index].dom.pre] = w
}
}
// The final 'Step 3' is now outside the loop.
for v := buckets[0]; v != root; v = buckets[v.dom.pre] {
v.dom.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.dom.idom = nil
} else {
if w.dom.idom != lt.sdom[w.Index] {
w.dom.idom = w.dom.idom.dom.idom
}
// Calculate Children relation as inverse of Idom.
w.dom.idom.dom.children = append(w.dom.idom.dom.children, w)
}
}
pre, post := numberDomTree(root, 0, 0)
if recover != nil {
numberDomTree(recover, pre, post)
}
// 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.
//
func numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
v.dom.pre = pre
pre++
for _, child := range v.dom.children {
pre, post = numberDomTree(child, pre, post)
}
v.dom.post = post
post++
return pre, 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 {
// A 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 := b.Dominates(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
}
}
}
preorder := f.DomPreorder()
for _, b := range f.Blocks {
if got := preorder[b.dom.pre]; got != b {
fmt.Fprintf(os.Stderr, "preorder[%d]==%s, want %s\n", b.dom.pre, got, b)
ok = false
}
}
if !ok {
panic("sanityCheckDomTree failed for " + f.String())
}
}
// Printing functions ----------------------------------------
// printDomTree prints the dominator tree as text, using indentation.
func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) {
fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v)
for _, child := range v.dom.children {
printDomTreeText(buf, child, indent+1)
}
}
// printDomTreeDot prints the dominator tree of f in AT&T GraphViz
// (.dot) format.
func printDomTreeDot(buf *bytes.Buffer, f *Function) {
fmt.Fprintln(buf, "//", f)
fmt.Fprintln(buf, "digraph domtree {")
for i, b := range f.Blocks {
v := b.dom
fmt.Fprintf(buf, "\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(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre)
}
// CFG edges.
for _, pred := range b.Preds {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre)
}
}
fmt.Fprintln(buf, "}")
}

468
vendor/golang.org/x/tools/go/ssa/emit.go generated vendored Normal file
View File

@@ -0,0 +1,468 @@
// 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 (
"fmt"
"go/ast"
"go/token"
"go/types"
)
// emitNew emits to f a new (heap Alloc) instruction allocating an
// object of type typ. pos is the optional source location.
//
func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc {
v := &Alloc{Heap: true}
v.setType(types.NewPointer(typ))
v.setPos(pos)
f.emit(v)
return v
}
// emitLoad emits to f an instruction to load the address addr into a
// new temporary, and returns the value so defined.
//
func emitLoad(f *Function, addr Value) *UnOp {
v := &UnOp{Op: token.MUL, X: addr}
v.setType(deref(addr.Type()))
f.emit(v)
return v
}
// emitDebugRef emits to f a DebugRef pseudo-instruction associating
// expression e with value v.
//
func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
if !f.debugInfo() {
return // debugging not enabled
}
if v == nil || e == nil {
panic("nil")
}
var obj types.Object
e = unparen(e)
if id, ok := e.(*ast.Ident); ok {
if isBlankIdent(id) {
return
}
obj = f.Pkg.objectOf(id)
switch obj.(type) {
case *types.Nil, *types.Const, *types.Builtin:
return
}
}
f.emit(&DebugRef{
X: v,
Expr: e,
IsAddr: isAddr,
object: obj,
})
}
// emitArith emits to f code to compute the binary operation op(x, y)
// where op is an eager shift, logical or arithmetic operation.
// (Use emitCompare() for comparisons and Builder.logicalBinop() for
// non-eager operations.)
//
func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value {
switch op {
case token.SHL, token.SHR:
x = emitConv(f, x, t)
// y may be signed or an 'untyped' constant.
// TODO(adonovan): whence signed values?
if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
y = emitConv(f, y, types.Typ[types.Uint64])
}
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
x = emitConv(f, x, t)
y = emitConv(f, y, t)
default:
panic("illegal op in emitArith: " + op.String())
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(t)
return f.emit(v)
}
// emitCompare emits to f code compute the boolean result of
// comparison comparison 'x op y'.
//
func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
xt := x.Type().Underlying()
yt := y.Type().Underlying()
// Special case to optimise a tagless SwitchStmt so that
// these are equivalent
// switch { case e: ...}
// switch true { case e: ... }
// if e==true { ... }
// even in the case when e's type is an interface.
// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
if x == vTrue && op == token.EQL {
if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
return y
}
}
if types.Identical(xt, yt) {
// no conversion necessary
} else if _, ok := xt.(*types.Interface); ok {
y = emitConv(f, y, x.Type())
} else if _, ok := yt.(*types.Interface); ok {
x = emitConv(f, x, y.Type())
} else if _, ok := x.(*Const); ok {
x = emitConv(f, x, y.Type())
} else if _, ok := y.(*Const); ok {
y = emitConv(f, y, x.Type())
} else {
// other cases, e.g. channels. No-op.
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(tBool)
return f.emit(v)
}
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named type.
//
func isValuePreserving(ut_src, ut_dst types.Type) bool {
// Identical underlying types?
if structTypesIdentical(ut_dst, ut_src) {
return true
}
switch ut_dst.(type) {
case *types.Chan:
// Conversion between channel types?
_, ok := ut_src.(*types.Chan)
return ok
case *types.Pointer:
// Conversion between pointers with identical base types?
_, ok := ut_src.(*types.Pointer)
return ok
}
return false
}
// emitConv emits to f code to convert Value val to exactly type typ,
// and returns the converted value. Implicit conversions are required
// by language assignability rules in assignments, parameter passing,
// etc. Conversions cannot fail dynamically.
//
func emitConv(f *Function, val Value, typ types.Type) Value {
t_src := val.Type()
// Identical types? Conversion is a no-op.
if types.Identical(t_src, typ) {
return val
}
ut_dst := typ.Underlying()
ut_src := t_src.Underlying()
// Just a change of type, but not value or representation?
if isValuePreserving(ut_src, ut_dst) {
c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
}
// Conversion to, or construction of a value of, an interface type?
if _, ok := ut_dst.(*types.Interface); ok {
// Assignment from one interface type to another?
if _, ok := ut_src.(*types.Interface); ok {
c := &ChangeInterface{X: val}
c.setType(typ)
return f.emit(c)
}
// Untyped nil constant? Return interface-typed nil constant.
if ut_src == tUntypedNil {
return nilConst(typ)
}
// Convert (non-nil) "untyped" literals to their default type.
if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
val = emitConv(f, val, DefaultType(ut_src))
}
f.Pkg.Prog.needMethodsOf(val.Type())
mi := &MakeInterface{X: val}
mi.setType(typ)
return f.emit(mi)
}
// Conversion of a compile-time constant value?
if c, ok := val.(*Const); ok {
if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() {
// Conversion of a compile-time constant to
// another constant type results in a new
// constant of the destination type and
// (initially) the same abstract value.
// We don't truncate the value yet.
return NewConst(c.Value, typ)
}
// We're converting from constant to non-constant type,
// e.g. string -> []byte/[]rune.
}
// A representation-changing conversion?
// At least one of {ut_src,ut_dst} must be *Basic.
// (The other may be []byte or []rune.)
_, ok1 := ut_src.(*types.Basic)
_, ok2 := ut_dst.(*types.Basic)
if ok1 || ok2 {
c := &Convert{X: val}
c.setType(typ)
return f.emit(c)
}
panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
}
// emitStore emits to f an instruction to store value val at location
// addr, applying implicit conversions as required by assignability rules.
//
func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
s := &Store{
Addr: addr,
Val: emitConv(f, val, deref(addr.Type())),
pos: pos,
}
f.emit(s)
return s
}
// emitJump emits to f a jump to target, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
//
func emitJump(f *Function, target *BasicBlock) {
b := f.currentBlock
b.emit(new(Jump))
addEdge(b, target)
f.currentBlock = nil
}
// emitIf emits to f a conditional jump to tblock or fblock based on
// cond, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
//
func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
b := f.currentBlock
b.emit(&If{Cond: cond})
addEdge(b, tblock)
addEdge(b, fblock)
f.currentBlock = nil
}
// emitExtract emits to f an instruction to extract the index'th
// component of tuple. It returns the extracted value.
//
func emitExtract(f *Function, tuple Value, index int) Value {
e := &Extract{Tuple: tuple, Index: index}
e.setType(tuple.Type().(*types.Tuple).At(index).Type())
return f.emit(e)
}
// emitTypeAssert emits to f a type assertion value := x.(t) and
// returns the value. x.Type() must be an interface.
//
func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{X: x, AssertedType: t}
a.setPos(pos)
a.setType(t)
return f.emit(a)
}
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
// a (value, ok) tuple. x.Type() must be an interface.
//
func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{
X: x,
AssertedType: t,
CommaOk: true,
}
a.setPos(pos)
a.setType(types.NewTuple(
newVar("value", t),
varOk,
))
return f.emit(a)
}
// emitTailCall emits to f a function call in tail position. The
// caller is responsible for all fields of 'call' except its type.
// Intended for wrapper methods.
// Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil.
//
func emitTailCall(f *Function, call *Call) {
tresults := f.Signature.Results()
nr := tresults.Len()
if nr == 1 {
call.typ = tresults.At(0).Type()
} else {
call.typ = tresults
}
tuple := f.emit(call)
var ret Return
switch nr {
case 0:
// no-op
case 1:
ret.Results = []Value{tuple}
default:
for i := 0; i < nr; i++ {
v := emitExtract(f, tuple, i)
// TODO(adonovan): in principle, this is required:
// v = emitConv(f, o.Type, f.Signature.Results[i].Type)
// but in practice emitTailCall is only used when
// the types exactly match.
ret.Results = append(ret.Results, v)
}
}
f.emit(&ret)
f.currentBlock = nil
}
// emitImplicitSelections emits to f code to apply the sequence of
// implicit field selections specified by indices to base value v, and
// returns the selected value.
//
// If v is the address of a struct, the result will be the address of
// a field; if it is the value of a struct, the result will be the
// value of a field.
//
func emitImplicitSelections(f *Function, v Value, indices []int) Value {
for _, index := range indices {
fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff indirectly embedded.
if isPointer(fld.Type()) {
v = emitLoad(f, v)
}
} else {
instr := &Field{
X: v,
Field: index,
}
instr.setType(fld.Type())
v = f.emit(instr)
}
}
return v
}
// emitFieldSelection emits to f code to select the index'th field of v.
//
// If wantAddr, the input must be a pointer-to-struct and the result
// will be the field's address; otherwise the result will be the
// field's value.
// Ident id is used for position and debug info.
//
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff we don't want its address.
if !wantAddr {
v = emitLoad(f, v)
}
} else {
instr := &Field{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(fld.Type())
v = f.emit(instr)
}
emitDebugRef(f, id, v, wantAddr)
return v
}
// zeroValue emits to f code to produce a zero value of type t,
// and returns it.
//
func zeroValue(f *Function, t types.Type) Value {
switch t.Underlying().(type) {
case *types.Struct, *types.Array:
return emitLoad(f, f.addLocal(t, token.NoPos))
default:
return zeroConst(t)
}
}
// createRecoverBlock emits to f a block of code to return after a
// recovered panic, and sets f.Recover to it.
//
// If f's result parameters are named, the code loads and returns
// their current values, otherwise it returns the zero values of their
// type.
//
// Idempotent.
//
func createRecoverBlock(f *Function) {
if f.Recover != nil {
return // already created
}
saved := f.currentBlock
f.Recover = f.newBasicBlock("recover")
f.currentBlock = f.Recover
var results []Value
if f.namedResults != nil {
// Reload NRPs to form value tuple.
for _, r := range f.namedResults {
results = append(results, emitLoad(f, r))
}
} else {
R := f.Signature.Results()
for i, n := 0, R.Len(); i < n; i++ {
T := R.At(i).Type()
// Return zero value of each result type.
results = append(results, zeroValue(f, T))
}
}
f.emit(&Return{Results: results})
f.currentBlock = saved
}

138
vendor/golang.org/x/tools/go/ssa/example_test.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
// 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/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"os"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
const hello = `
package main
import "fmt"
const message = "Hello, World!"
func main() {
fmt.Println(message)
}
`
// This program demonstrates how to run the SSA builder on a single
// package of one or more already-parsed files. Its dependencies are
// loaded from compiler export data. This is what you'd typically use
// for a compiler; it does not depend on golang.org/x/tools/go/loader.
//
// It shows the printed representation of packages, functions, and
// instructions. Within the function listing, the name of each
// BasicBlock such as ".0.entry" is printed left-aligned, followed by
// the block's Instructions.
//
// For each instruction that defines an SSA virtual register
// (i.e. implements Value), the type of that value is shown in the
// right column.
//
// Build and run the ssadump.go program if you want a standalone tool
// with similar functionality. It is located at
// golang.org/x/tools/cmd/ssadump.
//
func ExampleBuildPackage() {
// Parse the source files.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
if err != nil {
fmt.Print(err) // parse error
return
}
files := []*ast.File{f}
// Create the type-checker's package.
pkg := types.NewPackage("hello", "")
// Type-check the package, load dependencies.
// Create and build the SSA program.
hello, _, err := ssautil.BuildPackage(
&types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions)
if err != nil {
fmt.Print(err) // type error in some package
return
}
// Print out the package.
hello.WriteTo(os.Stdout)
// Print out the package-level functions.
hello.Func("init").WriteTo(os.Stdout)
hello.Func("main").WriteTo(os.Stdout)
// Output:
//
// package hello:
// func init func()
// var init$guard bool
// func main func()
// const message message = "Hello, World!":untyped string
//
// # Name: hello.init
// # Package: hello
// # Synthetic: package initializer
// func init():
// 0: entry P:0 S:2
// t0 = *init$guard bool
// if t0 goto 2 else 1
// 1: init.start P:1 S:1
// *init$guard = true:bool
// t1 = fmt.init() ()
// jump 2
// 2: init.done P:2 S:0
// return
//
// # Name: hello.main
// # Package: hello
// # Location: hello.go:8:6
// func main():
// 0: entry P:0 S:0
// t0 = new [1]interface{} (varargs) *[1]interface{}
// t1 = &t0[0:int] *interface{}
// t2 = make interface{} <- string ("Hello, World!":string) interface{}
// *t1 = t2
// t3 = slice t0[:] []interface{}
// t4 = fmt.Println(t3...) (n int, err error)
// return
}
// This program shows how to load a main package (cmd/cover) and all its
// dependencies from source, using the loader, and then build SSA code
// for the entire program. This is what you'd typically use for a
// whole-program analysis.
//
func ExampleLoadProgram() {
// Load cmd/cover and its dependencies.
var conf loader.Config
conf.Import("cmd/cover")
lprog, err := conf.Load()
if err != nil {
fmt.Print(err) // type error in some package
return
}
// Create SSA-form program representation.
prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions)
// Build SSA code for the entire cmd/cover program.
prog.Build()
// Output:
}

689
vendor/golang.org/x/tools/go/ssa/func.go generated vendored Normal file
View File

@@ -0,0 +1,689 @@
// 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 (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"os"
"strings"
)
// addEdge adds a control-flow graph edge from from to to.
func addEdge(from, to *BasicBlock) {
from.Succs = append(from.Succs, to)
to.Preds = append(to.Preds, from)
}
// Parent returns the function that contains block b.
func (b *BasicBlock) Parent() *Function { return b.parent }
// String returns a human-readable label of this block.
// It is not guaranteed unique within the function.
//
func (b *BasicBlock) String() string {
return fmt.Sprintf("%d", b.Index)
}
// emit appends an instruction to the current basic block.
// If the instruction defines a Value, it is returned.
//
func (b *BasicBlock) emit(i Instruction) Value {
i.setBlock(b)
b.Instrs = append(b.Instrs, i)
v, _ := i.(Value)
return v
}
// predIndex returns the i such that b.Preds[i] == c or panics if
// there is none.
func (b *BasicBlock) predIndex(c *BasicBlock) int {
for i, pred := range b.Preds {
if pred == c {
return i
}
}
panic(fmt.Sprintf("no edge %s -> %s", c, b))
}
// hasPhi returns true if b.Instrs contains φ-nodes.
func (b *BasicBlock) hasPhi() bool {
_, ok := b.Instrs[0].(*Phi)
return ok
}
// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
func (b *BasicBlock) phis() []Instruction {
for i, instr := range b.Instrs {
if _, ok := instr.(*Phi); !ok {
return b.Instrs[:i]
}
}
return nil // unreachable in well-formed blocks
}
// replacePred replaces all occurrences of p in b's predecessor list with q.
// Ordinarily there should be at most one.
//
func (b *BasicBlock) replacePred(p, q *BasicBlock) {
for i, pred := range b.Preds {
if pred == p {
b.Preds[i] = q
}
}
}
// replaceSucc replaces all occurrences of p in b's successor list with q.
// Ordinarily there should be at most one.
//
func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
for i, succ := range b.Succs {
if succ == p {
b.Succs[i] = q
}
}
}
// removePred removes all occurrences of p in b's
// predecessor list and φ-nodes.
// Ordinarily there should be at most one.
//
func (b *BasicBlock) removePred(p *BasicBlock) {
phis := b.phis()
// We must preserve edge order for φ-nodes.
j := 0
for i, pred := range b.Preds {
if pred != p {
b.Preds[j] = b.Preds[i]
// Strike out φ-edge too.
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges[j] = phi.Edges[i]
}
j++
}
}
// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
for i := j; i < len(b.Preds); i++ {
b.Preds[i] = nil
for _, instr := range phis {
instr.(*Phi).Edges[i] = nil
}
}
b.Preds = b.Preds[:j]
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges = phi.Edges[:j]
}
}
// Destinations associated with unlabelled for/switch/select stmts.
// We push/pop one of these as we enter/leave each construct and for
// each BranchStmt we scan for the innermost target of the right type.
//
type targets struct {
tail *targets // rest of stack
_break *BasicBlock
_continue *BasicBlock
_fallthrough *BasicBlock
}
// Destinations associated with a labelled block.
// We populate these as labels are encountered in forward gotos or
// labelled statements.
//
type lblock struct {
_goto *BasicBlock
_break *BasicBlock
_continue *BasicBlock
}
// labelledBlock returns the branch target associated with the
// specified label, creating it if needed.
//
func (f *Function) labelledBlock(label *ast.Ident) *lblock {
lb := f.lblocks[label.Obj]
if lb == nil {
lb = &lblock{_goto: f.newBasicBlock(label.Name)}
if f.lblocks == nil {
f.lblocks = make(map[*ast.Object]*lblock)
}
f.lblocks[label.Obj] = lb
}
return lb
}
// addParam adds a (non-escaping) parameter to f.Params of the
// specified name, type and source position.
//
func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
v := &Parameter{
name: name,
typ: typ,
pos: pos,
parent: f,
}
f.Params = append(f.Params, v)
return v
}
func (f *Function) addParamObj(obj types.Object) *Parameter {
name := obj.Name()
if name == "" {
name = fmt.Sprintf("arg%d", len(f.Params))
}
param := f.addParam(name, obj.Type(), obj.Pos())
param.object = obj
return param
}
// addSpilledParam declares a parameter that is pre-spilled to the
// stack; the function body will load/store the spilled location.
// Subsequent lifting will eliminate spills where possible.
//
func (f *Function) addSpilledParam(obj types.Object) {
param := f.addParamObj(obj)
spill := &Alloc{Comment: obj.Name()}
spill.setType(types.NewPointer(obj.Type()))
spill.setPos(obj.Pos())
f.objects[obj] = spill
f.Locals = append(f.Locals, spill)
f.emit(spill)
f.emit(&Store{Addr: spill, Val: param})
}
// startBody initializes the function prior to generating SSA code for its body.
// Precondition: f.Type() already set.
//
func (f *Function) startBody() {
f.currentBlock = f.newBasicBlock("entry")
f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init
}
// createSyntacticParams populates f.Params and generates code (spills
// and named result locals) for all the parameters declared in the
// syntax. In addition it populates the f.objects mapping.
//
// Preconditions:
// f.startBody() was called.
// Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
//
func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
// Receiver (at most one inner iteration).
if recv != nil {
for _, field := range recv.List {
for _, n := range field.Names {
f.addSpilledParam(f.Pkg.info.Defs[n])
}
// Anonymous receiver? No need to spill.
if field.Names == nil {
f.addParamObj(f.Signature.Recv())
}
}
}
// Parameters.
if functype.Params != nil {
n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range functype.Params.List {
for _, n := range field.Names {
f.addSpilledParam(f.Pkg.info.Defs[n])
}
// Anonymous parameter? No need to spill.
if field.Names == nil {
f.addParamObj(f.Signature.Params().At(len(f.Params) - n))
}
}
}
// Named results.
if functype.Results != nil {
for _, field := range functype.Results.List {
// Implicit "var" decl of locals for named results.
for _, n := range field.Names {
f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
}
}
}
}
// numberRegisters assigns numbers to all SSA registers
// (value-defining Instructions) in f, to aid debugging.
// (Non-Instruction Values are named at construction.)
//
func numberRegisters(f *Function) {
v := 0
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
switch instr.(type) {
case Value:
instr.(interface {
setNum(int)
}).setNum(v)
v++
}
}
}
}
// buildReferrers populates the def/use information in all non-nil
// Value.Referrers slice.
// Precondition: all such slices are initially empty.
func buildReferrers(f *Function) {
var rands []*Value
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if r := *rand; r != nil {
if ref := r.Referrers(); ref != nil {
*ref = append(*ref, instr)
}
}
}
}
}
}
// finishBody() finalizes the function after SSA code generation of its body.
func (f *Function) finishBody() {
f.objects = nil
f.currentBlock = nil
f.lblocks = nil
// Don't pin the AST in memory (except in debug mode).
if n := f.syntax; n != nil && !f.debugInfo() {
f.syntax = extentNode{n.Pos(), n.End()}
}
// Remove from f.Locals any Allocs that escape to the heap.
j := 0
for _, l := range f.Locals {
if !l.Heap {
f.Locals[j] = l
j++
}
}
// Nil out f.Locals[j:] to aid GC.
for i := j; i < len(f.Locals); i++ {
f.Locals[i] = nil
}
f.Locals = f.Locals[:j]
optimizeBlocks(f)
buildReferrers(f)
buildDomTree(f)
if f.Prog.mode&NaiveForm == 0 {
// For debugging pre-state of lifting pass:
// numberRegisters(f)
// f.WriteTo(os.Stderr)
lift(f)
}
f.namedResults = nil // (used by lifting)
numberRegisters(f)
if f.Prog.mode&PrintFunctions != 0 {
printMu.Lock()
f.WriteTo(os.Stdout)
printMu.Unlock()
}
if f.Prog.mode&SanityCheckFunctions != 0 {
mustSanityCheck(f, nil)
}
}
// removeNilBlocks eliminates nils from f.Blocks and updates each
// BasicBlock.Index. Use this after any pass that may delete blocks.
//
func (f *Function) removeNilBlocks() {
j := 0
for _, b := range f.Blocks {
if b != nil {
b.Index = j
f.Blocks[j] = b
j++
}
}
// Nil out f.Blocks[j:] to aid GC.
for i := j; i < len(f.Blocks); i++ {
f.Blocks[i] = nil
}
f.Blocks = f.Blocks[:j]
}
// SetDebugMode sets the debug mode for package pkg. If true, all its
// functions will include full debug info. This greatly increases the
// size of the instruction stream, and causes Functions to depend upon
// the ASTs, potentially keeping them live in memory for longer.
//
func (pkg *Package) SetDebugMode(debug bool) {
// TODO(adonovan): do we want ast.File granularity?
pkg.debug = debug
}
// debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool {
return f.Pkg != nil && f.Pkg.debug
}
// addNamedLocal creates a local variable, adds it to function f and
// returns it. Its name and type are taken from obj. Subsequent
// calls to f.lookup(obj) will return the same local.
//
func (f *Function) addNamedLocal(obj types.Object) *Alloc {
l := f.addLocal(obj.Type(), obj.Pos())
l.Comment = obj.Name()
f.objects[obj] = l
return l
}
func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc {
return f.addNamedLocal(f.Pkg.info.Defs[id])
}
// addLocal creates an anonymous local variable of type typ, adds it
// to function f and returns it. pos is the optional source location.
//
func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
v := &Alloc{}
v.setType(types.NewPointer(typ))
v.setPos(pos)
f.Locals = append(f.Locals, v)
f.emit(v)
return v
}
// lookup returns the address of the named variable identified by obj
// that is local to function f or one of its enclosing functions.
// If escaping, the reference comes from a potentially escaping pointer
// expression and the referent must be heap-allocated.
//
func (f *Function) lookup(obj types.Object, escaping bool) Value {
if v, ok := f.objects[obj]; ok {
if alloc, ok := v.(*Alloc); ok && escaping {
alloc.Heap = true
}
return v // function-local var (address)
}
// Definition must be in an enclosing function;
// plumb it through intervening closures.
if f.parent == nil {
panic("no ssa.Value for " + obj.String())
}
outer := f.parent.lookup(obj, true) // escaping
v := &FreeVar{
name: obj.Name(),
typ: outer.Type(),
pos: outer.Pos(),
outer: outer,
parent: f,
}
f.objects[obj] = v
f.FreeVars = append(f.FreeVars, v)
return v
}
// emit emits the specified instruction to function f.
func (f *Function) emit(instr Instruction) Value {
return f.currentBlock.emit(instr)
}
// RelString returns the full name of this function, qualified by
// package name, receiver type, etc.
//
// The specific formatting rules are not guaranteed and may change.
//
// Examples:
// "math.IsNaN" // a package-level function
// "(*bytes.Buffer).Bytes" // a declared method or a wrapper
// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
// "main.main$1" // an anonymous function in main
// "main.init#1" // a declared init function
// "main.init" // the synthesized package initializer
//
// When these functions are referred to from within the same package
// (i.e. from == f.Pkg.Object), they are rendered without the package path.
// For example: "IsNaN", "(*Buffer).Bytes", etc.
//
// All non-synthetic functions have distinct package-qualified names.
// (But two methods may have the same name "(T).f" if one is a synthetic
// wrapper promoting a non-exported method "f" from another package; in
// that case, the strings are equal but the identifiers "f" are distinct.)
//
func (f *Function) RelString(from *types.Package) string {
// Anonymous?
if f.parent != nil {
// An anonymous function's Name() looks like "parentName$1",
// but its String() should include the type/package/etc.
parent := f.parent.RelString(from)
for i, anon := range f.parent.AnonFuncs {
if anon == f {
return fmt.Sprintf("%s$%d", parent, 1+i)
}
}
return f.name // should never happen
}
// Method (declared or wrapper)?
if recv := f.Signature.Recv(); recv != nil {
return f.relMethod(from, recv.Type())
}
// Thunk?
if f.method != nil {
return f.relMethod(from, f.method.Recv())
}
// Bound?
if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
return f.relMethod(from, f.FreeVars[0].Type())
}
// Package-level function?
// Prefix with package name for cross-package references only.
if p := f.pkg(); p != nil && p != from {
return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
// Unknown.
return f.name
}
func (f *Function) relMethod(from *types.Package, recv types.Type) string {
return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
}
// writeSignature writes to buf the signature sig in declaration syntax.
func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) {
buf.WriteString("func ")
if recv := sig.Recv(); recv != nil {
buf.WriteString("(")
if n := params[0].Name(); n != "" {
buf.WriteString(n)
buf.WriteString(" ")
}
types.WriteType(buf, params[0].Type(), types.RelativeTo(from))
buf.WriteString(") ")
}
buf.WriteString(name)
types.WriteSignature(buf, sig, types.RelativeTo(from))
}
func (f *Function) pkg() *types.Package {
if f.Pkg != nil {
return f.Pkg.Pkg
}
return nil
}
var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
func (f *Function) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WriteFunction(&buf, f)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WriteFunction writes to buf a human-readable "disassembly" of f.
func WriteFunction(buf *bytes.Buffer, f *Function) {
fmt.Fprintf(buf, "# Name: %s\n", f.String())
if f.Pkg != nil {
fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
}
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(buf, "# Synthetic:", syn)
}
if pos := f.Pos(); pos.IsValid() {
fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
}
if f.parent != nil {
fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
}
if f.Recover != nil {
fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
}
from := f.pkg()
if f.FreeVars != nil {
buf.WriteString("# Free variables:\n")
for i, fv := range f.FreeVars {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
}
}
if len(f.Locals) > 0 {
buf.WriteString("# Locals:\n")
for i, l := range f.Locals {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from))
}
}
writeSignature(buf, from, f.Name(), f.Signature, f.Params)
buf.WriteString(":\n")
if f.Blocks == nil {
buf.WriteString("\t(external)\n")
}
// NB. column calculations are confused by non-ASCII
// characters and assume 8-space tabs.
const punchcard = 80 // for old time's sake.
const tabwidth = 8
for _, b := range f.Blocks {
if b == nil {
// Corrupt CFG.
fmt.Fprintf(buf, ".nil:\n")
continue
}
n, _ := fmt.Fprintf(buf, "%d:", b.Index)
bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
if false { // CFG debugging
fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
}
for _, instr := range b.Instrs {
buf.WriteString("\t")
switch v := instr.(type) {
case Value:
l := punchcard - tabwidth
// Left-align the instruction.
if name := v.Name(); name != "" {
n, _ := fmt.Fprintf(buf, "%s = ", name)
l -= n
}
n, _ := buf.WriteString(instr.String())
l -= n
// Right-align the type if there's space.
if t := v.Type(); t != nil {
buf.WriteByte(' ')
ts := relType(t, from)
l -= len(ts) + len(" ") // (spaces before and after type)
if l > 0 {
fmt.Fprintf(buf, "%*s", l, "")
}
buf.WriteString(ts)
}
case nil:
// Be robust against bad transforms.
buf.WriteString("<deleted>")
default:
buf.WriteString(instr.String())
}
buf.WriteString("\n")
}
}
fmt.Fprintf(buf, "\n")
}
// newBasicBlock adds to f a new basic block and returns it. It does
// not automatically become the current block for subsequent calls to emit.
// comment is an optional string for more readable debugging output.
//
func (f *Function) newBasicBlock(comment string) *BasicBlock {
b := &BasicBlock{
Index: len(f.Blocks),
Comment: comment,
parent: f,
}
b.Succs = b.succs2[:0]
f.Blocks = append(f.Blocks, b)
return b
}
// NewFunction returns a new synthetic Function instance belonging to
// prog, with its name and signature fields set as specified.
//
// The caller is responsible for initializing the remaining fields of
// the function object, e.g. Pkg, Params, Blocks.
//
// It is practically impossible for clients to construct well-formed
// SSA functions/packages/programs directly, so we assume this is the
// job of the Builder alone. NewFunction exists to provide clients a
// little flexibility. For example, analysis tools may wish to
// construct fake Functions for the root of the callgraph, a fake
// "reflect" package, etc.
//
// TODO(adonovan): think harder about the API here.
//
func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
}
type extentNode [2]token.Pos
func (n extentNode) Pos() token.Pos { return n[0] }
func (n extentNode) End() token.Pos { return n[1] }
// Syntax returns an ast.Node whose Pos/End methods provide the
// lexical extent of the function if it was defined by Go source code
// (f.Synthetic==""), or nil otherwise.
//
// If f was built with debug information (see Package.SetDebugRef),
// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
// function. Otherwise, it is an opaque Node providing only position
// information; this avoids pinning the AST in memory.
//
func (f *Function) Syntax() ast.Node { return f.syntax }

7
vendor/golang.org/x/tools/go/ssa/identical.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// +build go1.8
package ssa
import "go/types"
var structTypesIdentical = types.IdenticalIgnoreTags

7
vendor/golang.org/x/tools/go/ssa/identical_17.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// +build !go1.8
package ssa
import "go/types"
var structTypesIdentical = types.Identical

9
vendor/golang.org/x/tools/go/ssa/identical_test.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
//+build go1.8
package ssa_test
import "testing"
func TestValueForExprStructConv(t *testing.T) {
testValueForExpr(t, "testdata/structconv.go")
}

562
vendor/golang.org/x/tools/go/ssa/interp/external.go generated vendored Normal file
View File

@@ -0,0 +1,562 @@
// 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 (
"go/types"
"math"
"os"
"runtime"
"strings"
"sync/atomic"
"time"
"unsafe"
"golang.org/x/tools/go/ssa"
)
type externalFn func(fr *frame, args []value) value
// TODO(adonovan): fix: reflect.Value abstracts an lvalue or an
// rvalue; Set() causes mutations that can be observed via aliases.
// We have not captured that correctly here.
// Key strings are from Function.String().
var externals = make(map[string]externalFn)
func init() {
// That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd].
for k, v := range map[string]externalFn{
"(*sync.Pool).Get": ext۰sync۰Pool۰Get,
"(*sync.Pool).Put": ext۰nop,
"(reflect.Value).Bool": ext۰reflect۰Value۰Bool,
"(reflect.Value).CanAddr": ext۰reflect۰Value۰CanAddr,
"(reflect.Value).CanInterface": ext۰reflect۰Value۰CanInterface,
"(reflect.Value).Elem": ext۰reflect۰Value۰Elem,
"(reflect.Value).Field": ext۰reflect۰Value۰Field,
"(reflect.Value).Float": ext۰reflect۰Value۰Float,
"(reflect.Value).Index": ext۰reflect۰Value۰Index,
"(reflect.Value).Int": ext۰reflect۰Value۰Int,
"(reflect.Value).Interface": ext۰reflect۰Value۰Interface,
"(reflect.Value).IsNil": ext۰reflect۰Value۰IsNil,
"(reflect.Value).IsValid": ext۰reflect۰Value۰IsValid,
"(reflect.Value).Kind": ext۰reflect۰Value۰Kind,
"(reflect.Value).Len": ext۰reflect۰Value۰Len,
"(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex,
"(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys,
"(reflect.Value).NumField": ext۰reflect۰Value۰NumField,
"(reflect.Value).NumMethod": ext۰reflect۰Value۰NumMethod,
"(reflect.Value).Pointer": ext۰reflect۰Value۰Pointer,
"(reflect.Value).Set": ext۰reflect۰Value۰Set,
"(reflect.Value).String": ext۰reflect۰Value۰String,
"(reflect.Value).Type": ext۰reflect۰Value۰Type,
"(reflect.Value).Uint": ext۰reflect۰Value۰Uint,
"(reflect.error).Error": ext۰reflect۰error۰Error,
"(reflect.rtype).Bits": ext۰reflect۰rtype۰Bits,
"(reflect.rtype).Elem": ext۰reflect۰rtype۰Elem,
"(reflect.rtype).Field": ext۰reflect۰rtype۰Field,
"(reflect.rtype).In": ext۰reflect۰rtype۰In,
"(reflect.rtype).Kind": ext۰reflect۰rtype۰Kind,
"(reflect.rtype).NumField": ext۰reflect۰rtype۰NumField,
"(reflect.rtype).NumIn": ext۰reflect۰rtype۰NumIn,
"(reflect.rtype).NumMethod": ext۰reflect۰rtype۰NumMethod,
"(reflect.rtype).NumOut": ext۰reflect۰rtype۰NumOut,
"(reflect.rtype).Out": ext۰reflect۰rtype۰Out,
"(reflect.rtype).Size": ext۰reflect۰rtype۰Size,
"(reflect.rtype).String": ext۰reflect۰rtype۰String,
"bytes.init": ext۰nop, // avoid asm dependency
"bytes.Equal": ext۰bytes۰Equal,
"bytes.IndexByte": ext۰bytes۰IndexByte,
"hash/crc32.haveSSE42": ext۰crc32۰haveSSE42,
"internal/cpu.cpuid": ext۰cpu۰cpuid,
"internal/syscall/unix.syscall_fcntl": ext۰syscall۰unix۰syscall_fcntl,
"math.Abs": ext۰math۰Abs,
"math.Exp": ext۰math۰Exp,
"math.Float32bits": ext۰math۰Float32bits,
"math.Float32frombits": ext۰math۰Float32frombits,
"math.Float64bits": ext۰math۰Float64bits,
"math.Float64frombits": ext۰math۰Float64frombits,
"math.Ldexp": ext۰math۰Ldexp,
"math.Log": ext۰math۰Log,
"math.Min": ext۰math۰Min,
"math.hasSSE4": ext۰math۰hasSSE4,
"math.hasVectorFacility": ext۰math۰hasVectorFacility,
"os.runtime_args": ext۰os۰runtime_args,
"os.runtime_beforeExit": ext۰nop,
"os/signal.init": ext۰nop,
"reflect.New": ext۰reflect۰New,
"reflect.SliceOf": ext۰reflect۰SliceOf,
"reflect.TypeOf": ext۰reflect۰TypeOf,
"reflect.ValueOf": ext۰reflect۰ValueOf,
"reflect.Zero": ext۰reflect۰Zero,
"reflect.init": ext۰reflect۰Init,
"reflect.valueInterface": ext۰reflect۰valueInterface,
"runtime.Breakpoint": ext۰runtime۰Breakpoint,
"runtime.Caller": ext۰runtime۰Caller,
"runtime.Callers": ext۰runtime۰Callers,
"runtime.FuncForPC": ext۰runtime۰FuncForPC,
"runtime.GC": ext۰runtime۰GC,
"runtime.GOMAXPROCS": ext۰runtime۰GOMAXPROCS,
"runtime.Goexit": ext۰runtime۰Goexit,
"runtime.Gosched": ext۰runtime۰Gosched,
"runtime.init": ext۰nop,
"runtime.KeepAlive": ext۰nop,
"runtime.NumCPU": ext۰runtime۰NumCPU,
"runtime.NumGoroutine": ext۰runtime۰NumGoroutine,
"runtime.ReadMemStats": ext۰runtime۰ReadMemStats,
"runtime.SetFinalizer": ext۰nop, // ignore
"(*runtime.Func).Entry": ext۰runtime۰Func۰Entry,
"(*runtime.Func).FileLine": ext۰runtime۰Func۰FileLine,
"(*runtime.Func).Name": ext۰runtime۰Func۰Name,
"runtime.environ": ext۰runtime۰environ,
"runtime.getgoroot": ext۰runtime۰getgoroot,
"strings.init": ext۰nop, // avoid asm dependency
"strings.Count": ext۰strings۰Count,
"strings.Index": ext۰strings۰Index,
"strings.IndexByte": ext۰strings۰IndexByte,
"sync.runtime_Semacquire": ext۰nop, // unimplementable
"sync.runtime_Semrelease": ext۰nop, // unimplementable
"sync.runtime_Syncsemcheck": ext۰nop, // unimplementable
"sync.runtime_notifyListCheck": ext۰nop,
"sync.runtime_registerPoolCleanup": ext۰nop,
"sync/atomic.AddInt32": ext۰atomic۰AddInt32,
"sync/atomic.AddUint32": ext۰atomic۰AddUint32,
"sync/atomic.CompareAndSwapInt32": ext۰atomic۰CompareAndSwapInt32,
"sync/atomic.CompareAndSwapUint32": ext۰atomic۰CompareAndSwapUint32,
"sync/atomic.LoadInt32": ext۰atomic۰LoadInt32,
"sync/atomic.LoadUint32": ext۰atomic۰LoadUint32,
"sync/atomic.StoreInt32": ext۰atomic۰StoreInt32,
"sync/atomic.StoreUint32": ext۰atomic۰StoreUint32,
"sync/atomic.AddInt64": ext۰atomic۰AddInt64,
"sync/atomic.AddUint64": ext۰atomic۰AddUint64,
"sync/atomic.CompareAndSwapInt64": ext۰atomic۰CompareAndSwapInt64,
"sync/atomic.CompareAndSwapUint64": ext۰atomic۰CompareAndSwapUint64,
"sync/atomic.LoadInt64": ext۰atomic۰LoadInt64,
"sync/atomic.LoadUint64": ext۰atomic۰LoadUint64,
"sync/atomic.StoreInt64": ext۰atomic۰StoreInt64,
"sync/atomic.StoreUint64": ext۰atomic۰StoreUint64,
"(*sync/atomic.Value).Load": ext۰atomic۰ValueLoad,
"(*sync/atomic.Value).Store": ext۰atomic۰ValueStore,
"testing.MainStart": ext۰testing۰MainStart,
"time.Sleep": ext۰time۰Sleep,
"time.now": ext۰time۰now,
} {
externals[k] = v
}
}
// 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۰nop(fr *frame, args []value) value { return nil }
func ext۰sync۰Pool۰Get(fr *frame, args []value) value {
Pool := fr.i.prog.ImportedPackage("sync").Type("Pool").Object()
_, newIndex, _ := types.LookupFieldOrMethod(Pool.Type(), false, Pool.Pkg(), "New")
if New := (*args[0].(*value)).(structure)[newIndex[0]]; New != nil {
return call(fr.i, fr, 0, New, nil)
}
return nil
}
func ext۰bytes۰Equal(fr *frame, args []value) value {
// func Equal(a, b []byte) bool
a := args[0].([]value)
b := args[1].([]value)
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func ext۰bytes۰IndexByte(fr *frame, args []value) value {
// func IndexByte(s []byte, c byte) int
s := args[0].([]value)
c := args[1].(byte)
for i, b := range s {
if b.(byte) == c {
return i
}
}
return -1
}
func ext۰crc32۰haveSSE42(fr *frame, args []value) value {
return false
}
func ext۰math۰Float64frombits(fr *frame, args []value) value {
return math.Float64frombits(args[0].(uint64))
}
func ext۰math۰Float64bits(fr *frame, args []value) value {
return math.Float64bits(args[0].(float64))
}
func ext۰math۰Float32frombits(fr *frame, args []value) value {
return math.Float32frombits(args[0].(uint32))
}
func ext۰math۰Abs(fr *frame, args []value) value {
return math.Abs(args[0].(float64))
}
func ext۰math۰Exp(fr *frame, args []value) value {
return math.Exp(args[0].(float64))
}
func ext۰math۰Float32bits(fr *frame, args []value) value {
return math.Float32bits(args[0].(float32))
}
func ext۰math۰Min(fr *frame, args []value) value {
return math.Min(args[0].(float64), args[1].(float64))
}
func ext۰math۰hasSSE4(fr *frame, args []value) value {
return false
}
func ext۰math۰hasVectorFacility(fr *frame, args []value) value {
return false
}
func ext۰math۰Ldexp(fr *frame, args []value) value {
return math.Ldexp(args[0].(float64), args[1].(int))
}
func ext۰math۰Log(fr *frame, args []value) value {
return math.Log(args[0].(float64))
}
func ext۰os۰runtime_args(fr *frame, args []value) value {
return fr.i.osArgs
}
func ext۰runtime۰Breakpoint(fr *frame, args []value) value {
runtime.Breakpoint()
return nil
}
func ext۰runtime۰Caller(fr *frame, args []value) value {
// func Caller(skip int) (pc uintptr, file string, line int, ok bool)
skip := 1 + args[0].(int)
for i := 0; i < skip; i++ {
if fr != nil {
fr = fr.caller
}
}
var pc uintptr
var file string
var line int
var ok bool
if fr != nil {
fn := fr.fn
// TODO(adonovan): use pc/posn of current instruction, not start of fn.
// (Required to interpret the log package's tests.)
pc = uintptr(unsafe.Pointer(fn))
posn := fn.Prog.Fset.Position(fn.Pos())
file = posn.Filename
line = posn.Line
ok = true
}
return tuple{pc, file, line, ok}
}
func ext۰runtime۰Callers(fr *frame, args []value) value {
// Callers(skip int, pc []uintptr) int
skip := args[0].(int)
pc := args[1].([]value)
for i := 0; i < skip; i++ {
if fr != nil {
fr = fr.caller
}
}
i := 0
for fr != nil && i < len(pc) {
pc[i] = uintptr(unsafe.Pointer(fr.fn))
i++
fr = fr.caller
}
return i
}
func ext۰runtime۰FuncForPC(fr *frame, args []value) value {
// FuncForPC(pc uintptr) *Func
pc := args[0].(uintptr)
var fn *ssa.Function
if pc != 0 {
fn = (*ssa.Function)(unsafe.Pointer(pc)) // indeed unsafe!
}
var Func value
Func = structure{fn} // a runtime.Func
return &Func
}
func ext۰runtime۰environ(fr *frame, args []value) value {
// This function also implements syscall.runtime_envs.
return environ
}
func ext۰runtime۰getgoroot(fr *frame, args []value) value {
return os.Getenv("GOROOT")
}
func ext۰strings۰Count(fr *frame, args []value) value {
// Call compiled version to avoid asm dependency.
return strings.Count(args[0].(string), args[1].(string))
}
func ext۰strings۰IndexByte(fr *frame, args []value) value {
// Call compiled version to avoid asm dependency.
return strings.IndexByte(args[0].(string), args[1].(byte))
}
func ext۰strings۰Index(fr *frame, args []value) value {
// Call compiled version to avoid asm dependency.
return strings.Index(args[0].(string), args[1].(string))
}
func ext۰runtime۰GOMAXPROCS(fr *frame, args []value) value {
// Ignore args[0]; don't let the interpreted program
// set the interpreter's GOMAXPROCS!
return runtime.GOMAXPROCS(0)
}
func ext۰runtime۰Goexit(fr *frame, args []value) value {
// TODO(adonovan): don't kill the interpreter's main goroutine.
runtime.Goexit()
return nil
}
func ext۰runtime۰GC(fr *frame, args []value) value {
runtime.GC()
return nil
}
func ext۰runtime۰Gosched(fr *frame, args []value) value {
runtime.Gosched()
return nil
}
func ext۰runtime۰NumCPU(fr *frame, args []value) value {
return runtime.NumCPU()
}
func ext۰runtime۰NumGoroutine(fr *frame, args []value) value {
return int(atomic.LoadInt32(&fr.i.goroutines))
}
func ext۰runtime۰ReadMemStats(fr *frame, args []value) value {
// TODO(adonovan): populate args[0].(Struct)
return nil
}
func ext۰atomic۰LoadUint32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
return (*args[0].(*value)).(uint32)
}
func ext۰atomic۰StoreUint32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
*args[0].(*value) = args[1].(uint32)
return nil
}
func ext۰atomic۰LoadInt32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
return (*args[0].(*value)).(int32)
}
func ext۰atomic۰StoreInt32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
*args[0].(*value) = args[1].(int32)
return nil
}
func ext۰atomic۰CompareAndSwapInt32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
if (*p).(int32) == args[1].(int32) {
*p = args[2].(int32)
return true
}
return false
}
func ext۰atomic۰CompareAndSwapUint32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
if (*p).(uint32) == args[1].(uint32) {
*p = args[2].(uint32)
return true
}
return false
}
func ext۰atomic۰AddInt32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
newv := (*p).(int32) + args[1].(int32)
*p = newv
return newv
}
func ext۰atomic۰AddUint32(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
newv := (*p).(uint32) + args[1].(uint32)
*p = newv
return newv
}
func ext۰atomic۰LoadUint64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
return (*args[0].(*value)).(uint64)
}
func ext۰atomic۰StoreUint64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
*args[0].(*value) = args[1].(uint64)
return nil
}
func ext۰atomic۰LoadInt64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
return (*args[0].(*value)).(int64)
}
func ext۰atomic۰StoreInt64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
*args[0].(*value) = args[1].(int64)
return nil
}
func ext۰atomic۰CompareAndSwapInt64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
if (*p).(int64) == args[1].(int64) {
*p = args[2].(int64)
return true
}
return false
}
func ext۰atomic۰CompareAndSwapUint64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
if (*p).(uint64) == args[1].(uint64) {
*p = args[2].(uint64)
return true
}
return false
}
func ext۰atomic۰AddInt64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
newv := (*p).(int64) + args[1].(int64)
*p = newv
return newv
}
func ext۰atomic۰AddUint64(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
p := args[0].(*value)
newv := (*p).(uint64) + args[1].(uint64)
*p = newv
return newv
}
func ext۰atomic۰ValueLoad(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
// Receiver is *struct{v interface{}}.
return (*args[0].(*value)).(structure)[0]
}
func ext۰atomic۰ValueStore(fr *frame, args []value) value {
// TODO(adonovan): fix: not atomic!
// Receiver is *struct{v interface{}}.
(*args[0].(*value)).(structure)[0] = args[1]
return nil
}
func ext۰cpu۰cpuid(fr *frame, args []value) value {
return tuple{uint32(0), uint32(0), uint32(0), uint32(0)}
}
func ext۰syscall۰unix۰syscall_fcntl(fr *frame, args []value) value {
return tuple{int(0), wrapError(nil)}
}
// Pretend: type runtime.Func struct { entry *ssa.Function }
func ext۰runtime۰Func۰FileLine(fr *frame, args []value) value {
// func (*runtime.Func) FileLine(uintptr) (string, int)
f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function)
pc := args[1].(uintptr)
_ = pc
if f != nil {
// TODO(adonovan): use position of current instruction, not fn.
posn := f.Prog.Fset.Position(f.Pos())
return tuple{posn.Filename, posn.Line}
}
return tuple{"", 0}
}
func ext۰runtime۰Func۰Name(fr *frame, args []value) value {
// func (*runtime.Func) Name() string
f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function)
if f != nil {
return f.String()
}
return ""
}
func ext۰runtime۰Func۰Entry(fr *frame, args []value) value {
// func (*runtime.Func) Entry() uintptr
f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function)
return uintptr(unsafe.Pointer(f))
}
func ext۰time۰now(fr *frame, args []value) value {
nano := time.Now().UnixNano()
return tuple{int64(nano / 1e9), int32(nano % 1e9), int64(0)}
}
func ext۰time۰Sleep(fr *frame, args []value) value {
time.Sleep(time.Duration(args[0].(int64)))
return nil
}
func valueToBytes(v value) []byte {
in := v.([]value)
b := make([]byte, len(in))
for i := range in {
b[i] = in[i].(byte)
}
return b
}
func ext۰testing۰MainStart(fr *frame, args []value) value {
// We no longer support interpretation of the "testing" package
// because it changes too often and uses low-level features that
// are a pain to emulate.
panic(`interpretation of the "testing" package is no longer supported`)
}

View File

@@ -0,0 +1,35 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin
package interp
import "syscall"
func init() {
externals["syscall.Sysctl"] = ext۰syscall۰Sysctl
fillStat = func(st *syscall.Stat_t, stat structure) {
stat[0] = st.Dev
stat[1] = st.Mode
stat[2] = st.Nlink
stat[3] = st.Ino
stat[4] = st.Uid
stat[5] = st.Gid
stat[6] = st.Rdev
// TODO(adonovan): fix: copy Timespecs.
// stat[8] = st.Atim
// stat[9] = st.Mtim
// stat[10] = st.Ctim
stat[12] = st.Size
stat[13] = st.Blocks
stat[14] = st.Blksize
}
}
func ext۰syscall۰Sysctl(fr *frame, args []value) value {
r, err := syscall.Sysctl(args[0].(string))
return tuple{r, wrapError(err)}
}

View File

@@ -0,0 +1,256 @@
// 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 darwin linux
package interp
import "syscall"
func init() {
for k, v := range map[string]externalFn{
"os.Pipe": ext۰os۰Pipe,
"syscall.Close": ext۰syscall۰Close,
"syscall.Exit": ext۰syscall۰Exit,
"syscall.Fchown": ext۰syscall۰Fchown,
"syscall.Fstat": ext۰syscall۰Fstat,
"syscall.Ftruncate": ext۰syscall۰Ftruncate,
"syscall.Getpid": ext۰syscall۰Getpid,
"syscall.Getwd": ext۰syscall۰Getwd,
"syscall.Kill": ext۰syscall۰Kill,
"syscall.Link": ext۰syscall۰Link,
"syscall.Lstat": ext۰syscall۰Lstat,
"syscall.Mkdir": ext۰syscall۰Mkdir,
"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.Readlink": ext۰syscall۰Readlink,
"syscall.Rmdir": ext۰syscall۰Rmdir,
"syscall.Seek": ext۰syscall۰Seek,
"syscall.Stat": ext۰syscall۰Stat,
"syscall.Symlink": ext۰syscall۰Symlink,
"syscall.Write": ext۰syscall۰Write,
"syscall.Unlink": ext۰syscall۰Unlink,
"syscall۰UtimesNano": ext۰syscall۰UtimesNano,
"syscall.setenv_c": ext۰nop,
"syscall.unsetenv_c": ext۰nop,
"syscall.runtime_envs": ext۰runtime۰environ,
} {
externals[k] = v
}
syswrite = syscall.Write
}
func ext۰os۰Pipe(fr *frame, args []value) value {
// func os.Pipe() (r *File, w *File, err error)
// The portable POSIX pipe(2) call is good enough for our needs.
var p [2]int
if err := syscall.Pipe(p[:]); err != nil {
// TODO(adonovan): fix: return an *os.SyscallError.
return tuple{nil, nil, wrapError(err)}
}
NewFile := fr.i.prog.ImportedPackage("os").Func("NewFile")
r := call(fr.i, fr, 0, NewFile, []value{uintptr(p[0]), "|0"})
w := call(fr.i, fr, 0, NewFile, []value{uintptr(p[1]), "|1"})
return tuple{r, w, wrapError(nil)}
}
// overridden on darwin
var fillStat = func(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(fr *frame, args []value) value {
// func Close(fd int) (err error)
return wrapError(syscall.Close(args[0].(int)))
}
func ext۰syscall۰Exit(fr *frame, args []value) value {
panic(exitPanic(args[0].(int)))
}
func ext۰syscall۰Fchown(fr *frame, args []value) value {
fd := args[0].(int)
uid := args[1].(int)
gid := args[2].(int)
return wrapError(syscall.Fchown(fd, uid, gid))
}
func ext۰syscall۰Fstat(fr *frame, 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۰Ftruncate(fr *frame, args []value) value {
fd := args[0].(int)
length := args[1].(int64)
return wrapError(syscall.Ftruncate(fd, length))
}
func ext۰syscall۰Getpid(fr *frame, args []value) value {
return syscall.Getpid()
}
func ext۰syscall۰Getwd(fr *frame, args []value) value {
s, err := syscall.Getwd()
return tuple{s, wrapError(err)}
}
func ext۰syscall۰Kill(fr *frame, 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۰Link(fr *frame, args []value) value {
path := args[0].(string)
link := args[1].(string)
return wrapError(syscall.Link(path, link))
}
func ext۰syscall۰Lstat(fr *frame, 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۰Mkdir(fr *frame, args []value) value {
path := args[0].(string)
mode := args[1].(uint32)
return wrapError(syscall.Mkdir(path, mode))
}
func ext۰syscall۰Open(fr *frame, 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(fr *frame, 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۰RawSyscall(fr *frame, args []value) value {
return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)}
}
func ext۰syscall۰Read(fr *frame, 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۰ReadDirent(fr *frame, 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۰Readlink(fr *frame, args []value) value {
path := args[0].(string)
buf := valueToBytes(args[1])
n, err := syscall.Readlink(path, buf)
return tuple{n, wrapError(err)}
}
func ext۰syscall۰Rmdir(fr *frame, args []value) value {
return wrapError(syscall.Rmdir(args[0].(string)))
}
func ext۰syscall۰Seek(fr *frame, args []value) value {
fd := args[0].(int)
offset := args[1].(int64)
whence := args[2].(int)
new, err := syscall.Seek(fd, offset, whence)
return tuple{new, wrapError(err)}
}
func ext۰syscall۰Stat(fr *frame, 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۰Symlink(fr *frame, args []value) value {
path := args[0].(string)
link := args[1].(string)
return wrapError(syscall.Symlink(path, link))
}
func ext۰syscall۰Unlink(fr *frame, args []value) value {
return wrapError(syscall.Unlink(args[0].(string)))
}
func ext۰syscall۰UtimesNano(fr *frame, args []value) value {
path := args[0].(string)
var ts [2]syscall.Timespec
err := syscall.UtimesNano(path, ts[:])
// TODO(adonovan): copy the Timespecs into args[1]
return wrapError(err)
}
func ext۰syscall۰Write(fr *frame, 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)}
}

764
vendor/golang.org/x/tools/go/ssa/interp/interp.go generated vendored Normal file
View File

@@ -0,0 +1,764 @@
// 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.
//
// * The "testing" package is no longer supported because it
// depends on low-level details that change too often.
//
// * "sync/atomic" operations are not 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.
//
// * recover is only partially implemented. Also, the interpreter
// makes no attempt to distinguish target panics from interpreter
// crashes.
//
// * map iteration is asymptotically inefficient.
//
// * the sizes of the int, uint and uintptr types in the target
// program are assumed to be the same as those of the interpreter
// itself.
//
// * all values occupy space, even those of types defined by the spec
// to have zero size, e.g. struct{}. This can cause asymptotic
// performance degradation.
//
// * os.Exit is implemented using panic, causing deferred functions to
// run.
package interp // import "golang.org/x/tools/go/ssa/interp"
import (
"fmt"
"go/token"
"go/types"
"os"
"reflect"
"runtime"
"sync/atomic"
"golang.org/x/tools/go/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 {
osArgs []value // the value of os.Args
prog *ssa.Program // the SSA program
globals map[ssa.Value]*value // addresses of global variables (immutable)
mode Mode // interpreter options
reflectPackage *ssa.Package // the fake reflect package
errorMethods methodSet // the method set of reflect.error, which implements the error interface.
rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface.
runtimeErrorString types.Type // the runtime.errorString type
sizes types.Sizes // the effective type-sizing function
goroutines int32 // atomically updated
}
type deferred struct {
fn value
args []value
instr *ssa.Defer
tail *deferred
}
type frame struct {
i *interpreter
caller *frame
fn *ssa.Function
block, prevBlock *ssa.BasicBlock
env map[ssa.Value]value // dynamic values of SSA variables
locals []value
defers *deferred
result value
panicking bool
panic interface{}
}
func (fr *frame) get(key ssa.Value) value {
switch key := key.(type) {
case nil:
// Hack; simplifies handling of optional attributes
// such as ssa.Slice.{Low,High}.
return nil
case *ssa.Function, *ssa.Builtin:
return key
case *ssa.Const:
return constValue(key)
case *ssa.Global:
if r, ok := fr.i.globals[key]; ok {
return r
}
}
if r, ok := fr.env[key]; ok {
return r
}
panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name()))
}
// runDefer runs a deferred call d.
// It always returns normally, but may set or clear fr.panic.
//
func (fr *frame) runDefer(d *deferred) {
if fr.i.mode&EnableTracing != 0 {
fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n",
fr.i.prog.Fset.Position(d.instr.Pos()))
}
var ok bool
defer func() {
if !ok {
// Deferred call created a new state of panic.
fr.panicking = true
fr.panic = recover()
}
}()
call(fr.i, fr, d.instr.Pos(), d.fn, d.args)
ok = true
}
// runDefers executes fr's deferred function calls in LIFO order.
//
// On entry, fr.panicking indicates a state of panic; if
// true, fr.panic contains the panic value.
//
// On completion, if a deferred call started a panic, or if no
// deferred call recovered from a previous state of panic, then
// runDefers itself panics after the last deferred call has run.
//
// If there was no initial state of panic, or it was recovered from,
// runDefers returns normally.
//
func (fr *frame) runDefers() {
for d := fr.defers; d != nil; d = d.tail {
fr.runDefer(d)
}
fr.defers = nil
if fr.panicking {
panic(fr.panic) // new panic, or still panicking
}
}
// lookupMethod returns the method set for type typ, which may be one
// of the interpreter's fake types.
func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function {
switch typ {
case rtypeType:
return i.rtypeMethods[meth.Id()]
case errorType:
return i.errorMethods[meth.Id()]
}
return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name())
}
// visitInstr interprets a single ssa.Instruction within the activation
// record frame. It returns a continuation value indicating where to
// read the next instruction from.
func visitInstr(fr *frame, instr ssa.Instruction) continuation {
switch instr := instr.(type) {
case *ssa.DebugRef:
// no-op
case *ssa.UnOp:
fr.env[instr] = unop(instr, fr.get(instr.X))
case *ssa.BinOp:
fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y))
case *ssa.Call:
fn, args := prepareCall(fr, &instr.Call)
fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args)
case *ssa.ChangeInterface:
fr.env[instr] = fr.get(instr.X)
case *ssa.ChangeType:
fr.env[instr] = fr.get(instr.X) // (can't fail)
case *ssa.Convert:
fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
case *ssa.MakeInterface:
fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)}
case *ssa.Extract:
fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index]
case *ssa.Slice:
fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max))
case *ssa.Return:
switch len(instr.Results) {
case 0:
case 1:
fr.result = fr.get(instr.Results[0])
default:
var res []value
for _, r := range instr.Results {
res = append(res, fr.get(r))
}
fr.result = tuple(res)
}
fr.block = nil
return kReturn
case *ssa.RunDefers:
fr.runDefers()
case *ssa.Panic:
panic(targetPanic{fr.get(instr.X)})
case *ssa.Send:
fr.get(instr.Chan).(chan value) <- fr.get(instr.X)
case *ssa.Store:
store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val))
case *ssa.If:
succ := 1
if fr.get(instr.Cond).(bool) {
succ = 0
}
fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ]
return kJump
case *ssa.Jump:
fr.prevBlock, fr.block = fr.block, fr.block.Succs[0]
return kJump
case *ssa.Defer:
fn, args := prepareCall(fr, &instr.Call)
fr.defers = &deferred{
fn: fn,
args: args,
instr: instr,
tail: fr.defers,
}
case *ssa.Go:
fn, args := prepareCall(fr, &instr.Call)
atomic.AddInt32(&fr.i.goroutines, 1)
go func() {
call(fr.i, nil, instr.Pos(), fn, args)
atomic.AddInt32(&fr.i.goroutines, -1)
}()
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:
fr.env[instr] = &(*fr.get(instr.X).(*value)).(structure)[instr.Field]
case *ssa.Field:
fr.env[instr] = fr.get(instr.X).(structure)[instr.Field]
case *ssa.IndexAddr:
x := fr.get(instr.X)
idx := fr.get(instr.Index)
switch x := x.(type) {
case []value:
fr.env[instr] = &x[asInt(idx)]
case *value: // *array
fr.env[instr] = &(*x).(array)[asInt(idx)]
default:
panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
}
case *ssa.Index:
fr.env[instr] = fr.get(instr.X).(array)[asInt(fr.get(instr.Index))]
case *ssa.Lookup:
fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index))
case *ssa.MapUpdate:
m := fr.get(instr.Map)
key := fr.get(instr.Key)
v := fr.get(instr.Value)
switch m := m.(type) {
case map[value]value:
m[key] = v
case *hashmap:
m.insert(key.(hashable), v)
default:
panic(fmt.Sprintf("illegal map type: %T", m))
}
case *ssa.TypeAssert:
fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface))
case *ssa.MakeClosure:
var bindings []value
for _, binding := range instr.Bindings {
bindings = append(bindings, fr.get(binding))
}
fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings}
case *ssa.Phi:
for i, pred := range instr.Block().Preds {
if fr.prevBlock == pred {
fr.env[instr] = fr.get(instr.Edges[i])
break
}
}
case *ssa.Select:
var cases []reflect.SelectCase
if !instr.Blocking {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectDefault,
})
}
for _, state := range instr.States {
var dir reflect.SelectDir
if state.Dir == types.RecvOnly {
dir = reflect.SelectRecv
} else {
dir = reflect.SelectSend
}
var send reflect.Value
if state.Send != nil {
send = reflect.ValueOf(fr.get(state.Send))
}
cases = append(cases, reflect.SelectCase{
Dir: dir,
Chan: reflect.ValueOf(fr.get(state.Chan)),
Send: send,
})
}
chosen, recv, recvOk := reflect.Select(cases)
if !instr.Blocking {
chosen-- // default case should have index -1.
}
r := tuple{chosen, recvOk}
for i, st := range instr.States {
if st.Dir == types.RecvOnly {
var v value
if i == chosen && recvOk {
// No need to copy since send makes an unaliased copy.
v = recv.Interface().(value)
} else {
v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem())
}
r = append(r, v)
}
}
fr.env[instr] = r
default:
panic(fmt.Sprintf("unexpected instruction: %T", instr))
}
// if val, ok := instr.(ssa.Value); ok {
// fmt.Println(toString(fr.env[val])) // debugging
// }
return kNext
}
// prepareCall determines the function value and argument values for a
// function call in a Call, Go or Defer instruction, performing
// interface method lookup if needed.
//
func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
v := fr.get(call.Value)
if call.Method == nil {
// Function call.
fn = v
} else {
// Interface method invocation.
recv := v.(iface)
if recv.t == nil {
panic("method invoked on nil interface")
}
if f := lookupMethod(fr.i, recv.t, call.Method); f == nil {
// Unreachable in well-typed programs.
panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method))
} else {
fn = f
}
args = append(args, recv.v)
}
for _, arg := range call.Args {
args = append(args, fr.get(arg))
}
return
}
// call interprets a call to a function (function, builtin or closure)
// fn with arguments args, returning its result.
// callpos is the position of the callsite.
//
func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value {
switch fn := fn.(type) {
case *ssa.Function:
if fn == nil {
panic("call of nil function") // nil of func type
}
return callSSA(i, caller, callpos, fn, args, nil)
case *closure:
return callSSA(i, caller, callpos, fn.Fn, args, fn.Env)
case *ssa.Builtin:
return callBuiltin(caller, callpos, fn, args)
}
panic(fmt.Sprintf("cannot call %T", fn))
}
func loc(fset *token.FileSet, pos token.Pos) string {
if pos == token.NoPos {
return ""
}
return " at " + fset.Position(pos).String()
}
// callSSA interprets a call to function fn with arguments args,
// and lexical environment env, returning its result.
// callpos is the position of the callsite.
//
func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value {
if i.mode&EnableTracing != 0 {
fset := fn.Prog.Fset
// TODO(adonovan): fix: loc() lies for external functions.
fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos()))
suffix := ""
if caller != nil {
suffix = ", resuming " + caller.fn.String() + loc(fset, callpos)
}
defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix)
}
fr := &frame{
i: i,
caller: caller, // for panic/recover
fn: fn,
}
if fn.Parent() == nil {
name := fn.String()
if ext := externals[name]; ext != nil {
if i.mode&EnableTracing != 0 {
fmt.Fprintln(os.Stderr, "\t(external)")
}
return ext(fr, args)
}
if fn.Blocks == nil {
panic("no code for function: " + name)
}
}
fr.env = make(map[ssa.Value]value)
fr.block = fn.Blocks[0]
fr.locals = make([]value, len(fn.Locals))
for i, l := range fn.Locals {
fr.locals[i] = zero(deref(l.Type()))
fr.env[l] = &fr.locals[i]
}
for i, p := range fn.Params {
fr.env[p] = args[i]
}
for i, fv := range fn.FreeVars {
fr.env[fv] = env[i]
}
for fr.block != nil {
runFrame(fr)
}
// Destroy the locals to avoid accidental use after return.
for i := range fn.Locals {
fr.locals[i] = bad{}
}
return fr.result
}
// runFrame executes SSA instructions starting at fr.block and
// continuing until a return, a panic, or a recovered panic.
//
// After a panic, runFrame panics.
//
// After a normal return, fr.result contains the result of the call
// and fr.block is nil.
//
// A recovered panic in a function without named return parameters
// (NRPs) becomes a normal return of the zero value of the function's
// result type.
//
// After a recovered panic in a function with NRPs, fr.result is
// undefined and fr.block contains the block at which to resume
// control.
//
func runFrame(fr *frame) {
defer func() {
if fr.block == nil {
return // normal return
}
if fr.i.mode&DisableRecover != 0 {
return // let interpreter crash
}
fr.panicking = true
fr.panic = recover()
if fr.i.mode&EnableTracing != 0 {
fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic)
}
fr.runDefers()
fr.block = fr.fn.Recover
}()
for {
if fr.i.mode&EnableTracing != 0 {
fmt.Fprintf(os.Stderr, ".%s:\n", fr.block)
}
block:
for _, instr := range fr.block.Instrs {
if fr.i.mode&EnableTracing != 0 {
if v, ok := instr.(ssa.Value); ok {
fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr)
} else {
fmt.Fprintln(os.Stderr, "\t", instr)
}
}
switch visitInstr(fr, instr) {
case kReturn:
return
case kNext:
// no-op
case kJump:
break block
}
}
}
}
// doRecover implements the recover() built-in.
func doRecover(caller *frame) value {
// recover() must be exactly one level beneath the deferred
// function (two levels beneath the panicking function) to
// have any effect. Thus we ignore both "defer recover()" and
// "defer f() -> g() -> recover()".
if caller.i.mode&DisableRecover == 0 &&
caller != nil && !caller.panicking &&
caller.caller != nil && caller.caller.panicking {
caller.caller.panicking = false
p := caller.caller.panic
caller.caller.panic = nil
// TODO(adonovan): support runtime.Goexit.
switch p := p.(type) {
case targetPanic:
// The target program explicitly called panic().
return p.v
case runtime.Error:
// The interpreter encountered a runtime error.
return iface{caller.i.runtimeErrorString, p.Error()}
case string:
// The interpreter explicitly called panic().
return iface{caller.i.runtimeErrorString, p}
default:
panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p))
}
}
return iface{}
}
// setGlobal sets the value of a system-initialized global variable.
func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) {
if g, ok := i.globals[pkg.Var(name)]; ok {
*g = v
return
}
panic("no global variable: " + pkg.Pkg.Path() + "." + name)
}
var environ []value
func init() {
for _, s := range os.Environ() {
environ = append(environ, s)
}
environ = append(environ, "GOSSAINTERP=1")
environ = append(environ, "GOARCH="+runtime.GOARCH)
}
// deleteBodies delete the bodies of all standalone functions except the
// specified ones. A missing intrinsic leads to a clear runtime error.
func deleteBodies(pkg *ssa.Package, except ...string) {
keep := make(map[string]bool)
for _, e := range except {
keep[e] = true
}
for _, mem := range pkg.Members {
if fn, ok := mem.(*ssa.Function); ok && !keep[fn.Name()] {
fn.Blocks = nil
}
}
}
// Interpret interprets the Go program whose main package is mainpkg.
// mode specifies various interpreter options. filename and args are
// the initial values of os.Args for the target program. sizes is the
// effective type-sizing function for this program.
//
// Interpret returns the exit code of the program: 2 for panic (like
// gc does), or the argument to os.Exit for normal termination.
//
// The SSA program must include the "runtime" package.
//
func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) {
if syswrite == nil {
fmt.Fprintln(os.Stderr, "Interpret: unsupported platform.")
return 1
}
i := &interpreter{
prog: mainpkg.Prog,
globals: make(map[ssa.Value]*value),
mode: mode,
sizes: sizes,
goroutines: 1,
}
runtimePkg := i.prog.ImportedPackage("runtime")
if runtimePkg == nil {
panic("ssa.Program doesn't include runtime package")
}
i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type()
initReflect(i)
i.osArgs = append(i.osArgs, filename)
for _, arg := range args {
i.osArgs = append(i.osArgs, arg)
}
for _, pkg := range i.prog.AllPackages() {
// Initialize global storage.
for _, m := range pkg.Members {
switch v := m.(type) {
case *ssa.Global:
cell := zero(deref(v.Type()))
i.globals[v] = &cell
}
}
// Ad-hoc initialization for magic system variables.
switch pkg.Pkg.Path() {
case "syscall":
setGlobal(i, pkg, "envs", environ)
case "reflect":
deleteBodies(pkg, "DeepEqual", "deepValueEqual")
case "runtime":
sz := sizes.Sizeof(pkg.Pkg.Scope().Lookup("MemStats").Type())
setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz))
deleteBodies(pkg, "GOROOT", "gogetenv")
}
}
// Top-level error handler.
exitCode = 2
defer func() {
if exitCode != 2 || i.mode&DisableRecover != 0 {
return
}
switch p := recover().(type) {
case exitPanic:
exitCode = int(p)
return
case targetPanic:
fmt.Fprintln(os.Stderr, "panic:", toString(p.v))
case runtime.Error:
fmt.Fprintln(os.Stderr, "panic:", p.Error())
case string:
fmt.Fprintln(os.Stderr, "panic:", p)
default:
fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p)
}
// TODO(adonovan): dump panicking interpreter goroutine?
// buf := make([]byte, 0x10000)
// runtime.Stack(buf, false)
// fmt.Fprintln(os.Stderr, string(buf))
// (Or dump panicking target goroutine?)
}()
// Run!
call(i, nil, token.NoPos, mainpkg.Func("init"), nil)
if mainFn := mainpkg.Func("main"); mainFn != nil {
call(i, nil, token.NoPos, mainFn, nil)
exitCode = 0
} else {
fmt.Fprintln(os.Stderr, "No main function.")
exitCode = 1
}
return
}
// deref returns a pointer's element type; otherwise it returns typ.
// TODO(adonovan): Import from ssa?
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}

317
vendor/golang.org/x/tools/go/ssa/interp/interp_test.go generated vendored Normal file
View File

@@ -0,0 +1,317 @@
// 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 linux darwin
package interp_test
import (
"bytes"
"fmt"
"go/build"
"go/types"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil"
)
// 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/go/ssa/interp/testdata/.
var testdataTests = []string{
"boundmeth.go",
"complit.go",
"coverage.go",
"defer.go",
"fieldprom.go",
"ifaceconv.go",
"ifaceprom.go",
"initorder.go",
"methprom.go",
"mrvchain.go",
"range.go",
"recover.go",
"reflect.go",
"static.go",
"callstack.go",
}
type successPredicate func(exitcode int, output string) error
func run(t *testing.T, dir, input string, success successPredicate) bool {
if runtime.GOOS == "darwin" {
t.Skip("skipping on darwin until golang.org/issue/23166 is fixed")
}
fmt.Printf("Input: %s\n", input)
start := time.Now()
var inputs []string
for _, i := range strings.Split(input, " ") {
if strings.HasSuffix(i, ".go") {
i = dir + i
}
inputs = append(inputs, i)
}
var conf loader.Config
if _, err := conf.FromArgs(inputs, true); err != nil {
t.Errorf("FromArgs(%s) failed: %s", inputs, err)
return false
}
conf.Import("runtime")
// Print a helpful hint if we don't make it to the end.
var hint string
defer func() {
if hint != "" {
fmt.Println("FAIL")
fmt.Println(hint)
} else {
fmt.Println("PASS")
}
interp.CapturedOutput = nil
}()
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input)
iprog, err := conf.Load()
if err != nil {
t.Errorf("conf.Load(%s) failed: %s", inputs, err)
return false
}
prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
prog.Build()
// Find first main or test package among the initial packages.
var mainPkg *ssa.Package
for _, info := range iprog.InitialPackages() {
if info.Pkg.Path() == "runtime" {
continue // not an initial package
}
p := prog.Package(info.Pkg)
if p.Pkg.Name() == "main" && p.Func("main") != nil {
mainPkg = p
break
}
mainPkg = prog.CreateTestMainPackage(p)
if mainPkg != nil {
break
}
}
if mainPkg == nil {
t.Fatalf("no main or test packages among initial packages: %s", inputs)
}
var out bytes.Buffer
interp.CapturedOutput = &out
hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -test -run --interp=T %s\n", input)
exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{WordSize: 8, MaxAlign: 8}, inputs[0], []string{})
// The definition of success varies with each file.
if err := success(exitCode, out.String()); err != nil {
t.Errorf("interp.Interpret(%s) failed: %s", inputs, err)
return false
}
hint = "" // call off the hounds
if false {
fmt.Println(input, time.Since(start)) // test profiling
}
return true
}
const slash = string(os.PathSeparator)
func printFailures(failures []string) {
if failures != nil {
fmt.Println("The following tests failed:")
for _, f := range failures {
fmt.Printf("\t%s\n", f)
}
}
}
func success(exitcode int, output string) error {
if exitcode != 0 {
return fmt.Errorf("exit code was %d", exitcode)
}
if strings.Contains(output, "BUG") {
return fmt.Errorf("exited zero but output contained 'BUG'")
}
return nil
}
// TestTestdataFiles runs the interpreter on testdata/*.go.
func TestTestdataFiles(t *testing.T) {
var failures []string
start := time.Now()
for _, input := range testdataTests {
if testing.Short() && time.Since(start) > 30*time.Second {
printFailures(failures)
t.Skipf("timeout - aborting test")
}
if !run(t, "testdata"+slash, input, success) {
failures = append(failures, input)
}
}
printFailures(failures)
}
// TestGorootTest runs the interpreter on $GOROOT/test/*.go.
func TestGorootTest(t *testing.T) {
if testing.Short() {
t.Skip() // too slow (~30s)
}
var failures []string
for _, input := range gorootTestTests {
if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
failures = append(failures, input)
}
}
printFailures(failures)
}
// CreateTestMainPackage should return nil if there were no tests.
func TestNullTestmainPackage(t *testing.T) {
var conf loader.Config
conf.CreateFromFilenames("", "testdata/b_test.go")
iprog, err := conf.Load()
if err != nil {
t.Fatalf("CreatePackages failed: %s", err)
}
prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
mainPkg := prog.Package(iprog.Created[0].Pkg)
if mainPkg.Func("main") != nil {
t.Fatalf("unexpected main function")
}
if prog.CreateTestMainPackage(mainPkg) != nil {
t.Fatalf("CreateTestMainPackage returned non-nil")
}
}

121
vendor/golang.org/x/tools/go/ssa/interp/map.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
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 (
"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
}
// entries returns a rangeable map of entries.
func (m *hashmap) entries() map[int]*entry {
if m != nil {
return m.table
}
return nil
}

1396
vendor/golang.org/x/tools/go/ssa/interp/ops.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

574
vendor/golang.org/x/tools/go/ssa/interp/reflect.go generated vendored Normal file
View File

@@ -0,0 +1,574 @@
// 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"
"go/types"
"reflect"
"unsafe"
"golang.org/x/tools/go/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")
// rtype is the concrete type the interpreter uses to implement the
// reflect.Type interface.
//
// type rtype <opaque>
var rtypeType = makeNamedType("rtype", &opaqueType{nil, "rtype"})
// error is an (interpreted) named type whose underlying type is string.
// The interpreter uses it for all implementations of the built-in error
// interface that it creates.
// We put it in the "reflect" package for expedience.
//
// type error string
var errorType = makeNamedType("error", &opaqueType{nil, "error"})
func makeNamedType(name string, underlying types.Type) *types.Named {
obj := types.NewTypeName(token.NoPos, reflectTypesPackage, name, nil)
return types.NewNamed(obj, underlying, nil)
}
func makeReflectValue(t types.Type, v value) value {
return structure{rtype{t}, v}
}
// Given a reflect.Value, returns its rtype.
func rV2T(v value) rtype {
return v.(structure)[0].(rtype)
}
// Given a reflect.Value, returns the underlying interpreter value.
func rV2V(v value) value {
return v.(structure)[1]
}
// makeReflectType boxes up an rtype in a reflect.Type interface.
func makeReflectType(rt rtype) value {
return iface{rtypeType, rt}
}
func ext۰reflect۰Init(fr *frame, args []value) value {
// Signature: func()
return nil
}
func ext۰reflect۰rtype۰Bits(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) int
rt := args[0].(rtype).t
basic, ok := rt.Underlying().(*types.Basic)
if !ok {
panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt))
}
return int(fr.i.sizes.Sizeof(basic)) * 8
}
func ext۰reflect۰rtype۰Elem(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) reflect.Type
return makeReflectType(rtype{args[0].(rtype).t.Underlying().(interface {
Elem() types.Type
}).Elem()})
}
func ext۰reflect۰rtype۰Field(fr *frame, args []value) value {
// Signature: func (t reflect.rtype, i int) reflect.StructField
st := args[0].(rtype).t.Underlying().(*types.Struct)
i := args[1].(int)
f := st.Field(i)
return structure{
f.Name(),
f.Pkg().Path(),
makeReflectType(rtype{f.Type()}),
st.Tag(i),
0, // TODO(adonovan): offset
[]value{}, // TODO(adonovan): indices
f.Anonymous(),
}
}
func ext۰reflect۰rtype۰In(fr *frame, args []value) value {
// Signature: func (t reflect.rtype, i int) int
i := args[1].(int)
return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Params().At(i).Type()})
}
func ext۰reflect۰rtype۰Kind(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) uint
return uint(reflectKind(args[0].(rtype).t))
}
func ext۰reflect۰rtype۰NumField(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) int
return args[0].(rtype).t.Underlying().(*types.Struct).NumFields()
}
func ext۰reflect۰rtype۰NumIn(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) int
return args[0].(rtype).t.(*types.Signature).Params().Len()
}
func ext۰reflect۰rtype۰NumMethod(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) int
return fr.i.prog.MethodSets.MethodSet(args[0].(rtype).t).Len()
}
func ext۰reflect۰rtype۰NumOut(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) int
return args[0].(rtype).t.(*types.Signature).Results().Len()
}
func ext۰reflect۰rtype۰Out(fr *frame, args []value) value {
// Signature: func (t reflect.rtype, i int) int
i := args[1].(int)
return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results().At(i).Type()})
}
func ext۰reflect۰rtype۰Size(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) uintptr
return uintptr(fr.i.sizes.Sizeof(args[0].(rtype).t))
}
func ext۰reflect۰rtype۰String(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) string
return args[0].(rtype).t.String()
}
func ext۰reflect۰New(fr *frame, args []value) value {
// Signature: func (t reflect.Type) reflect.Value
t := args[0].(iface).v.(rtype).t
alloc := zero(t)
return makeReflectValue(types.NewPointer(t), &alloc)
}
func ext۰reflect۰SliceOf(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) Type
return makeReflectType(rtype{types.NewSlice(args[0].(iface).v.(rtype).t)})
}
func ext۰reflect۰TypeOf(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) Type
return makeReflectType(rtype{args[0].(iface).t})
}
func ext۰reflect۰ValueOf(fr *frame, args []value) value {
// Signature: func (interface{}) reflect.Value
itf := args[0].(iface)
return makeReflectValue(itf.t, itf.v)
}
func ext۰reflect۰Zero(fr *frame, args []value) value {
// Signature: func (t reflect.Type) reflect.Value
t := args[0].(iface).v.(rtype).t
return makeReflectValue(t, zero(t))
}
func reflectKind(t types.Type) reflect.Kind {
switch t := t.(type) {
case *types.Named:
return reflectKind(t.Underlying())
case *types.Basic:
switch t.Kind() {
case types.Bool:
return reflect.Bool
case types.Int:
return reflect.Int
case types.Int8:
return reflect.Int8
case types.Int16:
return reflect.Int16
case types.Int32:
return reflect.Int32
case types.Int64:
return reflect.Int64
case types.Uint:
return reflect.Uint
case types.Uint8:
return reflect.Uint8
case types.Uint16:
return reflect.Uint16
case types.Uint32:
return reflect.Uint32
case types.Uint64:
return reflect.Uint64
case types.Uintptr:
return reflect.Uintptr
case types.Float32:
return reflect.Float32
case types.Float64:
return reflect.Float64
case types.Complex64:
return reflect.Complex64
case types.Complex128:
return reflect.Complex128
case types.String:
return reflect.String
case types.UnsafePointer:
return reflect.UnsafePointer
}
case *types.Array:
return reflect.Array
case *types.Chan:
return reflect.Chan
case *types.Signature:
return reflect.Func
case *types.Interface:
return reflect.Interface
case *types.Map:
return reflect.Map
case *types.Pointer:
return reflect.Ptr
case *types.Slice:
return reflect.Slice
case *types.Struct:
return reflect.Struct
}
panic(fmt.Sprint("unexpected type: ", t))
}
func ext۰reflect۰Value۰Kind(fr *frame, args []value) value {
// Signature: func (reflect.Value) uint
return uint(reflectKind(rV2T(args[0]).t))
}
func ext۰reflect۰Value۰String(fr *frame, args []value) value {
// Signature: func (reflect.Value) string
return toString(rV2V(args[0]))
}
func ext۰reflect۰Value۰Type(fr *frame, args []value) value {
// Signature: func (reflect.Value) reflect.Type
return makeReflectType(rV2T(args[0]))
}
func ext۰reflect۰Value۰Uint(fr *frame, args []value) value {
// Signature: func (reflect.Value) uint64
switch v := rV2V(args[0]).(type) {
case uint:
return uint64(v)
case uint8:
return uint64(v)
case uint16:
return uint64(v)
case uint32:
return uint64(v)
case uint64:
return uint64(v)
case uintptr:
return uint64(v)
}
panic("reflect.Value.Uint")
}
func ext۰reflect۰Value۰Len(fr *frame, args []value) value {
// Signature: func (reflect.Value) int
switch v := rV2V(args[0]).(type) {
case string:
return len(v)
case array:
return len(v)
case chan value:
return cap(v)
case []value:
return len(v)
case *hashmap:
return v.len()
case map[value]value:
return len(v)
default:
panic(fmt.Sprintf("reflect.(Value).Len(%v)", v))
}
}
func ext۰reflect۰Value۰MapIndex(fr *frame, args []value) value {
// Signature: func (reflect.Value) Value
tValue := rV2T(args[0]).t.Underlying().(*types.Map).Key()
k := rV2V(args[1])
switch m := rV2V(args[0]).(type) {
case map[value]value:
if v, ok := m[k]; ok {
return makeReflectValue(tValue, v)
}
case *hashmap:
if v := m.lookup(k.(hashable)); v != nil {
return makeReflectValue(tValue, v)
}
default:
panic(fmt.Sprintf("(reflect.Value).MapIndex(%T, %T)", m, k))
}
return makeReflectValue(nil, nil)
}
func ext۰reflect۰Value۰MapKeys(fr *frame, args []value) value {
// Signature: func (reflect.Value) []Value
var keys []value
tKey := rV2T(args[0]).t.Underlying().(*types.Map).Key()
switch v := rV2V(args[0]).(type) {
case map[value]value:
for k := range v {
keys = append(keys, makeReflectValue(tKey, k))
}
case *hashmap:
for _, e := range v.entries() {
for ; e != nil; e = e.next {
keys = append(keys, makeReflectValue(tKey, e.key))
}
}
default:
panic(fmt.Sprintf("(reflect.Value).MapKeys(%T)", v))
}
return keys
}
func ext۰reflect۰Value۰NumField(fr *frame, args []value) value {
// Signature: func (reflect.Value) int
return len(rV2V(args[0]).(structure))
}
func ext۰reflect۰Value۰NumMethod(fr *frame, args []value) value {
// Signature: func (reflect.Value) int
return fr.i.prog.MethodSets.MethodSet(rV2T(args[0]).t).Len()
}
func ext۰reflect۰Value۰Pointer(fr *frame, args []value) value {
// Signature: func (v reflect.Value) uintptr
switch v := rV2V(args[0]).(type) {
case *value:
return uintptr(unsafe.Pointer(v))
case chan value:
return reflect.ValueOf(v).Pointer()
case []value:
return reflect.ValueOf(v).Pointer()
case *hashmap:
return reflect.ValueOf(v.entries()).Pointer()
case map[value]value:
return reflect.ValueOf(v).Pointer()
case *ssa.Function:
return uintptr(unsafe.Pointer(v))
case *closure:
return uintptr(unsafe.Pointer(v))
default:
panic(fmt.Sprintf("reflect.(Value).Pointer(%T)", v))
}
}
func ext۰reflect۰Value۰Index(fr *frame, args []value) value {
// Signature: func (v reflect.Value, i int) Value
i := args[1].(int)
t := rV2T(args[0]).t.Underlying()
switch v := rV2V(args[0]).(type) {
case array:
return makeReflectValue(t.(*types.Array).Elem(), v[i])
case []value:
return makeReflectValue(t.(*types.Slice).Elem(), v[i])
default:
panic(fmt.Sprintf("reflect.(Value).Index(%T)", v))
}
}
func ext۰reflect۰Value۰Bool(fr *frame, args []value) value {
// Signature: func (reflect.Value) bool
return rV2V(args[0]).(bool)
}
func ext۰reflect۰Value۰CanAddr(fr *frame, args []value) value {
// Signature: func (v reflect.Value) bool
// Always false for our representation.
return false
}
func ext۰reflect۰Value۰CanInterface(fr *frame, args []value) value {
// Signature: func (v reflect.Value) bool
// Always true for our representation.
return true
}
func ext۰reflect۰Value۰Elem(fr *frame, args []value) value {
// Signature: func (v reflect.Value) reflect.Value
switch x := rV2V(args[0]).(type) {
case iface:
return makeReflectValue(x.t, x.v)
case *value:
return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x)
default:
panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x))
}
}
func ext۰reflect۰Value۰Field(fr *frame, args []value) value {
// Signature: func (v reflect.Value, i int) reflect.Value
v := args[0]
i := args[1].(int)
return makeReflectValue(rV2T(v).t.Underlying().(*types.Struct).Field(i).Type(), rV2V(v).(structure)[i])
}
func ext۰reflect۰Value۰Float(fr *frame, args []value) value {
// Signature: func (reflect.Value) float64
switch v := rV2V(args[0]).(type) {
case float32:
return float64(v)
case float64:
return float64(v)
}
panic("reflect.Value.Float")
}
func ext۰reflect۰Value۰Interface(fr *frame, args []value) value {
// Signature: func (v reflect.Value) interface{}
return ext۰reflect۰valueInterface(fr, args)
}
func ext۰reflect۰Value۰Int(fr *frame, args []value) value {
// Signature: func (reflect.Value) int64
switch x := rV2V(args[0]).(type) {
case int:
return int64(x)
case int8:
return int64(x)
case int16:
return int64(x)
case int32:
return int64(x)
case int64:
return x
default:
panic(fmt.Sprintf("reflect.(Value).Int(%T)", x))
}
}
func ext۰reflect۰Value۰IsNil(fr *frame, args []value) value {
// Signature: func (reflect.Value) bool
switch x := rV2V(args[0]).(type) {
case *value:
return x == nil
case chan value:
return x == nil
case map[value]value:
return x == nil
case *hashmap:
return x == nil
case iface:
return x.t == nil
case []value:
return x == nil
case *ssa.Function:
return x == nil
case *ssa.Builtin:
return x == nil
case *closure:
return x == nil
default:
panic(fmt.Sprintf("reflect.(Value).IsNil(%T)", x))
}
}
func ext۰reflect۰Value۰IsValid(fr *frame, args []value) value {
// Signature: func (reflect.Value) bool
return rV2V(args[0]) != nil
}
func ext۰reflect۰Value۰Set(fr *frame, args []value) value {
// TODO(adonovan): implement.
return nil
}
func ext۰reflect۰valueInterface(fr *frame, args []value) value {
// Signature: func (v reflect.Value, safe bool) interface{}
v := args[0].(structure)
return iface{rV2T(v).t, rV2V(v)}
}
func ext۰reflect۰error۰Error(fr *frame, args []value) value {
return args[0]
}
// newMethod creates a new method of the specified name, package and receiver type.
func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function {
// TODO(adonovan): fix: hack: currently the only part of Signature
// that is needed is the "pointerness" of Recv.Type, and for
// now, we'll set it to always be false since we're only
// concerned with rtype. Encapsulate this better.
sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false)
fn := pkg.Prog.NewFunction(name, sig, "fake reflect method")
fn.Pkg = pkg
return fn
}
func initReflect(i *interpreter) {
i.reflectPackage = &ssa.Package{
Prog: i.prog,
Pkg: reflectTypesPackage,
Members: make(map[string]ssa.Member),
}
// Clobber the type-checker's notion of reflect.Value's
// underlying type so that it more closely matches the fake one
// (at least in the number of fields---we lie about the type of
// the rtype field).
//
// We must ensure that calls to (ssa.Value).Type() return the
// fake type so that correct "shape" is used when allocating
// variables, making zero values, loading, and storing.
//
// TODO(adonovan): obviously this is a hack. We need a cleaner
// way to fake the reflect package (almost---DeepEqual is fine).
// One approach would be not to even load its source code, but
// provide fake source files. This would guarantee that no bad
// information leaks into other packages.
if r := i.prog.ImportedPackage("reflect"); r != nil {
rV := r.Pkg.Scope().Lookup("Value").Type().(*types.Named)
// delete bodies of the old methods
mset := i.prog.MethodSets.MethodSet(rV)
for j := 0; j < mset.Len(); j++ {
i.prog.MethodValue(mset.At(j)).Blocks = nil
}
tEface := types.NewInterface(nil, nil).Complete()
rV.SetUnderlying(types.NewStruct([]*types.Var{
types.NewField(token.NoPos, r.Pkg, "t", tEface, false), // a lie
types.NewField(token.NoPos, r.Pkg, "v", tEface, false),
}, nil))
}
i.rtypeMethods = methodSet{
"Bits": newMethod(i.reflectPackage, rtypeType, "Bits"),
"Elem": newMethod(i.reflectPackage, rtypeType, "Elem"),
"Field": newMethod(i.reflectPackage, rtypeType, "Field"),
"In": newMethod(i.reflectPackage, rtypeType, "In"),
"Kind": newMethod(i.reflectPackage, rtypeType, "Kind"),
"NumField": newMethod(i.reflectPackage, rtypeType, "NumField"),
"NumIn": newMethod(i.reflectPackage, rtypeType, "NumIn"),
"NumMethod": newMethod(i.reflectPackage, rtypeType, "NumMethod"),
"NumOut": newMethod(i.reflectPackage, rtypeType, "NumOut"),
"Out": newMethod(i.reflectPackage, rtypeType, "Out"),
"Size": newMethod(i.reflectPackage, rtypeType, "Size"),
"String": newMethod(i.reflectPackage, rtypeType, "String"),
}
i.errorMethods = methodSet{
"Error": newMethod(i.reflectPackage, errorType, "Error"),
}
}

View File

@@ -0,0 +1,17 @@
package 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.

View File

@@ -0,0 +1,11 @@
package b
import "testing"
func NotATest(t *testing.T) {
t.Error("foo")
}
func NotABenchmark(b *testing.B) {
b.Error("wiz")
}

View File

@@ -0,0 +1,144 @@
// Tests of bound method closures.
package main
import "fmt"
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
}
// Regression test for b/7269:
// taking the value of an interface method performs a nil check.
func nilInterfaceMethodValue() {
err := fmt.Errorf("ok")
f := err.Error
if got := f(); got != "ok" {
panic(got)
}
err = nil
if got := f(); got != "ok" {
panic(got)
}
defer func() {
r := fmt.Sprint(recover())
// runtime panic string varies across toolchains
if r != "runtime error: interface conversion: interface is nil, not error" &&
r != "runtime error: invalid memory address or nil pointer dereference" {
panic("want runtime panic from nil interface method value, got " + r)
}
}()
f = err.Error // runtime panic: err is nil
panic("unreachable")
}
func main() {
valueReceiver()
pointerReceiver()
addressibleValuePointerReceiver()
promotedReceiver()
anonStruct()
typeCheck()
if e := regress1(errString("hi"))(); e != "hi" {
panic(e)
}
nilInterfaceMethodValue()
}

View File

@@ -0,0 +1,17 @@
package c_test
import (
"os"
"testing"
)
func TestC(t *testing.T) {
println("TestC")
}
func TestMain(m *testing.M) {
println("TestMain start")
code := m.Run()
println("TestMain end")
os.Exit(code)
}

View File

@@ -0,0 +1,52 @@
package main
import (
"fmt"
"path"
"runtime"
"strings"
)
var stack string
func f() {
pc := make([]uintptr, 6)
pc = pc[:runtime.Callers(1, pc)]
for _, f := range pc {
Func := runtime.FuncForPC(f)
name := Func.Name()
if strings.Contains(name, "$") || strings.Contains(name, ".func") {
name = "func" // anon funcs vary across toolchains
}
file, line := Func.FileLine(0)
stack += fmt.Sprintf("%s at %s:%d\n", name, path.Base(file), line)
}
}
func g() { f() }
func h() { g() }
func i() { func() { h() }() }
// Hack: the 'func' and the call to Caller are on the same line,
// to paper over differences between toolchains.
// (The interpreter's location info isn't yet complete.)
func runtimeCaller0() (uintptr, string, int, bool) { return runtime.Caller(0) }
func main() {
i()
if stack != `main.f at callstack.go:12
main.g at callstack.go:26
main.h at callstack.go:27
func at callstack.go:28
main.i at callstack.go:28
main.main at callstack.go:35
` {
panic("unexpected stack: " + stack)
}
pc, file, line, _ := runtimeCaller0()
got := fmt.Sprintf("%s @ %s:%d", runtime.FuncForPC(pc).Name(), path.Base(file), line)
if got != "main.runtimeCaller0 @ callstack.go:33" {
panic("runtime.Caller: " + got)
}
}

View File

@@ -0,0 +1,184 @@
package main
// Tests of composite literals.
import "fmt"
// Map 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)
}
}
// Nonliteral keys in composite literal.
func init() {
const zero int = 1
var v = []int{1 + zero: 42}
if x := fmt.Sprint(v); x != "[0 0 42]" {
panic(x)
}
}
// Test for in-place initialization.
func init() {
// struct
type S struct {
a, b int
}
s := S{1, 2}
s = S{b: 3}
if s.a != 0 {
panic("s.a != 0")
}
if s.b != 3 {
panic("s.b != 3")
}
s = S{}
if s.a != 0 {
panic("s.a != 0")
}
if s.b != 0 {
panic("s.b != 0")
}
// array
type A [4]int
a := A{2, 4, 6, 8}
a = A{1: 6, 2: 4}
if a[0] != 0 {
panic("a[0] != 0")
}
if a[1] != 6 {
panic("a[1] != 6")
}
if a[2] != 4 {
panic("a[2] != 4")
}
if a[3] != 0 {
panic("a[3] != 0")
}
a = A{}
if a[0] != 0 {
panic("a[0] != 0")
}
if a[1] != 0 {
panic("a[1] != 0")
}
if a[2] != 0 {
panic("a[2] != 0")
}
if a[3] != 0 {
panic("a[3] != 0")
}
}
// Regression test for https://github.com/golang/go/issues/10127:
// composite literal clobbers destination before reading from it.
func init() {
// map
{
type M map[string]int
m := M{"x": 1, "y": 2}
m = M{"x": m["y"], "y": m["x"]}
if m["x"] != 2 || m["y"] != 1 {
panic(fmt.Sprint(m))
}
n := M{"x": 3}
m, n = M{"x": n["x"]}, M{"x": m["x"]} // parallel assignment
if got := fmt.Sprint(m["x"], n["x"]); got != "3 2" {
panic(got)
}
}
// struct
{
type T struct{ x, y, z int }
t := T{x: 1, y: 2, z: 3}
t = T{x: t.y, y: t.z, z: t.x} // all fields
if got := fmt.Sprint(t); got != "{2 3 1}" {
panic(got)
}
t = T{x: t.y, y: t.z + 3} // not all fields
if got := fmt.Sprint(t); got != "{3 4 0}" {
panic(got)
}
u := T{x: 5, y: 6, z: 7}
t, u = T{x: u.x}, T{x: t.x} // parallel assignment
if got := fmt.Sprint(t, u); got != "{5 0 0} {3 0 0}" {
panic(got)
}
}
// array
{
a := [3]int{0: 1, 1: 2, 2: 3}
a = [3]int{0: a[1], 1: a[2], 2: a[0]} // all elements
if got := fmt.Sprint(a); got != "[2 3 1]" {
panic(got)
}
a = [3]int{0: a[1], 1: a[2] + 3} // not all elements
if got := fmt.Sprint(a); got != "[3 4 0]" {
panic(got)
}
b := [3]int{0: 5, 1: 6, 2: 7}
a, b = [3]int{0: b[0]}, [3]int{0: a[0]} // parallel assignment
if got := fmt.Sprint(a, b); got != "[5 0 0] [3 0 0]" {
panic(got)
}
}
// slice
{
s := []int{0: 1, 1: 2, 2: 3}
s = []int{0: s[1], 1: s[2], 2: s[0]} // all elements
if got := fmt.Sprint(s); got != "[2 3 1]" {
panic(got)
}
s = []int{0: s[1], 1: s[2] + 3} // not all elements
if got := fmt.Sprint(s); got != "[3 4]" {
panic(got)
}
t := []int{0: 5, 1: 6, 2: 7}
s, t = []int{0: t[0]}, []int{0: s[0]} // parallel assignment
if got := fmt.Sprint(s, t); got != "[5] [3]" {
panic(got)
}
}
}
// Regression test for https://github.com/golang/go/issues/13341:
// within a map literal, if a key expression is a composite literal,
// Go 1.5 allows its type to be omitted. An & operation may be implied.
func init() {
type S struct{ x int }
// same as map[*S]bool{&S{x: 1}: true}
m := map[*S]bool{{x: 1}: true}
for s := range m {
if s.x != 1 {
panic(s) // wrong key
}
return
}
panic("map is empty")
}
func main() {
}

View File

@@ -0,0 +1,534 @@
// This interpreter test is designed to run very quickly yet provide
// some coverage of a broad selection of constructs.
//
// Validate this file with 'go run' after editing.
// TODO(adonovan): break this into small files organized by theme.
package main
import (
"fmt"
"reflect"
)
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"
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)
}
}
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: {}}
}
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")
}
}
// 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() {
defer func() {
r := fmt.Sprint(recover())
// Exact error varies by toolchain.
if r != "runtime error: interface conversion: interface is nil, not main.I" &&
r != "interface conversion: interface is nil, not main.I" {
panic("I->I type assertion succeeded 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()
}
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
}
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)
}
}
// Regression test for issue 6949:
// []byte("foo") is not a constant since it allocates memory.
func init() {
var r string
for i, b := range "ABC" {
x := []byte("abc")
x[i] = byte(b)
r += string(x)
}
if r != "AbcaBcabC" {
panic(r)
}
}
// Test of 3-operand x[lo:hi:max] slice.
func init() {
s := []int{0, 1, 2, 3}
lenCapLoHi := func(x []int) [4]int { return [4]int{len(x), cap(x), x[0], x[len(x)-1]} }
if got := lenCapLoHi(s[1:3]); got != [4]int{2, 3, 1, 2} {
panic(got)
}
if got := lenCapLoHi(s[1:3:3]); got != [4]int{2, 2, 1, 2} {
panic(got)
}
max := 3
if "a"[0] == 'a' {
max = 2 // max is non-constant, even in SSA form
}
if got := lenCapLoHi(s[1:2:max]); got != [4]int{1, 1, 1, 1} {
panic(got)
}
}
var one = 1 // not a constant
// Test makeslice.
func init() {
check := func(s []string, wantLen, wantCap int) {
if len(s) != wantLen {
panic(len(s))
}
if cap(s) != wantCap {
panic(cap(s))
}
}
// SSA form:
check(make([]string, 10), 10, 10) // new([10]string)[:10]
check(make([]string, one), 1, 1) // make([]string, one, one)
check(make([]string, 0, 10), 0, 10) // new([10]string)[:0]
check(make([]string, 0, one), 0, 1) // make([]string, 0, one)
check(make([]string, one, 10), 1, 10) // new([10]string)[:one]
check(make([]string, one, one), 1, 1) // make([]string, one, one)
}
// Test that a nice error is issued by indirection wrappers.
func init() {
var ptr *T
var i I = ptr
defer func() {
r := fmt.Sprint(recover())
// Exact error varies by toolchain:
if r != "runtime error: value method (main.T).f called using nil *main.T pointer" &&
r != "value method main.T.f called using nil *T pointer" {
panic("want panic from call with nil receiver, got " + r)
}
}()
i.f()
panic("unreachable")
}
// Regression test for a subtle bug in which copying values would causes
// subcomponents of aggregate variables to change address, breaking
// aliases.
func init() {
type T struct{ f int }
var x T
p := &x.f
x = T{}
*p = 1
if x.f != 1 {
panic("lost store")
}
if p != &x.f {
panic("unstable address")
}
}

View File

@@ -0,0 +1,53 @@
package main
// Tests of defer. (Deferred recover() belongs is recover.go.)
import "fmt"
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 main() {
}

View File

@@ -0,0 +1,114 @@
package 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)
}

View File

@@ -0,0 +1,83 @@
package 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")
}
}

View File

@@ -0,0 +1,58 @@
package 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)
}
}

View File

@@ -0,0 +1,67 @@
package main
import "fmt"
// Test of initialization order of package-level vars.
var counter int
func next() int {
c := counter
counter++
return c
}
func next2() (x int, y int) {
x = next()
y = next()
return
}
func makeOrder() int {
_, _, _, _ = f, b, d, e
return 0
}
func main() {
// Initialization constraints:
// - {f,b,c/d,e} < order (ref graph traversal)
// - order < {a} (lexical order)
// - b < c/d < e < f (lexical order)
// Solution: a b c/d e f
abcdef := [6]int{a, b, c, d, e, f}
if abcdef != [6]int{0, 1, 2, 3, 4, 5} {
panic(abcdef)
}
}
var order = makeOrder()
var a, b = next(), next()
var c, d = next2()
var e, f = next(), next()
// ------------------------------------------------------------------------
var order2 []string
func create(x int, name string) int {
order2 = append(order2, name)
return x
}
var C = create(B+1, "C")
var A, B = create(1, "A"), create(2, "B")
// Initialization order of package-level value specs.
func init() {
x := fmt.Sprint(order2)
// Result varies by toolchain. This is a spec bug.
if x != "[B C A]" && // gc
x != "[A B C]" { // go/types
panic(x)
}
if C != 3 {
panic(c)
}
}

View File

@@ -0,0 +1,93 @@
package 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()
}

View File

@@ -0,0 +1,75 @@
// 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 h2() (i interface{}, ok bool) {
ch := make(chan string, 1)
ch <- "hi"
i, ok = <-ch // 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)
}
i, ok = h2()
if !ok || i.(string) != "hi" {
panic(i)
}
}

View File

@@ -0,0 +1,55 @@
package main
// Tests of range loops.
import "fmt"
// 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)
}
var x int
for range "Hello, 世界" {
x++
}
if x != len(indices) {
panic(x)
}
}
// Regression test for range of pointer to named array type.
func init() {
type intarr [3]int
ia := intarr{1, 2, 3}
var count int
for _, x := range &ia {
count += x
}
if count != 6 {
panic(count)
}
}
func main() {
}

View File

@@ -0,0 +1,34 @@
package main
// Tests of panic/recover.
import "fmt"
func fortyTwo() (r int) {
r = 42
// The next two statements simulate a 'return' statement.
defer func() { recover() }()
panic(nil)
}
func zero() int {
defer func() { recover() }()
panic(1)
}
func zeroEmpty() (int, string) {
defer func() { recover() }()
panic(1)
}
func main() {
if r := fortyTwo(); r != 42 {
panic(r)
}
if r := zero(); r != 0 {
panic(r)
}
if r, s := zeroEmpty(); r != 0 || s != "" {
panic(fmt.Sprint(r, s))
}
}

View File

@@ -0,0 +1,11 @@
package main
import "reflect"
func main() {
// Regression test for issue 9462.
got := reflect.SliceOf(reflect.TypeOf(byte(0))).String()
if got != "[]uint8" && got != "[]byte" { // result varies by toolchain
println("BUG: " + got)
}
}

View File

@@ -0,0 +1,58 @@
package main
// Static tests of SSA builder (via the sanity checker).
// Dynamic semantics are not exercised.
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
}
var a int
// Regression test for issue 7840 (covered by SSA sanity checker).
func bug7840() bool {
// This creates a single-predecessor block with a φ-node.
return false && a == 0 && a == 0
}
// A blocking select (sans "default:") cannot fall through.
// Regression test for issue 7022.
func bug7022() int {
var c1, c2 chan int
select {
case <-c1:
return 123
case <-c2:
return 456
}
}
// Parens should not prevent intrinsic treatment of built-ins.
// (Regression test for a crash.)
func init() {
_ = (new)(int)
_ = (make)([]int, 0)
}
func main() {}

497
vendor/golang.org/x/tools/go/ssa/interp/value.go generated vendored Normal file
View File

@@ -0,0 +1,497 @@
// 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"
"go/types"
"io"
"reflect"
"strings"
"sync"
"unsafe"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
)
type value interface{}
type tuple []value
type array []value
type iface struct {
t types.Type // never an "untyped" type
v value
}
type structure []value
// For map, array, *array, slice, string or channel.
type iter interface {
// next returns a Tuple (key, value, ok).
// key and value are unaliased, e.g. copies of the sequence element.
next() tuple
}
type closure struct {
Fn *ssa.Function
Env []value
}
type bad struct{}
type rtype struct {
t types.Type
}
// Hash functions and equivalence relation:
// hashString computes the FNV hash of s.
func hashString(s string) int {
var h uint32
for i := 0; i < len(s); i++ {
h ^= uint32(s[i])
h *= 16777619
}
return int(h)
}
var (
mu sync.Mutex
hasher = typeutil.MakeHasher()
)
// hashType returns a hash for t such that
// types.Identical(x, y) => hashType(x) == hashType(y).
func hashType(t types.Type) int {
mu.Lock()
h := int(hasher.Hash(t))
mu.Unlock()
return h
}
// usesBuiltinMap returns true if the built-in hash function and
// equivalence relation for type t are consistent with those of the
// interpreter's representation of type t. Such types are: all basic
// types (bool, numbers, string), pointers and channels.
//
// usesBuiltinMap returns false for types that require a custom map
// implementation: interfaces, arrays and structs.
//
// Panic ensues if t is an invalid map key type: function, map or slice.
func usesBuiltinMap(t types.Type) bool {
switch t := t.(type) {
case *types.Basic, *types.Chan, *types.Pointer:
return true
case *types.Named:
return usesBuiltinMap(t.Underlying())
case *types.Interface, *types.Array, *types.Struct:
return false
}
panic(fmt.Sprintf("invalid map key type: %T", t))
}
func (x array) eq(t types.Type, _y interface{}) bool {
y := _y.(array)
tElt := t.Underlying().(*types.Array).Elem()
for i, xi := range x {
if !equals(tElt, xi, y[i]) {
return false
}
}
return true
}
func (x array) hash(t types.Type) int {
h := 0
tElt := t.Underlying().(*types.Array).Elem()
for _, xi := range x {
h += hash(tElt, xi)
}
return h
}
func (x structure) eq(t types.Type, _y interface{}) bool {
y := _y.(structure)
tStruct := t.Underlying().(*types.Struct)
for i, n := 0, tStruct.NumFields(); i < n; i++ {
if f := tStruct.Field(i); !f.Anonymous() {
if !equals(f.Type(), x[i], y[i]) {
return false
}
}
}
return true
}
func (x structure) hash(t types.Type) int {
tStruct := t.Underlying().(*types.Struct)
h := 0
for i, n := 0, tStruct.NumFields(); i < n; i++ {
if f := tStruct.Field(i); !f.Anonymous() {
h += hash(f.Type(), x[i])
}
}
return h
}
// nil-tolerant variant of types.Identical.
func sameType(x, y types.Type) bool {
if x == nil {
return y == nil
}
return y != nil && types.Identical(x, y)
}
func (x iface) eq(t types.Type, _y interface{}) bool {
y := _y.(iface)
return sameType(x.t, y.t) && (x.t == nil || equals(x.t, x.v, y.v))
}
func (x iface) hash(_ types.Type) int {
return hashType(x.t)*8581 + hash(x.t, x.v)
}
func (x rtype) hash(_ types.Type) int {
return hashType(x.t)
}
func (x rtype) eq(_ types.Type, y interface{}) bool {
return types.Identical(x.t, y.(rtype).t)
}
// equals returns true iff x and y are equal according to Go's
// linguistic equivalence relation for type t.
// In a well-typed program, the dynamic types of x and y are
// guaranteed equal.
func equals(t types.Type, x, y value) bool {
switch x := x.(type) {
case bool:
return x == y.(bool)
case int:
return x == y.(int)
case int8:
return x == y.(int8)
case int16:
return x == y.(int16)
case int32:
return x == y.(int32)
case int64:
return x == y.(int64)
case uint:
return x == y.(uint)
case uint8:
return x == y.(uint8)
case uint16:
return x == y.(uint16)
case uint32:
return x == y.(uint32)
case uint64:
return x == y.(uint64)
case uintptr:
return x == y.(uintptr)
case float32:
return x == y.(float32)
case float64:
return x == y.(float64)
case complex64:
return x == y.(complex64)
case complex128:
return x == y.(complex128)
case string:
return x == y.(string)
case *value:
return x == y.(*value)
case chan value:
return x == y.(chan value)
case structure:
return x.eq(t, y)
case array:
return x.eq(t, y)
case iface:
return x.eq(t, y)
case rtype:
return x.eq(t, y)
}
// Since map, func and slice don't support comparison, this
// case is only reachable if one of x or y is literally nil
// (handled in eqnil) or via interface{} values.
panic(fmt.Sprintf("comparing uncomparable type %s", t))
}
// Returns an integer hash of x such that equals(x, y) => hash(x) == hash(y).
func hash(t types.Type, x value) int {
switch x := x.(type) {
case bool:
if x {
return 1
}
return 0
case int:
return x
case int8:
return int(x)
case int16:
return int(x)
case int32:
return int(x)
case int64:
return int(x)
case uint:
return int(x)
case uint8:
return int(x)
case uint16:
return int(x)
case uint32:
return int(x)
case uint64:
return int(x)
case uintptr:
return int(x)
case float32:
return int(x)
case float64:
return int(x)
case complex64:
return int(real(x))
case complex128:
return int(real(x))
case string:
return hashString(x)
case *value:
return int(uintptr(unsafe.Pointer(x)))
case chan value:
return int(uintptr(reflect.ValueOf(x).Pointer()))
case structure:
return x.hash(t)
case array:
return x.hash(t)
case iface:
return x.hash(t)
case rtype:
return x.hash(t)
}
panic(fmt.Sprintf("%T is unhashable", x))
}
// reflect.Value struct values don't have a fixed shape, since the
// payload can be a scalar or an aggregate depending on the instance.
// So store (and load) can't simply use recursion over the shape of the
// rhs value, or the lhs, to copy the value; we need the static type
// information. (We can't make reflect.Value a new basic data type
// because its "structness" is exposed to Go programs.)
// load returns the value of type T in *addr.
func load(T types.Type, addr *value) value {
switch T := T.Underlying().(type) {
case *types.Struct:
v := (*addr).(structure)
a := make(structure, len(v))
for i := range a {
a[i] = load(T.Field(i).Type(), &v[i])
}
return a
case *types.Array:
v := (*addr).(array)
a := make(array, len(v))
for i := range a {
a[i] = load(T.Elem(), &v[i])
}
return a
default:
return *addr
}
}
// store stores value v of type T into *addr.
func store(T types.Type, addr *value, v value) {
switch T := T.Underlying().(type) {
case *types.Struct:
lhs := (*addr).(structure)
rhs := v.(structure)
for i := range lhs {
store(T.Field(i).Type(), &lhs[i], rhs[i])
}
case *types.Array:
lhs := (*addr).(array)
rhs := v.(array)
for i := range lhs {
store(T.Elem(), &lhs[i], rhs[i])
}
default:
*addr = v
}
}
// Prints in the style of built-in println.
// (More or less; in gc println is actually a compiler intrinsic and
// can distinguish println(1) from println(interface{}(1)).)
func writeValue(buf *bytes.Buffer, v value) {
switch v := v.(type) {
case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string:
fmt.Fprintf(buf, "%v", v)
case map[value]value:
buf.WriteString("map[")
sep := ""
for k, e := range v {
buf.WriteString(sep)
sep = " "
writeValue(buf, k)
buf.WriteString(":")
writeValue(buf, e)
}
buf.WriteString("]")
case *hashmap:
buf.WriteString("map[")
sep := " "
for _, e := range v.entries() {
for e != nil {
buf.WriteString(sep)
sep = " "
writeValue(buf, e.key)
buf.WriteString(":")
writeValue(buf, e.value)
e = e.next
}
}
buf.WriteString("]")
case chan value:
fmt.Fprintf(buf, "%v", v) // (an address)
case *value:
if v == nil {
buf.WriteString("<nil>")
} else {
fmt.Fprintf(buf, "%p", v)
}
case iface:
fmt.Fprintf(buf, "(%s, ", v.t)
writeValue(buf, v.v)
buf.WriteString(")")
case structure:
buf.WriteString("{")
for i, e := range v {
if i > 0 {
buf.WriteString(" ")
}
writeValue(buf, e)
}
buf.WriteString("}")
case array:
buf.WriteString("[")
for i, e := range v {
if i > 0 {
buf.WriteString(" ")
}
writeValue(buf, e)
}
buf.WriteString("]")
case []value:
buf.WriteString("[")
for i, e := range v {
if i > 0 {
buf.WriteString(" ")
}
writeValue(buf, e)
}
buf.WriteString("]")
case *ssa.Function, *ssa.Builtin, *closure:
fmt.Fprintf(buf, "%p", v) // (an address)
case rtype:
buf.WriteString(v.t.String())
case tuple:
// Unreachable in well-formed Go programs
buf.WriteString("(")
for i, e := range v {
if i > 0 {
buf.WriteString(", ")
}
writeValue(buf, e)
}
buf.WriteString(")")
default:
fmt.Fprintf(buf, "<%T>", v)
}
}
// Implements printing of Go values in the style of built-in println.
func toString(v value) string {
var b bytes.Buffer
writeValue(&b, v)
return b.String()
}
// ------------------------------------------------------------------------
// Iterators
type stringIter struct {
*strings.Reader
i int
}
func (it *stringIter) next() tuple {
okv := make(tuple, 3)
ch, n, err := it.ReadRune()
ok := err != io.EOF
okv[0] = ok
if ok {
okv[1] = it.i
okv[2] = ch
}
it.i += n
return okv
}
type mapIter chan [2]value
func (it mapIter) next() tuple {
kv, ok := <-it
return tuple{ok, kv[0], kv[1]}
}

653
vendor/golang.org/x/tools/go/ssa/lift.go generated vendored Normal file
View File

@@ -0,0 +1,653 @@
// 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.
//
// Also see many other "TODO: opt" suggestions in the code.
import (
"fmt"
"go/token"
"go/types"
"math/big"
"os"
)
// If true, show diagnostic information at each step of lifting.
// Very verbose.
const debugLifting = false
// domFrontier maps each block to the set of blocks in its dominance
// frontier. The outer slice is conceptually a map keyed by
// Block.Index. The inner slice is conceptually a set, possibly
// containing duplicates.
//
// TODO(adonovan): opt: measure impact of dups; consider a packed bit
// representation, e.g. big.Int, and bitwise parallel operations for
// the union step in the Children loop.
//
// domFrontier's methods mutate the slice's elements but not its
// length, so their receivers needn't be pointers.
//
type domFrontier [][]*BasicBlock
func (df domFrontier) add(u, v *BasicBlock) {
p := &df[u.Index]
*p = append(*p, v)
}
// build builds the dominance frontier df for the dominator (sub)tree
// rooted at u, using the Cytron et al. algorithm.
//
// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
// by pruning the entire IDF computation, rather than merely pruning
// the DF -> IDF step.
func (df domFrontier) build(u *BasicBlock) {
// Encounter each node u in postorder of dom tree.
for _, child := range u.dom.children {
df.build(child)
}
for _, vb := range u.Succs {
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
for _, w := range u.dom.children {
for _, vb := range df[w.Index] {
// TODO(adonovan): opt: use word-parallel bitwise union.
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
}
}
func buildDomFrontier(fn *Function) domFrontier {
df := make(domFrontier, len(fn.Blocks))
df.build(fn.Blocks[0])
if fn.Recover != nil {
df.build(fn.Recover)
}
return df
}
func removeInstr(refs []Instruction, instr Instruction) []Instruction {
i := 0
for _, ref := range refs {
if ref == instr {
continue
}
refs[i] = ref
i++
}
for j := i; j != len(refs); j++ {
refs[j] = nil // aid GC
}
return refs[:i]
}
// lift replaces local and new Allocs accessed only with
// load/store by SSA registers, inserting φ-nodes where necessary.
// The result is a program in classical pruned SSA form.
//
// Preconditions:
// - fn has no dead blocks (blockopt has run).
// - Def/use info (Operands and Referrers) is up-to-date.
// - The dominator tree is up-to-date.
//
func lift(fn *Function) {
// TODO(adonovan): opt: lots of little optimizations may be
// worthwhile here, especially if they cause us to avoid
// buildDomFrontier. For example:
//
// - Alloc never loaded? Eliminate.
// - Alloc never stored? Replace all loads with a zero constant.
// - Alloc stored once? Replace loads with dominating store;
// don't forget that an Alloc is itself an effective store
// of zero.
// - Alloc used only within a single block?
// Use degenerate algorithm avoiding φ-nodes.
// - Consider synergy with scalar replacement of aggregates (SRA).
// e.g. *(&x.f) where x is an Alloc.
// Perhaps we'd get better results if we generated this as x.f
// i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
// Unclear.
//
// But we will start with the simplest correct code.
df := buildDomFrontier(fn)
if debugLifting {
title := false
for i, blocks := range df {
if blocks != nil {
if !title {
fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
title = true
}
fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
}
}
}
newPhis := make(newPhiMap)
// During this pass we will replace some BasicBlock.Instrs
// (allocs, loads and stores) with nil, keeping a count in
// BasicBlock.gaps. At the end we will reset Instrs to the
// concatenation of all non-dead newPhis and non-nil Instrs
// for the block, reusing the original array if space permits.
// While we're here, we also eliminate 'rundefers'
// instructions in functions that contain no 'defer'
// instructions.
usesDefer := false
// A counter used to generate ~unique ids for Phi nodes, as an
// aid to debugging. We use large numbers to make them highly
// visible. All nodes are renumbered later.
fresh := 1000
// 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, &fresh) {
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 φ-nodes.
removeDeadPhis(fn.Blocks, newPhis)
// Prepend remaining live φ-nodes to each block.
for _, b := range fn.Blocks {
nps := newPhis[b]
j := len(nps)
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 (rightwards)
// if Instrs has sufficient space or slack.
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++
}
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]
}
// removeDeadPhis removes φ-nodes not transitively needed by a
// non-Phi, non-DebugRef instruction.
func removeDeadPhis(blocks []*BasicBlock, newPhis newPhiMap) {
// First pass: find the set of "live" φ-nodes: those reachable
// from some non-Phi instruction.
//
// We compute reachability in reverse, starting from each φ,
// rather than forwards, starting from each live non-Phi
// instruction, because this way visits much less of the
// Value graph.
livePhis := make(map[*Phi]bool)
for _, npList := range newPhis {
for _, np := range npList {
phi := np.phi
if !livePhis[phi] && phiHasDirectReferrer(phi) {
markLivePhi(livePhis, phi)
}
}
}
// Existing φ-nodes due to && and || operators
// are all considered live (see Go issue 19622).
for _, b := range blocks {
for _, phi := range b.phis() {
markLivePhi(livePhis, phi.(*Phi))
}
}
// Second pass: eliminate unused phis from newPhis.
for block, npList := range newPhis {
j := 0
for _, np := range npList {
if livePhis[np.phi] {
npList[j] = np
j++
} else {
// discard it, first removing it from referrers
for _, val := range np.phi.Edges {
if refs := val.Referrers(); refs != nil {
*refs = removeInstr(*refs, np.phi)
}
}
np.phi.block = nil
}
}
newPhis[block] = npList[:j]
}
}
// markLivePhi marks phi, and all φ-nodes transitively reachable via
// its Operands, live.
func markLivePhi(livePhis map[*Phi]bool, phi *Phi) {
livePhis[phi] = true
for _, rand := range phi.Operands(nil) {
if q, ok := (*rand).(*Phi); ok {
if !livePhis[q] {
markLivePhi(livePhis, q)
}
}
}
}
// phiHasDirectReferrer reports whether phi is directly referred to by
// a non-Phi instruction. Such instructions are the
// roots of the liveness traversal.
func phiHasDirectReferrer(phi *Phi) bool {
for _, instr := range *phi.Referrers() {
if _, ok := instr.(*Phi); !ok {
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.
//
// fresh is a source of fresh ids for phi nodes.
//
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) 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,
}
// This is merely a debugging aid:
phi.setNum(*fresh)
*fresh++
phi.pos = alloc.Pos()
phi.setType(deref(alloc.Type()))
phi.block = v
if debugLifting {
fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
}
newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
if work.add(v) {
W.add(v)
}
}
}
}
return true
}
// replaceAll replaces all intraprocedural uses of x with y,
// updating x.Referrers and y.Referrers.
// Precondition: x.Referrers() != nil, i.e. x must be local to some function.
//
func replaceAll(x, y Value) {
var rands []*Value
pxrefs := x.Referrers()
pyrefs := y.Referrers()
for _, instr := range *pxrefs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if *rand != nil {
if *rand == x {
*rand = y
}
}
}
if pyrefs != nil {
*pyrefs = append(*pyrefs, instr) // dups ok
}
}
*pxrefs = nil // x is now unreferenced
}
// renamed returns the value to which alloc is being renamed,
// constructing it lazily if it's the implicit zero initialization.
//
func renamed(renaming []Value, alloc *Alloc) Value {
v := renaming[alloc.index]
if v == nil {
v = zeroConst(deref(alloc.Type()))
renaming[alloc.index] = v
}
return v
}
// rename implements the (Cytron et al) SSA renaming algorithm, a
// preorder traversal of the dominator tree replacing all loads of
// Alloc cells with the value stored to that cell by the dominating
// store instruction. For lifting, we need only consider loads,
// stores and φ-nodes.
//
// renaming is a map from *Alloc (keyed by index number) to its
// dominating stored value; newPhis[x] is the set of new φ-nodes to be
// prepended to block x.
//
func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
// Each φ-node becomes the new name for its associated Alloc.
for _, np := range newPhis[u] {
phi := np.phi
alloc := np.alloc
renaming[alloc.index] = phi
}
// Rename loads and stores of allocs.
for i, instr := range u.Instrs {
switch instr := instr.(type) {
case *Alloc:
if instr.index >= 0 { // store of zero to Alloc cell
// Replace dominated loads by the zero value.
renaming[instr.index] = nil
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
}
// Delete the Alloc.
u.Instrs[i] = nil
u.gaps++
}
case *Store:
if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
// Replace dominated loads by the stored value.
renaming[alloc.index] = instr.Val
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
instr, instr.Val.Name())
}
// Remove the store from the referrer list of the stored value.
if refs := instr.Val.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
// Delete the Store.
u.Instrs[i] = nil
u.gaps++
}
case *UnOp:
if instr.Op == token.MUL {
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
instr.Name(), instr, newval.Name())
}
// Replace all references to
// the loaded value by the
// dominating stored value.
replaceAll(instr, newval)
// Delete the Load.
u.Instrs[i] = nil
u.gaps++
}
}
case *DebugRef:
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell
if instr.IsAddr {
instr.X = renamed(renaming, alloc)
instr.IsAddr = false
// Add DebugRef to instr.X's referrers.
if refs := instr.X.Referrers(); refs != nil {
*refs = append(*refs, instr)
}
} else {
// A source expression denotes the address
// of an Alloc that was optimized away.
instr.X = nil
// Delete the DebugRef.
u.Instrs[i] = nil
u.gaps++
}
}
}
}
// For each φ-node in a CFG successor, rename the edge.
for _, v := range u.Succs {
phis := newPhis[v]
if len(phis) == 0 {
continue
}
i := v.predIndex(u)
for _, np := range phis {
phi := np.phi
alloc := np.alloc
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
phi.Name(), u, v, i, alloc.Name(), newval.Name())
}
phi.Edges[i] = newval
if prefs := newval.Referrers(); prefs != nil {
*prefs = append(*prefs, phi)
}
}
}
// Continue depth-first recursion over domtree, pushing a
// fresh copy of the renaming map for each subtree.
for i, v := range u.dom.children {
r := renaming
if i < len(u.dom.children)-1 {
// On all but the final iteration, we must make
// a copy to avoid destructive update.
r = make([]Value, len(renaming))
copy(r, renaming)
}
rename(v, r, newPhis)
}
}

120
vendor/golang.org/x/tools/go/ssa/lvalue.go generated vendored Normal file
View File

@@ -0,0 +1,120 @@
// 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"
"go/types"
)
// An lvalue represents an assignable location that may appear on the
// left-hand side of an assignment. This is a generalization of a
// pointer to permit updates to elements of maps.
//
type lvalue interface {
store(fn *Function, v Value) // stores v into the location
load(fn *Function) Value // loads the contents of the location
address(fn *Function) Value // address of the location
typ() types.Type // returns the type of the location
}
// An address is an lvalue represented by a true pointer.
type address struct {
addr Value
pos token.Pos // source position
expr ast.Expr // source syntax of the value (not address) [debug mode]
}
func (a *address) load(fn *Function) Value {
load := emitLoad(fn, a.addr)
load.pos = a.pos
return load
}
func (a *address) store(fn *Function, v Value) {
store := emitStore(fn, a.addr, v, a.pos)
if a.expr != nil {
// store.Val is v, converted for assignability.
emitDebugRef(fn, a.expr, store.Val, false)
}
}
func (a *address) address(fn *Function) Value {
if a.expr != nil {
emitDebugRef(fn, a.expr, a.addr, true)
}
return a.addr
}
func (a *address) typ() types.Type {
return deref(a.addr.Type())
}
// An element is an lvalue represented by m[k], the location of an
// element of a map or string. These locations are not addressable
// since pointers cannot be formed from them, but they do support
// load(), and in the case of maps, store().
//
type element struct {
m, k Value // map or string
t types.Type // map element type or string byte type
pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
}
func (e *element) load(fn *Function) Value {
l := &Lookup{
X: e.m,
Index: e.k,
}
l.setPos(e.pos)
l.setType(e.t)
return fn.emit(l)
}
func (e *element) store(fn *Function, v Value) {
up := &MapUpdate{
Map: e.m,
Key: e.k,
Value: emitConv(fn, v, e.t),
}
up.pos = e.pos
fn.emit(up)
}
func (e *element) address(fn *Function) Value {
panic("map/string elements are not addressable")
}
func (e *element) typ() types.Type {
return e.t
}
// A blank is a dummy variable whose name is "_".
// It is not reified: loads are illegal and stores are ignored.
//
type blank struct{}
func (bl blank) load(fn *Function) Value {
panic("blank.load is illegal")
}
func (bl blank) store(fn *Function, v Value) {
// no-op
}
func (bl blank) address(fn *Function) Value {
panic("blank var is not addressable")
}
func (bl blank) typ() types.Type {
// This should be the type of the blank Ident; the typechecker
// doesn't provide this yet, but fortunately, we don't need it
// yet either.
panic("blank.typ is unimplemented")
}

239
vendor/golang.org/x/tools/go/ssa/methods.go generated vendored Normal file
View File

@@ -0,0 +1,239 @@
// 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.
import (
"fmt"
"go/types"
)
// MethodValue returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
// abstract (interface) method.
//
// Precondition: sel.Kind() == MethodVal.
//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) MethodValue(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel))
}
T := sel.Recv()
if isInterface(T) {
return nil // abstract method
}
if prog.mode&LogSource != 0 {
defer logStack("Method %s %v", T, sel)()
}
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
return prog.addMethod(prog.createMethodSet(T), sel)
}
// LookupMethod returns the implementation of the method of type T
// identified by (pkg, name). It returns nil if the method exists but
// is abstract, and panics if T has no such method.
//
func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
if sel == nil {
panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
}
return prog.MethodValue(sel)
}
// methodSet contains the (concrete) methods of a non-interface type.
type methodSet struct {
mapping map[string]*Function // populated lazily
complete bool // mapping contains all methods
}
// Precondition: !isInterface(T).
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
func (prog *Program) createMethodSet(T types.Type) *methodSet {
mset, ok := prog.methodSets.At(T).(*methodSet)
if !ok {
mset = &methodSet{mapping: make(map[string]*Function)}
prog.methodSets.Set(T, mset)
}
return mset
}
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
if sel.Kind() == types.MethodExpr {
panic(sel)
}
id := sel.Obj().Id()
fn := mset.mapping[id]
if fn == nil {
obj := sel.Obj().(*types.Func)
needsPromotion := len(sel.Index()) > 1
needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
if needsPromotion || needsIndirection {
fn = makeWrapper(prog, sel)
} else {
fn = prog.declaredFunc(obj)
}
if fn.Signature.Recv() == nil {
panic(fn) // missing receiver
}
mset.mapping[id] = fn
}
return fn
}
// RuntimeTypes returns a new unordered slice containing all
// concrete types in the program for which a complete (non-empty)
// method set is required at run-time.
//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) RuntimeTypes() []types.Type {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
var res []types.Type
prog.methodSets.Iterate(func(T types.Type, v interface{}) {
if v.(*methodSet).complete {
res = append(res, T)
}
})
return res
}
// declaredFunc returns the concrete function/method denoted by obj.
// Panic ensues if there is none.
//
func (prog *Program) declaredFunc(obj *types.Func) *Function {
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Function)
}
panic("no concrete method: " + obj.String())
}
// needMethodsOf ensures that runtime type information (including the
// complete method set) is available for the specified type T and all
// its subcomponents.
//
// needMethodsOf must be called for at least every type that is an
// operand of some MakeInterface instruction, and for the type of
// every exported package member.
//
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
//
// Thread-safe. (Called via emitConv from multiple builder goroutines.)
//
// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) needMethodsOf(T types.Type) {
prog.methodsMu.Lock()
prog.needMethods(T, false)
prog.methodsMu.Unlock()
}
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
// Recursive case: skip => don't create methods for T.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func (prog *Program) needMethods(T types.Type, skip bool) {
// Each package maintains its own set of types it has visited.
if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
// needMethods(T) was previously called
if !prevSkip || skip {
return // already seen, with same or false 'skip' value
}
}
prog.runtimeTypes.Set(T, skip)
tmset := prog.MethodSets.MethodSet(T)
if !skip && !isInterface(T) && tmset.Len() > 0 {
// Create methods of T.
mset := prog.createMethodSet(T)
if !mset.complete {
mset.complete = true
n := tmset.Len()
for i := 0; i < n; i++ {
prog.addMethod(mset, tmset.At(i))
}
}
}
// Recursion over signatures of each method.
for i := 0; i < tmset.Len(); i++ {
sig := tmset.At(i).Type().(*types.Signature)
prog.needMethods(sig.Params(), false)
prog.needMethods(sig.Results(), false)
}
switch t := T.(type) {
case *types.Basic:
// nop
case *types.Interface:
// nop---handled by recursion over method set.
case *types.Pointer:
prog.needMethods(t.Elem(), false)
case *types.Slice:
prog.needMethods(t.Elem(), false)
case *types.Chan:
prog.needMethods(t.Elem(), false)
case *types.Map:
prog.needMethods(t.Key(), false)
prog.needMethods(t.Elem(), false)
case *types.Signature:
if t.Recv() != nil {
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
}
prog.needMethods(t.Params(), false)
prog.needMethods(t.Results(), false)
case *types.Named:
// A pointer-to-named type can be derived from a named
// type via reflection. It may have methods too.
prog.needMethods(types.NewPointer(T), false)
// Consider 'type T struct{S}' where S has methods.
// Reflection provides no way to get from T to struct{S},
// only to S, so the method set of struct{S} is unwanted,
// so set 'skip' flag during recursion.
prog.needMethods(t.Underlying(), true)
case *types.Array:
prog.needMethods(t.Elem(), false)
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
prog.needMethods(t.Field(i).Type(), false)
}
case *types.Tuple:
for i, n := 0, t.Len(); i < n; i++ {
prog.needMethods(t.At(i).Type(), false)
}
default:
panic(T)
}
}

100
vendor/golang.org/x/tools/go/ssa/mode.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines the BuilderMode type and its command-line flag.
import (
"bytes"
"fmt"
)
// BuilderMode is a bitmask of options for diagnostics and checking.
//
// *BuilderMode satisfies the flag.Value interface. Example:
//
// var mode = ssa.BuilderMode(0)
// func init() { flag.Var(&mode, "build", ssa.BuilderModeDoc) }
//
type BuilderMode uint
const (
PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout
PrintFunctions // Print function SSA code to stdout
LogSource // Log 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.
GlobalDebug // Enable debug info for all packages
BareInits // Build init functions without guards or calls to dependent inits
)
const BuilderModeDoc = `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 print [P]ackage inventory.
F print [F]unction SSA code.
S log [S]ource locations as SSA builder progresses.
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.
I build bare [I]nit functions: no init guards or calls to dependent inits.
`
func (m BuilderMode) String() string {
var buf bytes.Buffer
if m&GlobalDebug != 0 {
buf.WriteByte('D')
}
if m&PrintPackages != 0 {
buf.WriteByte('P')
}
if m&PrintFunctions != 0 {
buf.WriteByte('F')
}
if m&LogSource != 0 {
buf.WriteByte('S')
}
if m&SanityCheckFunctions != 0 {
buf.WriteByte('C')
}
if m&NaiveForm != 0 {
buf.WriteByte('N')
}
if m&BuildSerially != 0 {
buf.WriteByte('L')
}
return buf.String()
}
// Set parses the flag characters in s and updates *m.
func (m *BuilderMode) Set(s string) error {
var mode BuilderMode
for _, c := range s {
switch c {
case 'D':
mode |= GlobalDebug
case 'P':
mode |= PrintPackages
case 'F':
mode |= PrintFunctions
case 'S':
mode |= LogSource | BuildSerially
case 'C':
mode |= SanityCheckFunctions
case 'N':
mode |= NaiveForm
case 'L':
mode |= BuildSerially
default:
return fmt.Errorf("unknown BuilderMode option: %q", c)
}
}
*m = mode
return nil
}
// Get returns m.
func (m BuilderMode) Get() interface{} { return m }

431
vendor/golang.org/x/tools/go/ssa/print.go generated vendored Normal file
View File

@@ -0,0 +1,431 @@
// 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/types"
"io"
"reflect"
"sort"
"golang.org/x/tools/go/types/typeutil"
)
// relName returns the name of v relative to i.
// In most cases, this is identical to v.Name(), but references to
// Functions (including methods) and Globals use RelString and
// all types are displayed with relType, so that only cross-package
// references are package-qualified.
//
func relName(v Value, i Instruction) string {
var from *types.Package
if i != nil {
from = i.Parent().pkg()
}
switch v := v.(type) {
case Member: // *Function or *Global
return v.RelString(from)
case *Const:
return v.RelString(from)
}
return v.Name()
}
func relType(t types.Type, from *types.Package) string {
return types.TypeString(t, types.RelativeTo(from))
}
func relString(m Member, from *types.Package) string {
// NB: not all globals have an Object (e.g. init$guard),
// so use Package().Object not Object.Package().
if pkg := m.Package().Pkg; pkg != nil && pkg != from {
return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
}
return m.Name()
}
// Value.String()
//
// This method is provided only for debugging.
// It never appears in disassembly, which uses Value.Name().
func (v *Parameter) String() string {
from := v.Parent().pkg()
return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *FreeVar) String() string {
from := v.Parent().pkg()
return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s", v.Name())
}
// Instruction.String()
func (v *Alloc) String() string {
op := "local"
if v.Heap {
op = "new"
}
from := v.Parent().pkg()
return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment)
}
func (v *Phi) String() string {
var b bytes.Buffer
b.WriteString("phi [")
for i, edge := range v.Edges {
if i > 0 {
b.WriteString(", ")
}
// Be robust against malformed CFG.
if v.block == nil {
b.WriteString("??")
continue
}
block := -1
if i < len(v.block.Preds) {
block = v.block.Preds[i].Index
}
fmt.Fprintf(&b, "%d: ", block)
edgeVal := "<nil>" // be robust
if edge != nil {
edgeVal = relName(edge, v)
}
b.WriteString(edgeVal)
}
b.WriteString("]")
if v.Comment != "" {
b.WriteString(" #")
b.WriteString(v.Comment)
}
return b.String()
}
func printCall(v *CallCommon, prefix string, instr Instruction) string {
var b bytes.Buffer
b.WriteString(prefix)
if !v.IsInvoke() {
b.WriteString(relName(v.Value, instr))
} else {
fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
}
b.WriteString("(")
for i, arg := range v.Args {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(arg, instr))
}
if v.Signature().Variadic() {
b.WriteString("...")
}
b.WriteString(")")
return b.String()
}
func (c *CallCommon) String() string {
return printCall(c, "", nil)
}
func (v *Call) String() string {
return printCall(&v.Call, "", v)
}
func (v *BinOp) String() string {
return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
}
func (v *UnOp) String() string {
return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
}
func printConv(prefix string, v, x Value) string {
from := v.Parent().pkg()
return fmt.Sprintf("%s %s <- %s (%s)",
prefix,
relType(v.Type(), from),
relType(x.Type(), from),
relName(x, v.(Instruction)))
}
func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
func (v *Convert) String() string { return printConv("convert", v, v.X) }
func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
func (v *MakeClosure) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
if v.Bindings != nil {
b.WriteString(" [")
for i, c := range v.Bindings {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(c, v))
}
b.WriteString("]")
}
return b.String()
}
func (v *MakeSlice) String() string {
from := v.Parent().pkg()
return fmt.Sprintf("make %s %s %s",
relType(v.Type(), from),
relName(v.Len, v),
relName(v.Cap, v))
}
func (v *Slice) String() string {
var b bytes.Buffer
b.WriteString("slice ")
b.WriteString(relName(v.X, v))
b.WriteString("[")
if v.Low != nil {
b.WriteString(relName(v.Low, v))
}
b.WriteString(":")
if v.High != nil {
b.WriteString(relName(v.High, v))
}
if v.Max != nil {
b.WriteString(":")
b.WriteString(relName(v.Max, v))
}
b.WriteString("]")
return b.String()
}
func (v *MakeMap) String() string {
res := ""
if v.Reserve != nil {
res = relName(v.Reserve, v)
}
from := v.Parent().pkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
}
func (v *MakeChan) String() string {
from := v.Parent().pkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
}
func (v *FieldAddr) String() string {
st := deref(v.X.Type()).Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
name = st.Field(v.Field).Name()
}
return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *Field) String() string {
st := v.X.Type().Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
name = st.Field(v.Field).Name()
}
return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *IndexAddr) String() string {
return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Index) String() string {
return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Lookup) String() string {
return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
}
func (v *Range) String() string {
return "range " + relName(v.X, v)
}
func (v *Next) String() string {
return "next " + relName(v.Iter, v)
}
func (v *TypeAssert) String() string {
from := v.Parent().pkg()
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
}
func (v *Extract) String() string {
return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
}
func (s *Jump) String() string {
// Be robust against malformed CFG.
block := -1
if s.block != nil && len(s.block.Succs) == 1 {
block = s.block.Succs[0].Index
}
return fmt.Sprintf("jump %d", block)
}
func (s *If) String() string {
// Be robust against malformed CFG.
tblock, fblock := -1, -1
if s.block != nil && len(s.block.Succs) == 2 {
tblock = s.block.Succs[0].Index
fblock = s.block.Succs[1].Index
}
return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
}
func (s *Go) String() string {
return printCall(&s.Call, "go ", s)
}
func (s *Panic) String() string {
return "panic " + relName(s.X, s)
}
func (s *Return) String() string {
var b bytes.Buffer
b.WriteString("return")
for i, r := range s.Results {
if i == 0 {
b.WriteString(" ")
} else {
b.WriteString(", ")
}
b.WriteString(relName(r, s))
}
return b.String()
}
func (*RunDefers) String() string {
return "rundefers"
}
func (s *Send) String() string {
return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
}
func (s *Defer) String() string {
return printCall(&s.Call, "defer ", s)
}
func (s *Select) String() string {
var b bytes.Buffer
for i, st := range s.States {
if i > 0 {
b.WriteString(", ")
}
if st.Dir == types.RecvOnly {
b.WriteString("<-")
b.WriteString(relName(st.Chan, s))
} else {
b.WriteString(relName(st.Chan, s))
b.WriteString("<-")
b.WriteString(relName(st.Send, s))
}
}
non := ""
if !s.Blocking {
non = "non"
}
return fmt.Sprintf("select %sblocking [%s]", non, b.String())
}
func (s *Store) String() string {
return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
}
func (s *MapUpdate) String() string {
return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
}
func (s *DebugRef) String() string {
p := s.Parent().Prog.Fset.Position(s.Pos())
var descr interface{}
if s.object != nil {
descr = s.object // e.g. "var x int"
} else {
descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
}
var addr string
if s.IsAddr {
addr = "address of "
}
return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
}
func (p *Package) String() string {
return "package " + p.Pkg.Path()
}
var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
func (p *Package) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WritePackage(&buf, p)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WritePackage writes to buf a human-readable summary of p.
func WritePackage(buf *bytes.Buffer, p *Package) {
fmt.Fprintf(buf, "%s:\n", p)
var names []string
maxname := 0
for name := range p.Members {
if l := len(name); l > maxname {
maxname = l
}
names = append(names, name)
}
from := p.Pkg
sort.Strings(names)
for _, name := range names {
switch mem := p.Members[name].(type) {
case *NamedConst:
fmt.Fprintf(buf, " const %-*s %s = %s\n",
maxname, name, mem.Name(), mem.Value.RelString(from))
case *Function:
fmt.Fprintf(buf, " func %-*s %s\n",
maxname, name, relType(mem.Type(), from))
case *Type:
fmt.Fprintf(buf, " type %-*s %s\n",
maxname, name, relType(mem.Type().Underlying(), from))
for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
}
case *Global:
fmt.Fprintf(buf, " var %-*s %s\n",
maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
}
}
fmt.Fprintf(buf, "\n")
}
func commaOk(x bool) string {
if x {
return ",ok"
}
return ""
}

521
vendor/golang.org/x/tools/go/ssa/sanity.go generated vendored Normal file
View File

@@ -0,0 +1,521 @@
// 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"
"go/types"
"io"
"os"
"strings"
)
type sanity struct {
reporter io.Writer
fn *Function
block *BasicBlock
instrs map[Instruction]struct{}
insane bool
}
// sanityCheck performs integrity checking of the SSA representation
// of the function fn and returns true if it was valid. Diagnostics
// are written to reporter if non-nil, os.Stderr otherwise. Some
// diagnostics are only warnings and do not imply a negative result.
//
// Sanity-checking is intended to facilitate the debugging of code
// transformation passes.
//
func sanityCheck(fn *Function, reporter io.Writer) bool {
if reporter == nil {
reporter = os.Stderr
}
return (&sanity{reporter: reporter}).checkFunction(fn)
}
// mustSanityCheck is like sanityCheck but panics instead of returning
// a negative result.
//
func mustSanityCheck(fn *Function, reporter io.Writer) {
if !sanityCheck(fn, reporter) {
fn.WriteTo(os.Stderr)
panic("SanityCheck failed")
}
}
func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
if s.block != nil {
fmt.Fprintf(s.reporter, ", block %s", s.block)
}
io.WriteString(s.reporter, ": ")
fmt.Fprintf(s.reporter, format, args...)
io.WriteString(s.reporter, "\n")
}
func (s *sanity) errorf(format string, args ...interface{}) {
s.insane = true
s.diagnostic("Error", format, args...)
}
func (s *sanity) warnf(format string, args ...interface{}) {
s.diagnostic("Warning", format, args...)
}
// findDuplicate returns an arbitrary basic block that appeared more
// than once in blocks, or nil if all were unique.
func findDuplicate(blocks []*BasicBlock) *BasicBlock {
if len(blocks) < 2 {
return nil
}
if blocks[0] == blocks[1] {
return blocks[0]
}
// Slow path:
m := make(map[*BasicBlock]bool)
for _, b := range blocks {
if m[b] {
return b
}
m[b] = true
}
return nil
}
func (s *sanity) checkInstr(idx int, instr Instruction) {
switch instr := instr.(type) {
case *If, *Jump, *Return, *Panic:
s.errorf("control flow instruction not at end of block")
case *Phi:
if idx == 0 {
// It suffices to apply this check to just the first phi node.
if dup := findDuplicate(s.block.Preds); dup != nil {
s.errorf("phi node in block with duplicate predecessor %s", dup)
}
} else {
prev := s.block.Instrs[idx-1]
if _, ok := prev.(*Phi); !ok {
s.errorf("Phi instruction follows a non-Phi: %T", prev)
}
}
if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
s.errorf("phi node has %d edges but %d predecessors", ne, np)
} else {
for i, e := range instr.Edges {
if e == nil {
s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i])
}
}
}
case *Alloc:
if !instr.Heap {
found := false
for _, l := range s.fn.Locals {
if l == instr {
found = true
break
}
}
if !found {
s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr)
}
}
case *BinOp:
case *Call:
case *ChangeInterface:
case *ChangeType:
case *Convert:
if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok {
if _, ok := instr.Type().Underlying().(*types.Basic); !ok {
s.errorf("convert %s -> %s: at least one type must be basic", instr.X.Type(), instr.Type())
}
}
case *Defer:
case *Extract:
case *Field:
case *FieldAddr:
case *Go:
case *Index:
case *IndexAddr:
case *Lookup:
case *MakeChan:
case *MakeClosure:
numFree := len(instr.Fn.(*Function).FreeVars)
numBind := len(instr.Bindings)
if numFree != numBind {
s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
numBind, instr.Fn, numFree)
}
if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
s.errorf("MakeClosure's type includes receiver %s", recv.Type())
}
case *MakeInterface:
case *MakeMap:
case *MakeSlice:
case *MapUpdate:
case *Next:
case *Range:
case *RunDefers:
case *Select:
case *Send:
case *Slice:
case *Store:
case *TypeAssert:
case *UnOp:
case *DebugRef:
// TODO(adonovan): implement checks.
default:
panic(fmt.Sprintf("Unknown instruction type: %T", instr))
}
if call, ok := instr.(CallInstruction); ok {
if call.Common().Signature() == nil {
s.errorf("nil signature: %s", call)
}
}
// Check that value-defining instructions have valid types
// and a valid referrer list.
if v, ok := instr.(Value); ok {
t := v.Type()
if t == nil {
s.errorf("no type: %s = %s", v.Name(), v)
} else if t == tRangeIter {
// not a proper type; ignore.
} else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
}
s.checkReferrerList(v)
}
// Untyped constants are legal as instruction Operands(),
// for example:
// _ = "foo"[0]
// or:
// if wordsize==64 {...}
// All other non-Instruction Values can be found via their
// enclosing Function or Package.
}
func (s *sanity) checkFinalInstr(instr Instruction) {
switch instr := instr.(type) {
case *If:
if nsuccs := len(s.block.Succs); nsuccs != 2 {
s.errorf("If-terminated block has %d successors; expected 2", nsuccs)
return
}
if s.block.Succs[0] == s.block.Succs[1] {
s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0])
return
}
case *Jump:
if nsuccs := len(s.block.Succs); nsuccs != 1 {
s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs)
return
}
case *Return:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Return-terminated block has %d successors; expected none", nsuccs)
return
}
if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
s.errorf("%d-ary return in %d-ary function", na, nf)
}
case *Panic:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
return
}
default:
s.errorf("non-control flow instruction at end of block")
}
}
func (s *sanity) checkBlock(b *BasicBlock, index int) {
s.block = b
if b.Index != index {
s.errorf("block has incorrect Index %d", b.Index)
}
if b.parent != s.fn {
s.errorf("block has incorrect parent %s", b.parent)
}
// Check all blocks are reachable.
// (The entry block is always implicitly reachable,
// as is the Recover block, if any.)
if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
s.warnf("unreachable block")
if b.Instrs == nil {
// Since this block is about to be pruned,
// tolerating transient problems in it
// simplifies other optimizations.
return
}
}
// Check predecessor and successor relations are dual,
// and that all blocks in CFG belong to same function.
for _, a := range b.Preds {
found := false
for _, bb := range a.Succs {
if bb == b {
found = true
break
}
}
if !found {
s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
}
if a.parent != s.fn {
s.errorf("predecessor %s belongs to different function %s", a, a.parent)
}
}
for _, c := range b.Succs {
found := false
for _, bb := range c.Preds {
if bb == b {
found = true
break
}
}
if !found {
s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
}
if c.parent != s.fn {
s.errorf("successor %s belongs to different function %s", c, c.parent)
}
}
// Check each instruction is sane.
n := len(b.Instrs)
if n == 0 {
s.errorf("basic block contains no instructions")
}
var rands [10]*Value // reuse storage
for j, instr := range b.Instrs {
if instr == nil {
s.errorf("nil instruction at index %d", j)
continue
}
if b2 := instr.Block(); b2 == nil {
s.errorf("nil Block() for instruction at index %d", j)
continue
} else if b2 != b {
s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j)
continue
}
if j < n-1 {
s.checkInstr(j, instr)
} else {
s.checkFinalInstr(instr)
}
// Check Instruction.Operands.
operands:
for i, op := range instr.Operands(rands[:0]) {
if op == nil {
s.errorf("nil operand pointer %d of %s", i, instr)
continue
}
val := *op
if val == nil {
continue // a nil operand is ok
}
// Check that "untyped" types only appear on constant operands.
if _, ok := (*op).(*Const); !ok {
if basic, ok := (*op).Type().(*types.Basic); ok {
if basic.Info()&types.IsUntyped != 0 {
s.errorf("operand #%d of %s is untyped: %s", i, instr, basic)
}
}
}
// Check that Operands that are also Instructions belong to same function.
// TODO(adonovan): also check their block dominates block b.
if val, ok := val.(Instruction); ok {
if val.Block() == nil {
s.errorf("operand %d of %s is an instruction (%s) that belongs to no block", i, instr, val)
} else if val.Parent() != s.fn {
s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
}
}
// Check that each function-local operand of
// instr refers back to instr. (NB: quadratic)
switch val := val.(type) {
case *Const, *Global, *Builtin:
continue // not local
case *Function:
if val.parent == nil {
continue // only anon functions are local
}
}
// TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
if refs := val.Referrers(); refs != nil {
for _, ref := range *refs {
if ref == instr {
continue operands
}
}
s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
} else {
s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
}
}
}
}
func (s *sanity) checkReferrerList(v Value) {
refs := v.Referrers()
if refs == nil {
s.errorf("%s has missing referrer list", v.Name())
return
}
for i, ref := range *refs {
if _, ok := s.instrs[ref]; !ok {
s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
}
}
}
func (s *sanity) checkFunction(fn *Function) bool {
// TODO(adonovan): check Function invariants:
// - check params match signature
// - check transient fields are nil
// - warn if any fn.Locals do not appear among block instructions.
s.fn = fn
if fn.Prog == nil {
s.errorf("nil Prog")
}
fn.String() // must not crash
fn.RelString(fn.pkg()) // must not crash
// All functions have a package, except delegates (which are
// shared across packages, or duplicated as weak symbols in a
// separate-compilation model), and error.Error.
if fn.Pkg == nil {
if strings.HasPrefix(fn.Synthetic, "wrapper ") ||
strings.HasPrefix(fn.Synthetic, "bound ") ||
strings.HasPrefix(fn.Synthetic, "thunk ") ||
strings.HasSuffix(fn.name, "Error") {
// ok
} else {
s.errorf("nil Pkg")
}
}
if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
}
for i, l := range fn.Locals {
if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
}
if l.Heap {
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
}
}
// Build the set of valid referrers.
s.instrs = make(map[Instruction]struct{})
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
s.instrs[instr] = struct{}{}
}
}
for i, p := range fn.Params {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
s.checkReferrerList(p)
}
for i, fv := range fn.FreeVars {
if fv.Parent() != fn {
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
}
s.checkReferrerList(fv)
}
if fn.Blocks != nil && len(fn.Blocks) == 0 {
// Function _had_ blocks (so it's not external) but
// they were "optimized" away, even the entry block.
s.errorf("Blocks slice is non-nil but empty")
}
for i, b := range fn.Blocks {
if b == nil {
s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
continue
}
s.checkBlock(b, i)
}
if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
s.errorf("Recover block is not in Blocks slice")
}
s.block = nil
for i, anon := range fn.AnonFuncs {
if anon.Parent() != fn {
s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
}
}
s.fn = nil
return !s.insane
}
// sanityCheckPackage checks invariants of packages upon creation.
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
if pkg.Pkg == nil {
panic(fmt.Sprintf("Package %s has no Object", pkg))
}
pkg.String() // must not crash
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
pkg.Pkg.Path(), mem, mem.Name(), name))
}
obj := mem.Object()
if obj == nil {
// This check is sound because fields
// {Global,Function}.object have type
// types.Object. (If they were declared as
// *types.{Var,Func}, we'd have a non-empty
// interface containing a nil pointer.)
continue // not all members have typechecker objects
}
if obj.Name() != name {
if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
// Ok. The name of a declared init function varies between
// its types.Func ("init") and its ssa.Function ("init#%d").
} else {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
pkg.Pkg.Path(), mem, obj.Name(), name))
}
}
if obj.Pos() != mem.Pos() {
panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))
}
}
}

293
vendor/golang.org/x/tools/go/ssa/source.go generated vendored Normal file
View File

@@ -0,0 +1,293 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
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"
"go/types"
)
// EnclosingFunction returns the function that contains the syntax
// node denoted by path.
//
// Syntax associated with package-level variable specifications is
// enclosed by the package's init() function.
//
// Returns nil if not found; reasons might include:
// - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and
// its SSA function has not been created yet
// (pkg.Build() has not yet been called).
//
func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function...
fn := findEnclosingPackageLevelFunction(pkg, path)
if fn == nil {
return nil // not in any function
}
// ...then walk down the nested anonymous functions.
n := len(path)
outer:
for i := range path {
if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
for _, anon := range fn.AnonFuncs {
if anon.Pos() == lit.Type.Func {
fn = anon
continue outer
}
}
// SSA function not found:
// - package not yet built, or maybe
// - builder skipped FuncLit in dead block
// (in principle; but currently the Builder
// generates even dead FuncLits).
return nil
}
}
return fn
}
// HasEnclosingFunction returns true if the AST node denoted by path
// is contained within the declaration of some function or
// package-level variable.
//
// Unlike EnclosingFunction, the behaviour of this function does not
// depend on whether SSA code for pkg has been built, so it can be
// used to quickly reject check inputs that will cause
// EnclosingFunction to fail, prior to SSA building.
//
func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
return findEnclosingPackageLevelFunction(pkg, path) != nil
}
// findEnclosingPackageLevelFunction returns the Function
// corresponding to the package-level function enclosing path.
//
func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
switch decl := path[n-2].(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR && n >= 3 {
// Package-level 'var' initializer.
return pkg.init
}
case *ast.FuncDecl:
if decl.Recv == nil && decl.Name.Name == "init" {
// Explicit init() function.
for _, b := range pkg.init.Blocks {
for _, instr := range b.Instrs {
if instr, ok := instr.(*Call); ok {
if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
return callee
}
}
}
}
// Hack: return non-nil when SSA is not yet
// built so that HasEnclosingFunction works.
return pkg.init
}
// Declared function/method.
return findNamedFunc(pkg, decl.Name.NamePos)
}
}
return nil // not in any function
}
// findNamedFunc returns the named function whose FuncDecl.Ident is at
// position pos.
//
func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// Look at all package members and method sets of named types.
// Not very efficient.
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *Function:
if mem.Pos() == pos {
return mem
}
case *Type:
mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
// Don't call Program.Method: avoid creating wrappers.
obj := mset.At(i).Obj().(*types.Func)
if obj.Pos() == pos {
return pkg.values[obj].(*Function)
}
}
}
}
return nil
}
// ValueForExpr returns the SSA Value that corresponds to non-constant
// expression e.
//
// It returns nil if no value was found, e.g.
// - the expression is not lexically contained within f;
// - f was not built with debug information; or
// - e is a constant expression. (For efficiency, no debug
// information is stored for constants. Use
// go/types.Info.Types[e].Value instead.)
// - e is a reference to nil or a built-in function.
// - the value was optimised away.
//
// If e is an addressable expression used in an lvalue context,
// value is the address denoted by e, and isAddr is true.
//
// The types of e (or &e, if isAddr) and the result are equal
// (modulo "untyped" bools resulting from comparisons).
//
// (Tip: to find the ssa.Value given a source position, use
// importer.PathEnclosingInterval to locate the ast.Node, then
// EnclosingFunction to locate the Function, then ValueForExpr to find
// the ssa.Value.)
//
func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
if f.debugInfo() { // (opt)
e = unparen(e)
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if ref, ok := instr.(*DebugRef); ok {
if ref.Expr == e {
return ref.X, ref.IsAddr
}
}
}
}
}
return
}
// --- Lookup functions for source-level named entities (types.Objects) ---
// Package returns the SSA Package corresponding to the specified
// type-checker package object.
// It returns nil if no such SSA package has been created.
//
func (prog *Program) Package(obj *types.Package) *Package {
return prog.packages[obj]
}
// packageLevelValue returns the package-level value corresponding to
// the specified named object, which may be a package-level const
// (*Const), var (*Global) or func (*Function) of some package in
// prog. It returns nil if the object is not found.
//
func (prog *Program) packageLevelValue(obj types.Object) Value {
if pkg, ok := prog.packages[obj.Pkg()]; ok {
return pkg.values[obj]
}
return nil
}
// FuncValue returns the concrete Function denoted by the source-level
// named function obj, or nil if obj denotes an interface method.
//
// TODO(adonovan): check the invariant that obj.Type() matches the
// result's Signature, both in the params/results and in the receiver.
//
func (prog *Program) FuncValue(obj *types.Func) *Function {
fn, _ := prog.packageLevelValue(obj).(*Function)
return fn
}
// ConstValue returns the SSA Value denoted by the source-level named
// constant obj.
//
func (prog *Program) ConstValue(obj *types.Const) *Const {
// TODO(adonovan): opt: share (don't reallocate)
// Consts for const objects and constant ast.Exprs.
// Universal constant? {true,false,nil}
if obj.Parent() == types.Universe {
return NewConst(obj.Val(), obj.Type())
}
// Package-level named constant?
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Const)
}
return NewConst(obj.Val(), obj.Type())
}
// VarValue returns the SSA Value that corresponds to a specific
// identifier denoting the source-level named variable obj.
//
// VarValue returns nil if a local variable was not found, perhaps
// because its package was not built, the debug information was not
// requested during SSA construction, or the value was optimized away.
//
// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
// and that ident must resolve to obj.
//
// pkg is the package enclosing the reference. (A reference to a var
// always occurs within a function, so we need to know where to find it.)
//
// If the identifier is a field selector and its base expression is
// non-addressable, then VarValue returns the value of that field.
// For example:
// func f() struct {x int}
// f().x // VarValue(x) returns a *Field instruction of type int
//
// All other identifiers denote addressable locations (variables).
// For them, VarValue may return either the variable's address or its
// value, even when the expression is evaluated only for its value; the
// situation is reported by isAddr, the second component of the result.
//
// If !isAddr, the returned value is the one associated with the
// specific identifier. For example,
// var x int // VarValue(x) returns Const 0 here
// x = 1 // VarValue(x) returns Const 1 here
//
// It is not specified whether the value or the address is returned in
// any particular case, as it may depend upon optimizations performed
// during SSA code generation, such as registerization, constant
// folding, avoidance of materialization of subexpressions, etc.
//
func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
// All references to a var are local to some function, possibly init.
fn := EnclosingFunction(pkg, ref)
if fn == nil {
return // e.g. def of struct field; SSA not built?
}
id := ref[0].(*ast.Ident)
// Defining ident of a parameter?
if id.Pos() == obj.Pos() {
for _, param := range fn.Params {
if param.Object() == obj {
return param, false
}
}
}
// Other ident?
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
if dr, ok := instr.(*DebugRef); ok {
if dr.Pos() == id.Pos() {
return dr.X, dr.IsAddr
}
}
}
}
// Defining ident of package-level var?
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Global), true
}
return // e.g. debug info not requested, or var optimized away
}

397
vendor/golang.org/x/tools/go/ssa/source_test.go generated vendored Normal file
View File

@@ -0,0 +1,397 @@
// 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"
exact "go/constant"
"go/parser"
"go/token"
"go/types"
"os"
"regexp"
"runtime"
"strings"
"testing"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
func TestObjValueLookup(t *testing.T) {
if runtime.GOOS == "android" {
t.Skipf("no testdata directory on %s", runtime.GOOS)
}
conf := loader.Config{ParserMode: parser.ParseComments}
f, err := conf.ParseFile("testdata/objlookup.go", nil)
if err != nil {
t.Error(err)
return
}
conf.CreateFromFiles("main", f)
// Maps each var Ident (represented "name:linenum") to the
// kind of ssa.Value we expect (represented "Constant", "&Alloc").
expectations := make(map[string]string)
// Find all annotations of form x::BinOp, &y::Alloc, etc.
re := regexp.MustCompile(`(\b|&)?(\w*)::(\w*)\b`)
for _, c := range f.Comments {
text := c.Text()
pos := conf.Fset.Position(c.Pos())
for _, m := range re.FindAllStringSubmatch(text, -1) {
key := fmt.Sprintf("%s:%d", m[2], pos.Line)
value := m[1] + m[3]
expectations[key] = value
}
}
iprog, err := conf.Load()
if err != nil {
t.Error(err)
return
}
prog := ssautil.CreateProgram(iprog, 0 /*|ssa.PrintFunctions*/)
mainInfo := iprog.Created[0]
mainPkg := prog.Package(mainInfo.Pkg)
mainPkg.SetDebugMode(true)
mainPkg.Build()
var varIds []*ast.Ident
var varObjs []*types.Var
for id, obj := range mainInfo.Defs {
// Check invariants for func and const objects.
switch obj := obj.(type) {
case *types.Func:
checkFuncValue(t, prog, obj)
case *types.Const:
checkConstValue(t, prog, obj)
case *types.Var:
if id.Name == "_" {
continue
}
varIds = append(varIds, id)
varObjs = append(varObjs, obj)
}
}
for id, obj := range mainInfo.Uses {
if obj, ok := obj.(*types.Var); ok {
varIds = append(varIds, id)
varObjs = append(varObjs, obj)
}
}
// Check invariants for var objects.
// The result varies based on the specific Ident.
for i, id := range varIds {
obj := varObjs[i]
ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())
pos := prog.Fset.Position(id.Pos())
exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
if exp == "" {
t.Errorf("%s: no expectation for var ident %s ", pos, id.Name)
continue
}
wantAddr := false
if exp[0] == '&' {
wantAddr = true
exp = exp[1:]
}
checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)
}
}
func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) {
fn := prog.FuncValue(obj)
// fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging
if fn == nil {
if obj.Name() != "interfaceMethod" {
t.Errorf("FuncValue(%s) == nil", obj)
}
return
}
if fnobj := fn.Object(); fnobj != obj {
t.Errorf("FuncValue(%s).Object() == %s; value was %s",
obj, fnobj, fn.Name())
return
}
if !types.Identical(fn.Type(), obj.Type()) {
t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
return
}
}
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
c := prog.ConstValue(obj)
// fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
if c == nil {
t.Errorf("ConstValue(%s) == nil", obj)
return
}
if !types.Identical(c.Type(), obj.Type()) {
t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
return
}
if obj.Name() != "nil" {
if !exact.Compare(c.Value, token.EQL, obj.Val()) {
t.Errorf("ConstValue(%s).Value (%s) != %s",
obj, c.Value, obj.Val())
return
}
}
}
func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) {
// The prefix of all assertions messages.
prefix := fmt.Sprintf("VarValue(%s @ L%d)",
obj, prog.Fset.Position(ref[0].Pos()).Line)
v, gotAddr := prog.VarValue(obj, pkg, ref)
// Kind is the concrete type of the ssa Value.
gotKind := "nil"
if v != nil {
gotKind = fmt.Sprintf("%T", v)[len("*ssa."):]
}
// fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging
// Check the kinds match.
// "nil" indicates expected failure (e.g. optimized away).
if expKind != gotKind {
t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind)
}
// Check the types match.
// If wantAddr, the expected type is the object's address.
if v != nil {
expType := obj.Type()
if wantAddr {
expType = types.NewPointer(expType)
if !gotAddr {
t.Errorf("%s: got value, want address", prefix)
}
} else if gotAddr {
t.Errorf("%s: got address, want value", prefix)
}
if !types.Identical(v.Type(), expType) {
t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
}
}
}
// Ensure that, in debug mode, we can determine the ssa.Value
// corresponding to every ast.Expr.
func TestValueForExpr(t *testing.T) {
testValueForExpr(t, "testdata/valueforexpr.go")
}
func testValueForExpr(t *testing.T, testfile string) {
if runtime.GOOS == "android" {
t.Skipf("no testdata dir on %s", runtime.GOOS)
}
conf := loader.Config{ParserMode: parser.ParseComments}
f, err := conf.ParseFile(testfile, nil)
if err != nil {
t.Error(err)
return
}
conf.CreateFromFiles("main", f)
iprog, err := conf.Load()
if err != nil {
t.Error(err)
return
}
mainInfo := iprog.Created[0]
prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(mainInfo.Pkg)
mainPkg.SetDebugMode(true)
mainPkg.Build()
if false {
// debugging
for _, mem := range mainPkg.Members {
if fn, ok := mem.(*ssa.Function); ok {
fn.WriteTo(os.Stderr)
}
}
}
// Find the actual AST node for each canonical position.
parenExprByPos := make(map[token.Pos]*ast.ParenExpr)
ast.Inspect(f, func(n ast.Node) bool {
if n != nil {
if e, ok := n.(*ast.ParenExpr); ok {
parenExprByPos[e.Pos()] = e
}
}
return true
})
// Find all annotations of form /*@kind*/.
for _, c := range f.Comments {
text := strings.TrimSpace(c.Text())
if text == "" || text[0] != '@' {
continue
}
text = text[1:]
pos := c.End() + 1
position := prog.Fset.Position(pos)
var e ast.Expr
if target := parenExprByPos[pos]; target == nil {
t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text)
continue
} else {
e = target.X
}
path, _ := astutil.PathEnclosingInterval(f, pos, pos)
if path == nil {
t.Errorf("%s: can't find AST path from root to comment: %s", position, text)
continue
}
fn := ssa.EnclosingFunction(mainPkg, path)
if fn == nil {
t.Errorf("%s: can't find enclosing function", position)
continue
}
v, gotAddr := fn.ValueForExpr(e) // (may be nil)
got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")
if want := text; got != want {
t.Errorf("%s: got value %q, want %q", position, got, want)
}
if v != nil {
T := v.Type()
if gotAddr {
T = T.Underlying().(*types.Pointer).Elem() // deref
}
if !types.Identical(T, mainInfo.TypeOf(e)) {
t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
}
}
}
}
// findInterval parses input and returns the [start, end) positions of
// the first occurrence of substr in input. f==nil indicates failure;
// an error has already been reported in that case.
//
func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
f, err := parser.ParseFile(fset, "<input>", input, 0)
if err != nil {
t.Errorf("parse error: %s", err)
return
}
i := strings.Index(input, substr)
if i < 0 {
t.Errorf("%q is not a substring of input", substr)
f = nil
return
}
filePos := fset.File(f.Package)
return f, filePos.Pos(i), filePos.Pos(i + len(substr))
}
func TestEnclosingFunction(t *testing.T) {
tests := []struct {
input string // the input file
substr string // first occurrence of this string denotes interval
fn string // name of expected containing function
}{
// We use distinctive numbers as syntactic landmarks.
// Ordinary function:
{`package main
func f() { println(1003) }`,
"100", "main.f"},
// Methods:
{`package main
type T int
func (t T) f() { println(200) }`,
"200", "(main.T).f"},
// Function literal:
{`package main
func f() { println(func() { print(300) }) }`,
"300", "main.f$1"},
// Doubly nested
{`package main
func f() { println(func() { print(func() { print(350) })})}`,
"350", "main.f$1$1"},
// Implicit init for package-level var initializer.
{"package main; var a = 400", "400", "main.init"},
// No code for constants:
{"package main; const a = 500", "500", "(none)"},
// Explicit init()
{"package main; func init() { println(600) }", "600", "main.init#1"},
// Multiple explicit init functions:
{`package main
func init() { println("foo") }
func init() { println(800) }`,
"800", "main.init#2"},
// init() containing FuncLit.
{`package main
func init() { println(func(){print(900)}) }`,
"900", "main.init#1$1"},
}
for _, test := range tests {
conf := loader.Config{Fset: token.NewFileSet()}
f, start, end := findInterval(t, conf.Fset, test.input, test.substr)
if f == nil {
continue
}
path, exact := astutil.PathEnclosingInterval(f, start, end)
if !exact {
t.Errorf("EnclosingFunction(%q) not exact", test.substr)
continue
}
conf.CreateFromFiles("main", f)
iprog, err := conf.Load()
if err != nil {
t.Error(err)
continue
}
prog := ssautil.CreateProgram(iprog, 0)
pkg := prog.Package(iprog.Created[0].Pkg)
pkg.Build()
name := "(none)"
fn := ssa.EnclosingFunction(pkg, path)
if fn != nil {
name = fn.String()
}
if name != test.fn {
t.Errorf("EnclosingFunction(%q in %q) got %s, want %s",
test.substr, test.input, name, test.fn)
continue
}
// While we're here: test HasEnclosingFunction.
if has := ssa.HasEnclosingFunction(pkg, path); has != (fn != nil) {
t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v",
test.substr, test.input, has, fn != nil)
continue
}
}
}

1696
vendor/golang.org/x/tools/go/ssa/ssa.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

95
vendor/golang.org/x/tools/go/ssa/ssautil/load.go generated vendored Normal file
View File

@@ -0,0 +1,95 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssautil
// This file defines utility functions for constructing programs in SSA form.
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
)
// CreateProgram returns a new program in SSA form, given a program
// loaded from source. An SSA package is created for each transitively
// error-free package of lprog.
//
// Code for bodies of functions is not built until Build is called
// on the result.
//
// mode controls diagnostics and checking during SSA construction.
//
func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
prog := ssa.NewProgram(lprog.Fset, mode)
for _, info := range lprog.AllPackages {
if info.TransitivelyErrorFree {
prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
}
}
return prog
}
// BuildPackage builds an SSA program with IR for a single package.
//
// It populates pkg by type-checking the specified file ASTs. All
// dependencies are loaded using the importer specified by tc, which
// typically loads compiler export data; SSA code cannot be built for
// those packages. BuildPackage then constructs an ssa.Program with all
// dependency packages created, and builds and returns the SSA package
// corresponding to pkg.
//
// The caller must have set pkg.Path() to the import path.
//
// The operation fails if there were any type-checking or import errors.
//
// See ../ssa/example_test.go for an example.
//
func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
if fset == nil {
panic("no token.FileSet")
}
if pkg.Path() == "" {
panic("package has no import path")
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
return nil, nil, err
}
prog := ssa.NewProgram(fset, mode)
// Create SSA packages for all imports.
// Order is not significant.
created := make(map[*types.Package]bool)
var createAll func(pkgs []*types.Package)
createAll = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !created[p] {
created[p] = true
prog.CreatePackage(p, nil, nil, true)
createAll(p.Imports())
}
}
}
createAll(pkg.Imports())
// Create and build the primary package.
ssapkg := prog.CreatePackage(pkg, files, info, false)
ssapkg.Build()
return ssapkg, info, nil
}

64
vendor/golang.org/x/tools/go/ssa/ssautil/load_test.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssautil_test
import (
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"os"
"testing"
"golang.org/x/tools/go/ssa/ssautil"
)
const hello = `package main
import "fmt"
func main() {
fmt.Println("Hello, world")
}
`
func TestBuildPackage(t *testing.T) {
// There is a more substantial test of BuildPackage and the
// SSA program it builds in ../ssa/builder_test.go.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "hello.go", hello, 0)
if err != nil {
t.Fatal(err)
}
pkg := types.NewPackage("hello", "")
ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, pkg, []*ast.File{f}, 0)
if err != nil {
t.Fatal(err)
}
if pkg.Name() != "main" {
t.Errorf("pkg.Name() = %s, want main", pkg.Name())
}
if ssapkg.Func("main") == nil {
ssapkg.WriteTo(os.Stderr)
t.Errorf("ssapkg has no main function")
}
}
func TestBuildPackage_MissingImport(t *testing.T) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "bad.go", `package bad; import "missing"`, 0)
if err != nil {
t.Fatal(err)
}
pkg := types.NewPackage("bad", "")
ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
if err == nil || ssapkg != nil {
t.Fatal("BuildPackage succeeded unexpectedly")
}
}

234
vendor/golang.org/x/tools/go/ssa/ssautil/switch.go generated vendored Normal file
View File

@@ -0,0 +1,234 @@
// 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 ssautil
// This file implements discovery of switch and type-switch constructs
// from low-level control flow.
//
// Many techniques exist for compiling a high-level switch with
// constant cases to efficient machine code. The optimal choice will
// depend on the data type, the specific case values, the code in the
// body of each case, and the hardware.
// Some examples:
// - a lookup table (for a switch that maps constants to constants)
// - a computed goto
// - a binary tree
// - a perfect hash
// - a two-level switch (to partition constant strings by their first byte).
import (
"bytes"
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/go/ssa"
)
// A ConstCase represents a single constant comparison.
// It is part of a Switch.
type ConstCase struct {
Block *ssa.BasicBlock // block performing the comparison
Body *ssa.BasicBlock // body of the case
Value *ssa.Const // case comparand
}
// A TypeCase represents a single type assertion.
// It is part of a Switch.
type TypeCase struct {
Block *ssa.BasicBlock // block performing the type assert
Body *ssa.BasicBlock // body of the case
Type types.Type // case type
Binding ssa.Value // value bound by this case
}
// A Switch is a logical high-level control flow operation
// (a multiway branch) discovered by analysis of a CFG containing
// only if/else chains. It is not part of the ssa.Instruction set.
//
// One of ConstCases and TypeCases has length >= 2;
// the other is nil.
//
// In a value switch, the list of cases may contain duplicate constants.
// A type switch may contain duplicate types, or types assignable
// to an interface type also in the list.
// TODO(adonovan): eliminate such duplicates.
//
type Switch struct {
Start *ssa.BasicBlock // block containing start of if/else chain
X ssa.Value // the switch operand
ConstCases []ConstCase // ordered list of constant comparisons
TypeCases []TypeCase // ordered list of type assertions
Default *ssa.BasicBlock // successor if all comparisons fail
}
func (sw *Switch) String() string {
// We represent each block by the String() of its
// first Instruction, e.g. "print(42:int)".
var buf bytes.Buffer
if sw.ConstCases != nil {
fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
for _, c := range sw.ConstCases {
fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0])
}
} else {
fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
for _, c := range sw.TypeCases {
fmt.Fprintf(&buf, "case %s %s: %s\n",
c.Binding.Name(), c.Type, c.Body.Instrs[0])
}
}
if sw.Default != nil {
fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0])
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// Switches examines the control-flow graph of fn and returns the
// set of inferred value and type switches. A value switch tests an
// ssa.Value for equality against two or more compile-time constant
// values. Switches involving link-time constants (addresses) are
// ignored. A type switch type-asserts an ssa.Value against two or
// more types.
//
// The switches are returned in dominance order.
//
// The resulting switches do not necessarily correspond to uses of the
// 'switch' keyword in the source: for example, a single source-level
// switch statement with non-constant cases may result in zero, one or
// many Switches, one per plural sequence of constant cases.
// Switches may even be inferred from if/else- or goto-based control flow.
// (In general, the control flow constructs of the source program
// cannot be faithfully reproduced from the SSA representation.)
//
func Switches(fn *ssa.Function) []Switch {
// Traverse the CFG in dominance order, so we don't
// enter an if/else-chain in the middle.
var switches []Switch
seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
for _, b := range fn.DomPreorder() {
if x, k := isComparisonBlock(b); x != nil {
// Block b starts a switch.
sw := Switch{Start: b, X: x}
valueSwitch(&sw, k, seen)
if len(sw.ConstCases) > 1 {
switches = append(switches, sw)
}
}
if y, x, T := isTypeAssertBlock(b); y != nil {
// Block b starts a type switch.
sw := Switch{Start: b, X: x}
typeSwitch(&sw, y, T, seen)
if len(sw.TypeCases) > 1 {
switches = append(switches, sw)
}
}
}
return switches
}
func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.ConstCases = append(sw.ConstCases, ConstCase{
Block: b,
Body: b.Succs[0],
Value: k,
})
b = b.Succs[1]
if len(b.Instrs) > 2 {
// Block b contains not just 'if x == k',
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
x, k = isComparisonBlock(b)
}
sw.Default = b
}
func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.TypeCases = append(sw.TypeCases, TypeCase{
Block: b,
Body: b.Succs[0],
Type: T,
Binding: y,
})
b = b.Succs[1]
if len(b.Instrs) > 4 {
// Block b contains not just
// {TypeAssert; Extract #0; Extract #1; If}
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
y, x, T = isTypeAssertBlock(b)
}
sw.Default = b
}
// isComparisonBlock returns the operands (v, k) if a block ends with
// a comparison v==k, where k is a compile-time constant.
//
func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) {
if n := len(b.Instrs); n >= 2 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {
if k, ok := binop.Y.(*ssa.Const); ok {
return binop.X, k
}
if k, ok := binop.X.(*ssa.Const); ok {
return binop.Y, k
}
}
}
}
return
}
// isTypeAssertBlock returns the operands (y, x, T) if a block ends with
// a type assertion "if y, ok := x.(T); ok {".
//
func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) {
if n := len(b.Instrs); n >= 4 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 {
if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b {
// hack: relies upon instruction ordering.
if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok {
return ext0, ta.X, ta.AssertedType
}
}
}
}
}
return
}

View File

@@ -0,0 +1,74 @@
// 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.
// No testdata on Android.
// +build !android
package ssautil_test
import (
"go/parser"
"strings"
"testing"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
func TestSwitches(t *testing.T) {
conf := loader.Config{ParserMode: parser.ParseComments}
f, err := conf.ParseFile("testdata/switches.go", nil)
if err != nil {
t.Error(err)
return
}
conf.CreateFromFiles("main", f)
iprog, err := conf.Load()
if err != nil {
t.Error(err)
return
}
prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
mainPkg.Build()
for _, mem := range mainPkg.Members {
if fn, ok := mem.(*ssa.Function); ok {
if fn.Synthetic != "" {
continue // e.g. init()
}
// Each (multi-line) "switch" comment within
// this function must match the printed form
// of a ConstSwitch.
var wantSwitches []string
for _, c := range f.Comments {
if fn.Syntax().Pos() <= c.Pos() && c.Pos() < fn.Syntax().End() {
text := strings.TrimSpace(c.Text())
if strings.HasPrefix(text, "switch ") {
wantSwitches = append(wantSwitches, text)
}
}
}
switches := ssautil.Switches(fn)
if len(switches) != len(wantSwitches) {
t.Errorf("in %s, found %d switches, want %d", fn, len(switches), len(wantSwitches))
}
for i, sw := range switches {
got := sw.String()
if i >= len(wantSwitches) {
continue
}
want := wantSwitches[i]
if got != want {
t.Errorf("in %s, found switch %d: got <<%s>>, want <<%s>>", fn, i, got, want)
}
}
}
}
}

View File

@@ -0,0 +1,357 @@
// +build ignore
package main
// This file is the input to TestSwitches in switch_test.go.
// Each multiway conditional with constant or type cases (Switch)
// discovered by Switches is printed, and compared with the
// comments.
//
// The body of each case is printed as the value of its first
// instruction.
// -------- Value switches --------
func SimpleSwitch(x, y int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: print(23:int)
// case 3:int: print(23:int)
// case 4:int: print(3:int)
// default: x == y
// }
switch x {
case 1:
print(1)
case 2, 3:
print(23)
fallthrough
case 4:
print(3)
default:
print(4)
case y:
print(5)
}
print(6)
}
func four() int { return 4 }
// A non-constant case makes a switch "impure", but its pure
// cases form two separate switches.
func SwitchWithNonConstantCase(x int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: print(23:int)
// case 3:int: print(23:int)
// default: four()
// }
// switch x {
// case 5:int: print(5:int)
// case 6:int: print(6:int)
// default: print("done":string)
// }
switch x {
case 1:
print(1)
case 2, 3:
print(23)
case four():
print(3)
case 5:
print(5)
case 6:
print(6)
}
print("done")
}
// Switches may be found even where the source
// program doesn't have a switch statement.
func ImplicitSwitches(x, y int) {
// switch x {
// case 1:int: print(12:int)
// case 2:int: print(12:int)
// default: x < 5:int
// }
if x == 1 || 2 == x || x < 5 {
print(12)
}
// switch x {
// case 3:int: print(34:int)
// case 4:int: print(34:int)
// default: x == y
// }
if x == 3 || 4 == x || x == y {
print(34)
}
// Not a switch: no consistent variable.
if x == 5 || y == 6 {
print(56)
}
// Not a switch: only one constant comparison.
if x == 7 || x == y {
print(78)
}
}
func IfElseBasedSwitch(x int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: print(2:int)
// default: print("else":string)
// }
if x == 1 {
print(1)
} else if x == 2 {
print(2)
} else {
print("else")
}
}
func GotoBasedSwitch(x int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: print(2:int)
// default: print("else":string)
// }
if x == 1 {
goto L1
}
if x == 2 {
goto L2
}
print("else")
L1:
print(1)
goto end
L2:
print(2)
end:
}
func SwitchInAForLoop(x int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: print(2:int)
// default: print("head":string)
// }
loop:
for {
print("head")
switch x {
case 1:
print(1)
break loop
case 2:
print(2)
break loop
}
}
}
// This case is a switch in a for-loop, both constructed using goto.
// As before, the default case points back to the block containing the
// switch, but that's ok.
func SwitchInAForLoopUsingGoto(x int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: print(2:int)
// default: print("head":string)
// }
loop:
print("head")
if x == 1 {
goto L1
}
if x == 2 {
goto L2
}
goto loop
L1:
print(1)
goto end
L2:
print(2)
end:
}
func UnstructuredSwitchInAForLoop(x int) {
// switch x {
// case 1:int: print(1:int)
// case 2:int: x == 1:int
// default: print("end":string)
// }
for {
if x == 1 {
print(1)
return
}
if x == 2 {
continue
}
break
}
print("end")
}
func CaseWithMultiplePreds(x int) {
for {
if x == 1 {
print(1)
return
}
loop:
// This block has multiple predecessors,
// so can't be treated as a switch case.
if x == 2 {
goto loop
}
break
}
print("end")
}
func DuplicateConstantsAreNotEliminated(x int) {
// switch x {
// case 1:int: print(1:int)
// case 1:int: print("1a":string)
// case 2:int: print(2:int)
// default: return
// }
if x == 1 {
print(1)
} else if x == 1 { // duplicate => unreachable
print("1a")
} else if x == 2 {
print(2)
}
}
// Interface values (created by comparisons) are not constants,
// so ConstSwitch.X is never of interface type.
func MakeInterfaceIsNotAConstant(x interface{}) {
if x == "foo" {
print("foo")
} else if x == 1 {
print(1)
}
}
func ZeroInitializedVarsAreConstants(x int) {
// switch x {
// case 0:int: print(1:int)
// case 2:int: print(2:int)
// default: print("end":string)
// }
var zero int // SSA construction replaces zero with 0
if x == zero {
print(1)
} else if x == 2 {
print(2)
}
print("end")
}
// -------- Select --------
// NB, potentially fragile reliance on register number.
func SelectDesugarsToSwitch(ch chan int) {
// switch t1 {
// case 0:int: extract t0 #2
// case 1:int: println(0:int)
// case 2:int: println(1:int)
// default: println("default":string)
// }
select {
case x := <-ch:
println(x)
case <-ch:
println(0)
case ch <- 1:
println(1)
default:
println("default")
}
}
// NB, potentially fragile reliance on register number.
func NonblockingSelectDefaultCasePanics(ch chan int) {
// switch t1 {
// case 0:int: extract t0 #2
// case 1:int: println(0:int)
// case 2:int: println(1:int)
// default: make interface{} <- string ("blocking select m...":string)
// }
select {
case x := <-ch:
println(x)
case <-ch:
println(0)
case ch <- 1:
println(1)
}
}
// -------- Type switches --------
// NB, reliance on fragile register numbering.
func SimpleTypeSwitch(x interface{}) {
// switch x.(type) {
// case t3 int: println(x)
// case t7 bool: println(x)
// case t10 string: println(t10)
// default: println(x)
// }
switch y := x.(type) {
case nil:
println(y)
case int, bool:
println(y)
case string:
println(y)
default:
println(y)
}
}
// NB, potentially fragile reliance on register number.
func DuplicateTypesAreNotEliminated(x interface{}) {
// switch x.(type) {
// case t1 string: println(1:int)
// case t5 interface{}: println(t5)
// case t9 int: println(3:int)
// default: return
// }
switch y := x.(type) {
case string:
println(1)
case interface{}:
println(y)
case int:
println(3) // unreachable!
}
}
// NB, potentially fragile reliance on register number.
func AdHocTypeSwitch(x interface{}) {
// switch x.(type) {
// case t1 int: println(t1)
// case t5 string: println(t5)
// default: print("default":string)
// }
if i, ok := x.(int); ok {
println(i)
} else if s, ok := x.(string); ok {
println(s)
} else {
print("default")
}
}

79
vendor/golang.org/x/tools/go/ssa/ssautil/visit.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
// 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 ssautil // import "golang.org/x/tools/go/ssa/ssautil"
import "golang.org/x/tools/go/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 *ssa.Program) map[*ssa.Function]bool {
visit := visitor{
prog: prog,
seen: make(map[*ssa.Function]bool),
}
visit.program()
return visit.seen
}
type visitor struct {
prog *ssa.Program
seen map[*ssa.Function]bool
}
func (visit *visitor) program() {
for _, pkg := range visit.prog.AllPackages() {
for _, mem := range pkg.Members {
if fn, ok := mem.(*ssa.Function); ok {
visit.function(fn)
}
}
}
for _, T := range visit.prog.RuntimeTypes() {
mset := visit.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
visit.function(visit.prog.MethodValue(mset.At(i)))
}
}
}
func (visit *visitor) function(fn *ssa.Function) {
if !visit.seen[fn] {
visit.seen[fn] = true
var buf [10]*ssa.Value // avoid alloc in common case
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
for _, op := range instr.Operands(buf[:0]) {
if fn, ok := (*op).(*ssa.Function); ok {
visit.function(fn)
}
}
}
}
}
}
// MainPackages returns the subset of the specified packages
// named "main" that define a main function.
// The result may include synthetic "testmain" packages.
func MainPackages(pkgs []*ssa.Package) []*ssa.Package {
var mains []*ssa.Package
for _, pkg := range pkgs {
if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
mains = append(mains, pkg)
}
}
return mains
}

151
vendor/golang.org/x/tools/go/ssa/stdlib_test.go generated vendored Normal file
View File

@@ -0,0 +1,151 @@
// 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.
// Incomplete source tree on Android.
// +build !android
package ssa_test
// This file runs the SSA builder in sanity-checking mode on all
// packages beneath $GOROOT and prints some summary information.
//
// Run with "go test -cpu=8 to" set GOMAXPROCS.
import (
"go/ast"
"go/build"
"go/token"
"runtime"
"testing"
"time"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
// Skip the set of packages that transitively depend on
// cmd/internal/objfile, which uses vendoring,
// which go/loader does not yet support.
// TODO(adonovan): add support for vendoring and delete this.
var skip = map[string]bool{
"cmd/addr2line": true,
"cmd/internal/objfile": true,
"cmd/nm": true,
"cmd/objdump": true,
"cmd/pprof": true,
}
func bytesAllocated() uint64 {
runtime.GC()
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
return stats.Alloc
}
func TestStdlib(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode; too slow (golang.org/issue/14113)")
}
// Load, parse and type-check the program.
t0 := time.Now()
alloc0 := bytesAllocated()
// Load, parse and type-check the program.
ctxt := build.Default // copy
ctxt.GOPATH = "" // disable GOPATH
conf := loader.Config{Build: &ctxt}
for _, path := range buildutil.AllPackages(conf.Build) {
if skip[path] {
continue
}
conf.ImportWithTests(path)
}
iprog, err := conf.Load()
if err != nil {
t.Fatalf("Load failed: %v", err)
}
t1 := time.Now()
alloc1 := bytesAllocated()
// Create SSA packages.
var mode ssa.BuilderMode
// Comment out these lines during benchmarking. Approx SSA build costs are noted.
mode |= ssa.SanityCheckFunctions // + 2% space, + 4% time
mode |= ssa.GlobalDebug // +30% space, +18% time
prog := ssautil.CreateProgram(iprog, mode)
t2 := time.Now()
// Build SSA.
prog.Build()
t3 := time.Now()
alloc3 := bytesAllocated()
numPkgs := len(prog.AllPackages())
if want := 140; numPkgs < want {
t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
}
// Keep iprog reachable until after we've measured memory usage.
if len(iprog.AllPackages) == 0 {
panic("unreachable")
}
allFuncs := ssautil.AllFunctions(prog)
// Check that all non-synthetic functions have distinct names.
// Synthetic wrappers for exported methods should be distinct too,
// except for unexported ones (explained at (*Function).RelString).
byName := make(map[string]*ssa.Function)
for fn := range allFuncs {
if fn.Synthetic == "" || ast.IsExported(fn.Name()) {
str := fn.String()
prev := byName[str]
byName[str] = fn
if prev != nil {
t.Errorf("%s: duplicate function named %s",
prog.Fset.Position(fn.Pos()), str)
t.Errorf("%s: (previously defined here)",
prog.Fset.Position(prev.Pos()))
}
}
}
// Dump some statistics.
var numInstrs int
for fn := range allFuncs {
for _, b := range fn.Blocks {
numInstrs += len(b.Instrs)
}
}
// determine line count
var lineCount int
prog.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 AST+types: ", int64(alloc1-alloc0)/1e6)
t.Log("#MB SSA: ", int64(alloc3-alloc1)/1e6)
}

160
vendor/golang.org/x/tools/go/ssa/testdata/objlookup.go generated vendored Normal file
View File

@@ -0,0 +1,160 @@
//+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::FieldAddr
print(v7.x) // &v7::Alloc &x::FieldAddr
var v8 [1]int // &v8::Alloc
v8[0] = 0 // &v8::Alloc
print(v8[:]) // &v8::Alloc
_ = v8[0] // &v8::Alloc
_ = v8[:][0] // &v8::Alloc
v8ptr := &v8 // v8ptr::Alloc &v8::Alloc
_ = v8ptr[0] // v8ptr::Alloc
_ = *v8ptr // v8ptr::Alloc
v8a := make([]int, 1) // v8a::Slice
v8a[0] = 0 // v8a::Slice
print(v8a[:]) // v8a::Slice
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 // J::nil
// These vars are not optimised away.
if false {
v13 := 0 // v13::Const
println(v13) // v13::Const
}
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::Alloc &Op::FieldAddr
_ = &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
}

View File

@@ -0,0 +1,24 @@
//+build ignore
// This file is the input to TestValueForExprStructConv in identical_test.go,
// which uses the same framework as TestValueForExpr does in source_test.go.
//
// In Go 1.8, struct conversions are permitted even when the struct types have
// different tags. This wasn't permitted in earlier versions of Go, so this file
// exists separately from valueforexpr.go to just test this behavior in Go 1.8
// and later.
package main
type t1 struct {
x int
}
type t2 struct {
x int `tag`
}
func main() {
var tv1 t1
var tv2 t2 = /*@ChangeType*/ (t2(tv1))
_ = tv2
}

View File

@@ -0,0 +1,152 @@
//+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)
_ = /*@<nil>*/ (1 + 2) // (constant)
i := 0
f := func() (int, int) { return 0, 0 }
/*@Call*/ (print( /*@BinOp*/ (i + 1)))
_, _ = /*@Call*/ (f())
ch := /*@MakeChan*/ (make(chan int))
/*@UnOp*/ (<-ch)
x := /*@UnOp*/ (<-ch)
_ = 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
_ = /*@Slice*/ (make([]int, 0))
_ = /*@MakeClosure*/ (func() { print(spilled) })
sl := []int{}
_ = /*@Slice*/ (sl[:0])
_ = /*@<nil>*/ (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(& /*@Slice*/ ([]int{}))
sl1 := /*@Slice*/ ([]int{})
sl2 := /*@Alloc*/ (&[]int{})
sl3 := & /*@Slice*/ ([]int{})
_, _, _ = sl1, sl2, sl3
_ = /*@Slice*/ ([]int{})
_ = /*@<nil>*/ (& /*@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{}))
_ = & /*@Alloc*/ ([1]int{})
// 3. Maps
type M map[int]int
print( /*@MakeMap*/ (M{}))
print( /*@Alloc*/ (&M{}))
print(& /*@MakeMap*/ (M{}))
m1 := /*@MakeMap*/ (M{})
m2 := /*@Alloc*/ (&M{})
m3 := & /*@MakeMap*/ (M{})
_, _, _ = m1, m2, m3
_ = /*@MakeMap*/ (M{})
_ = /*@<nil>*/ (& /*@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))

267
vendor/golang.org/x/tools/go/ssa/testmain.go generated vendored Normal file
View File

@@ -0,0 +1,267 @@
// 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 $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing.
//
// TODO(adonovan): this file no longer needs to live in the ssa package.
// Move it to ssautil.
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/types"
"log"
"os"
"strings"
"text/template"
)
// FindTests returns the Test, Benchmark, and Example functions
// (as defined by "go test") defined in the specified package,
// and its TestMain function, if any.
func FindTests(pkg *Package) (tests, benchmarks, examples []*Function, main *Function) {
prog := pkg.Prog
// The first two of these may be nil: if the program doesn't import "testing",
// it can't contain any tests, but it may yet contain Examples.
var testSig *types.Signature // func(*testing.T)
var benchmarkSig *types.Signature // func(*testing.B)
var exampleSig = types.NewSignature(nil, nil, nil, false) // func()
// Obtain the types from the parameters of testing.MainStart.
if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
mainStart := testingPkg.Func("MainStart")
params := mainStart.Signature.Params()
testSig = funcField(params.At(1).Type())
benchmarkSig = funcField(params.At(2).Type())
// Does the package define this function?
// func TestMain(*testing.M)
if f := pkg.Func("TestMain"); f != nil {
sig := f.Type().(*types.Signature)
starM := mainStart.Signature.Results().At(0).Type() // *testing.M
if sig.Results().Len() == 0 &&
sig.Params().Len() == 1 &&
types.Identical(sig.Params().At(0).Type(), starM) {
main = f
}
}
}
// TODO(adonovan): use a stable order, e.g. lexical.
for _, mem := range pkg.Members {
if f, ok := mem.(*Function); ok &&
ast.IsExported(f.Name()) &&
strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
switch {
case testSig != nil && isTestSig(f, "Test", testSig):
tests = append(tests, f)
case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig):
benchmarks = append(benchmarks, f)
case isTestSig(f, "Example", exampleSig):
examples = append(examples, f)
default:
continue
}
}
}
return
}
// Like isTest, but checks the signature too.
func isTestSig(f *Function, prefix string, sig *types.Signature) bool {
return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig)
}
// Given the type of one of the three slice parameters of testing.Main,
// returns the function type.
func funcField(slice types.Type) *types.Signature {
return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature)
}
// 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.
// Plundered from $GOROOT/src/cmd/go/test.go
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):])
}
// CreateTestMainPackage creates and returns a synthetic "testmain"
// package for the specified package if it defines tests, benchmarks or
// executable examples, or nil otherwise. The new package is named
// "main" and provides a function named "main" that runs the tests,
// similar to the one that would be created by the 'go test' tool.
//
// Subsequent calls to prog.AllPackages include the new package.
// The package pkg must belong to the program prog.
func (prog *Program) CreateTestMainPackage(pkg *Package) *Package {
if pkg.Prog != prog {
log.Fatal("Package does not belong to Program")
}
// Template data
var data struct {
Pkg *Package
Tests, Benchmarks, Examples []*Function
Main *Function
Go18 bool
}
data.Pkg = pkg
// Enumerate tests.
data.Tests, data.Benchmarks, data.Examples, data.Main = FindTests(pkg)
if data.Main == nil &&
data.Tests == nil && data.Benchmarks == nil && data.Examples == nil {
return nil
}
// Synthesize source for testmain package.
path := pkg.Pkg.Path() + "$testmain"
tmpl := testmainTmpl
if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
// In Go 1.8, testing.MainStart's first argument is an interface, not a func.
data.Go18 = types.IsInterface(testingPkg.Func("MainStart").Signature.Params().At(0).Type())
} else {
// The program does not import "testing", but FindTests
// returned non-nil, which must mean there were Examples
// but no Test, Benchmark, or TestMain functions.
// We'll simply call them from testmain.main; this will
// ensure they don't panic, but will not check any
// "Output:" comments.
// (We should not execute an Example that has no
// "Output:" comment, but it's impossible to tell here.)
tmpl = examplesOnlyTmpl
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
log.Fatalf("internal error expanding template for %s: %v", path, err)
}
if false { // debugging
fmt.Fprintln(os.Stderr, buf.String())
}
// Parse and type-check the testmain package.
f, err := parser.ParseFile(prog.Fset, path+".go", &buf, parser.Mode(0))
if err != nil {
log.Fatalf("internal error parsing %s: %v", path, err)
}
conf := types.Config{
DisableUnusedImportCheck: true,
Importer: importer{pkg},
}
files := []*ast.File{f}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
testmainPkg, err := conf.Check(path, prog.Fset, files, info)
if err != nil {
log.Fatalf("internal error type-checking %s: %v", path, err)
}
// Create and build SSA code.
testmain := prog.CreatePackage(testmainPkg, files, info, false)
testmain.SetDebugMode(false)
testmain.Build()
testmain.Func("main").Synthetic = "test main function"
testmain.Func("init").Synthetic = "package initializer"
return testmain
}
// An implementation of types.Importer for an already loaded SSA program.
type importer struct {
pkg *Package // package under test; may be non-importable
}
func (imp importer) Import(path string) (*types.Package, error) {
if p := imp.pkg.Prog.ImportedPackage(path); p != nil {
return p.Pkg, nil
}
if path == imp.pkg.Pkg.Path() {
return imp.pkg.Pkg, nil
}
return nil, fmt.Errorf("not found") // can't happen
}
var testmainTmpl = template.Must(template.New("testmain").Parse(`
package main
import "io"
import "os"
import "testing"
import p {{printf "%q" .Pkg.Pkg.Path}}
{{if .Go18}}
type deps struct{}
func (deps) ImportPath() string { return "" }
func (deps) MatchString(pat, str string) (bool, error) { return true, nil }
func (deps) StartCPUProfile(io.Writer) error { return nil }
func (deps) StartTestLog(io.Writer) {}
func (deps) StopCPUProfile() {}
func (deps) StopTestLog() error { return nil }
func (deps) WriteHeapProfile(io.Writer) error { return nil }
func (deps) WriteProfileTo(string, io.Writer, int) error { return nil }
var match deps
{{else}}
func match(_, _ string) (bool, error) { return true, nil }
{{end}}
func main() {
tests := []testing.InternalTest{
{{range .Tests}}
{ {{printf "%q" .Name}}, p.{{.Name}} },
{{end}}
}
benchmarks := []testing.InternalBenchmark{
{{range .Benchmarks}}
{ {{printf "%q" .Name}}, p.{{.Name}} },
{{end}}
}
examples := []testing.InternalExample{
{{range .Examples}}
{Name: {{printf "%q" .Name}}, F: p.{{.Name}}},
{{end}}
}
m := testing.MainStart(match, tests, benchmarks, examples)
{{with .Main}}
p.{{.Name}}(m)
{{else}}
os.Exit(m.Run())
{{end}}
}
`))
var examplesOnlyTmpl = template.Must(template.New("examples").Parse(`
package main
import p {{printf "%q" .Pkg.Pkg.Path}}
func main() {
{{range .Examples}}
p.{{.Name}}()
{{end}}
}
`))

124
vendor/golang.org/x/tools/go/ssa/testmain_test.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa_test
// Tests of FindTests. CreateTestMainPackage is tested via the interpreter.
// TODO(adonovan): test the 'pkgs' result from FindTests.
import (
"fmt"
"sort"
"testing"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
func create(t *testing.T, content string) *ssa.Package {
var conf loader.Config
f, err := conf.ParseFile("foo_test.go", content)
if err != nil {
t.Fatal(err)
}
conf.CreateFromFiles("foo", f)
lprog, err := conf.Load()
if err != nil {
t.Fatal(err)
}
// We needn't call Build.
foo := lprog.Package("foo").Pkg
return ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions).Package(foo)
}
func TestFindTests(t *testing.T) {
test := `
package foo
import "testing"
type T int
// Tests:
func Test(t *testing.T) {}
func TestA(t *testing.T) {}
func TestB(t *testing.T) {}
// Not tests:
func testC(t *testing.T) {}
func TestD() {}
func testE(t *testing.T) int { return 0 }
func (T) Test(t *testing.T) {}
// Benchmarks:
func Benchmark(*testing.B) {}
func BenchmarkA(b *testing.B) {}
func BenchmarkB(*testing.B) {}
// Not benchmarks:
func benchmarkC(t *testing.T) {}
func BenchmarkD() {}
func benchmarkE(t *testing.T) int { return 0 }
func (T) Benchmark(t *testing.T) {}
// Examples:
func Example() {}
func ExampleA() {}
// Not examples:
func exampleC() {}
func ExampleD(t *testing.T) {}
func exampleE() int { return 0 }
func (T) Example() {}
`
pkg := create(t, test)
tests, benchmarks, examples, _ := ssa.FindTests(pkg)
sort.Sort(funcsByPos(tests))
if got, want := fmt.Sprint(tests), "[foo.Test foo.TestA foo.TestB]"; got != want {
t.Errorf("FindTests.tests = %s, want %s", got, want)
}
sort.Sort(funcsByPos(benchmarks))
if got, want := fmt.Sprint(benchmarks), "[foo.Benchmark foo.BenchmarkA foo.BenchmarkB]"; got != want {
t.Errorf("FindTests.benchmarks = %s, want %s", got, want)
}
sort.Sort(funcsByPos(examples))
if got, want := fmt.Sprint(examples), "[foo.Example foo.ExampleA]"; got != want {
t.Errorf("FindTests examples = %s, want %s", got, want)
}
}
func TestFindTestsTesting(t *testing.T) {
test := `
package foo
// foo does not import "testing", but defines Examples.
func Example() {}
func ExampleA() {}
`
pkg := create(t, test)
tests, benchmarks, examples, _ := ssa.FindTests(pkg)
if len(tests) > 0 {
t.Errorf("FindTests.tests = %s, want none", tests)
}
if len(benchmarks) > 0 {
t.Errorf("FindTests.benchmarks = %s, want none", benchmarks)
}
sort.Sort(funcsByPos(examples))
if got, want := fmt.Sprint(examples), "[foo.Example foo.ExampleA]"; got != want {
t.Errorf("FindTests examples = %s, want %s", got, want)
}
}
type funcsByPos []*ssa.Function
func (p funcsByPos) Len() int { return len(p) }
func (p funcsByPos) Less(i, j int) bool { return p[i].Pos() < p[j].Pos() }
func (p funcsByPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

119
vendor/golang.org/x/tools/go/ssa/util.go generated vendored Normal file
View File

@@ -0,0 +1,119 @@
// 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"
"go/token"
"go/types"
"io"
"os"
"golang.org/x/tools/go/ast/astutil"
)
//// AST utilities
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
// isBlankIdent returns true iff e is an Ident with name "_".
// They have no associated types.Object, and thus no type.
//
func isBlankIdent(e ast.Expr) bool {
id, ok := e.(*ast.Ident)
return ok && id.Name == "_"
}
//// Type utilities. Some of these belong in go/types.
// isPointer returns true for types whose underlying type is a pointer.
func isPointer(typ types.Type) bool {
_, ok := typ.Underlying().(*types.Pointer)
return ok
}
func isInterface(T types.Type) bool { return types.IsInterface(T) }
// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}
// recvType returns the receiver type of method obj.
func recvType(obj *types.Func) types.Type {
return obj.Type().(*types.Signature).Recv().Type()
}
// DefaultType returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
//
// Exported to ssa/interp.
//
// TODO(adonovan): use go/types.DefaultType after 1.8.
//
func DefaultType(typ types.Type) types.Type {
if t, ok := typ.(*types.Basic); ok {
k := t.Kind()
switch k {
case types.UntypedBool:
k = types.Bool
case types.UntypedInt:
k = types.Int
case types.UntypedRune:
k = types.Rune
case types.UntypedFloat:
k = types.Float64
case types.UntypedComplex:
k = types.Complex128
case types.UntypedString:
k = types.String
}
typ = types.Typ[k]
}
return typ
}
// logStack prints the formatted "start" message to stderr and
// returns a closure that prints the corresponding "end" message.
// Call using 'defer logStack(...)()' to show builder stack on panic.
// Don't forget trailing parens!
//
func logStack(format string, args ...interface{}) func() {
msg := fmt.Sprintf(format, args...)
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, "\n")
return func() {
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, " end\n")
}
}
// newVar creates a 'var' for use in a types.Tuple.
func newVar(name string, typ types.Type) *types.Var {
return types.NewParam(token.NoPos, nil, name, typ)
}
// anonVar creates an anonymous 'var' for use in a types.Tuple.
func anonVar(typ types.Type) *types.Var {
return newVar("", typ)
}
var lenResults = types.NewTuple(anonVar(tInt))
// makeLen returns the len builtin specialized to type func(T)int.
func makeLen(T types.Type) *Builtin {
lenParams := types.NewTuple(anonVar(T))
return &Builtin{
name: "len",
sig: types.NewSignature(nil, lenParams, lenResults, false),
}
}

294
vendor/golang.org/x/tools/go/ssa/wrappers.go generated vendored Normal file
View File

@@ -0,0 +1,294 @@
// 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 synthesis of Functions that delegate to declared
// methods; they come in three kinds:
//
// (1) wrappers: methods that wrap declared methods, performing
// implicit pointer indirections and embedded field selections.
//
// (2) thunks: funcs that wrap declared methods. Like wrappers,
// thunks perform indirections and field selections. The thunk's
// first parameter is used as the receiver for the method call.
//
// (3) bounds: funcs that wrap declared methods. The bound's sole
// free variable, supplied by a closure, is used as the receiver
// for the method call. No indirections or field selections are
// performed since they can be done before the call.
import (
"fmt"
"go/types"
)
// -- wrappers -----------------------------------------------------------
// makeWrapper returns a synthetic method that delegates to the
// declared method denoted by meth.Obj(), first performing any
// necessary pointer indirections or field selections implied by meth.
//
// The resulting method's receiver type is meth.Recv().
//
// This function is versatile but quite subtle! Consider the
// following axes of variation when making changes:
// - optional receiver indirection
// - optional implicit field selections
// - meth.Obj() may denote a concrete or an interface method
// - the result may be a thunk or a wrapper.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func makeWrapper(prog *Program, sel *types.Selection) *Function {
obj := sel.Obj().(*types.Func) // the declared function
sig := sel.Type().(*types.Signature) // type of this wrapper
var recv *types.Var // wrapper's receiver or thunk's params[0]
name := obj.Name()
var description string
var start int // first regular param
if sel.Kind() == types.MethodExpr {
name += "$thunk"
description = "thunk"
recv = sig.Params().At(0)
start = 1
} else {
description = "wrapper"
recv = sig.Recv()
}
description = fmt.Sprintf("%s for %s", description, sel.Obj())
if prog.mode&LogSource != 0 {
defer logStack("make %s to (%s)", description, recv.Type())()
}
fn := &Function{
name: name,
method: sel,
object: obj,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
}
fn.startBody()
fn.addSpilledParam(recv)
createParams(fn, start)
indices := sel.Index()
var v Value = fn.Locals[0] // spilled receiver
if isPointer(sel.Recv()) {
v = emitLoad(fn, v)
// For simple indirection wrappers, perform an informative nil-check:
// "value method (T).f called using nil *T pointer"
if len(indices) == 1 && !isPointer(recvType(obj)) {
var c Call
c.Call.Value = &Builtin{
name: "ssa:wrapnilchk",
sig: types.NewSignature(nil,
types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
types.NewTuple(anonVar(sel.Recv())), false),
}
c.Call.Args = []Value{
v,
stringConst(deref(sel.Recv()).String()),
stringConst(sel.Obj().Name()),
}
c.setType(v.Type())
v = fn.emit(&c)
}
}
// Invariant: v is a pointer, either
// value of *A receiver param, or
// address of A spilled receiver.
// We use pointer arithmetic (FieldAddr possibly followed by
// Load) in preference to value extraction (Field possibly
// preceded by Load).
v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
// Invariant: v is a pointer, either
// value of implicit *C field, or
// address of implicit C field.
var c Call
if r := recvType(obj); !isInterface(r) { // concrete method
if !isPointer(r) {
v = emitLoad(fn, v)
}
c.Call.Value = prog.declaredFunc(obj)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = obj
c.Call.Value = emitLoad(fn, v)
}
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
return fn
}
// createParams creates parameters for wrapper method fn based on its
// Signature.Params, which do not include the receiver.
// start is the index of the first regular parameter to use.
//
func createParams(fn *Function, start int) {
var last *Parameter
tparams := fn.Signature.Params()
for i, n := start, tparams.Len(); i < n; i++ {
last = fn.addParamObj(tparams.At(i))
}
if fn.Signature.Variadic() {
last.typ = types.NewSlice(last.typ)
}
}
// -- bounds -----------------------------------------------------------
// makeBound returns a bound method wrapper (or "bound"), a synthetic
// function that delegates to a concrete or interface method denoted
// by obj. The resulting function has no receiver, but has one free
// variable which will be used as the method's receiver in the
// tail-call.
//
// Use MakeClosure with such a wrapper to construct a bound method
// closure. e.g.:
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// var t T
// f := t.meth
// f() // calls t.meth()
//
// f is a closure of a synthetic wrapper defined as if by:
//
// f := func() { return t.meth() }
//
// Unlike makeWrapper, makeBound need perform no indirection or field
// selections because that can be done before the closure is
// constructed.
//
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
//
func makeBound(prog *Program, obj *types.Func) *Function {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
fn, ok := prog.bounds[obj]
if !ok {
description := fmt.Sprintf("bound method wrapper for %s", obj)
if prog.mode&LogSource != 0 {
defer logStack("%s", description)()
}
fn = &Function{
name: obj.Name() + "$bound",
object: obj,
Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
}
fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
fn.FreeVars = []*FreeVar{fv}
fn.startBody()
createParams(fn, 0)
var c Call
if !isInterface(recvType(obj)) { // concrete
c.Call.Value = prog.declaredFunc(obj)
c.Call.Args = []Value{fv}
} else {
c.Call.Value = fv
c.Call.Method = obj
}
for _, arg := range fn.Params {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
prog.bounds[obj] = fn
}
return fn
}
// -- thunks -----------------------------------------------------------
// makeThunk returns a thunk, a synthetic function that delegates to a
// concrete or interface method denoted by sel.Obj(). The resulting
// function has no receiver, but has an additional (first) regular
// parameter.
//
// Precondition: sel.Kind() == types.MethodExpr.
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// f := T.meth
// var t T
// f(t) // calls t.meth()
//
// f is a synthetic wrapper defined as if by:
//
// f := func(t T) { return t.meth() }
//
// TODO(adonovan): opt: currently the stub is created even when used
// directly in a function call: C.f(i, 0). This is less efficient
// than inlining the stub.
//
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
//
func makeThunk(prog *Program, sel *types.Selection) *Function {
if sel.Kind() != types.MethodExpr {
panic(sel)
}
key := selectionKey{
kind: sel.Kind(),
recv: sel.Recv(),
obj: sel.Obj(),
index: fmt.Sprint(sel.Index()),
indirect: sel.Indirect(),
}
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
// Canonicalize key.recv to avoid constructing duplicate thunks.
canonRecv, ok := prog.canon.At(key.recv).(types.Type)
if !ok {
canonRecv = key.recv
prog.canon.Set(key.recv, canonRecv)
}
key.recv = canonRecv
fn, ok := prog.thunks[key]
if !ok {
fn = makeWrapper(prog, sel)
if fn.Signature.Recv() != nil {
panic(fn) // unexpected receiver
}
prog.thunks[key] = fn
}
return fn
}
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
}
// selectionKey is like types.Selection but a usable map key.
type selectionKey struct {
kind types.SelectionKind
recv types.Type // canonicalized via Program.canon
obj types.Object
index string
indirect bool
}