go.tools/ssa: display named types package-qualified using types.TypeString.

Details:
- use relative (non-qualified) names in more places
- Member interface now has Package(), RelString() methods.
- (*Function).DumpTo: add "# Package: " header.
- Added sanity checks for String functions.

R=gri, gri
CC=golang-dev
https://golang.org/cl/26380043
This commit is contained in:
Alan Donovan 2013-11-15 09:21:48 -05:00
parent 0820934407
commit 9fcd20e680
7 changed files with 118 additions and 110 deletions

View File

@ -65,21 +65,27 @@ func zeroConst(t types.Type) *Const {
panic(fmt.Sprint("zeroConst: unexpected ", t))
}
func (c *Const) Name() string {
var s string
func (c *Const) valstring() string {
if c.Value == nil {
s = "nil"
return "nil"
} else if c.Value.Kind() == exact.String {
s = exact.StringVal(c.Value)
s := exact.StringVal(c.Value)
const max = 20
if len(s) > max {
s = s[:max-3] + "..." // abbreviate
}
s = strconv.Quote(s)
return strconv.Quote(s)
} else {
s = c.Value.String()
return c.Value.String()
}
return s + ":" + c.typ.String()
}
func (c *Const) Name() string {
return fmt.Sprintf("%s:%s", c.valstring(), c.typ)
}
func (v *Const) String() string {
return v.Name()
}
func (c *Const) Type() types.Type {

View File

@ -71,12 +71,16 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
switch obj := obj.(type) {
case *types.TypeName:
pkg.values[obj] = nil // for needMethods
pkg.Members[name] = &Type{object: obj}
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

View File

@ -82,6 +82,7 @@ func main() {
// const message message = "Hello, World!":untyped string
//
// # Name: main.init
// # Package: main
// # Synthetic: package initializer
// func init():
// .0.entry: P:0 S:2
@ -95,6 +96,7 @@ func main() {
// return
//
// # Name: main.main
// # Package: main
// # Location: hello.go:8:6
// func main():
// .0.entry: P:0 S:0

View File

@ -444,7 +444,7 @@ func (f *Function) emit(instr Instruction) Value {
return f.currentBlock.emit(instr)
}
// FullName returns the full name of this function, qualified by
// 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.
@ -453,13 +453,13 @@ func (f *Function) emit(instr Instruction) Value {
// "math.IsNaN" // a package-level function
// "IsNaN" // intra-package reference to same
// "(*sync.WaitGroup).Add" // a declared method
// "(*exp/ssa.Return).Block" // a promotion wrapper method
// "(ssa.Instruction).Block" // an interface method wrapper
// "(*Return).Block" // a promotion wrapper method (intra-package ref)
// "(Instruction).Block" // an interface method wrapper (intra-package ref)
// "func@5.32" // an anonymous function
// "bound$(*T).f" // a bound method wrapper
//
// If from==f.Pkg, suppress package qualification.
func (f *Function) fullName(from *Package) string {
func (f *Function) RelString(from *types.Package) string {
// TODO(adonovan): expose less fragile case discrimination
// using f.method.
@ -490,8 +490,8 @@ func (f *Function) fullName(from *Package) string {
// Package-level function.
// Prefix with package name for cross-package references only.
if from != f.Pkg {
return fmt.Sprintf("%s.%s", f.Pkg.Object.Path(), f.name)
if p := f.pkgobj(); p != from {
return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
return f.name
}
@ -499,7 +499,7 @@ func (f *Function) fullName(from *Package) string {
// writeSignature writes to w the signature sig in declaration syntax.
// Derived from types.Signature.String().
//
func writeSignature(w io.Writer, pkg *Package, name string, sig *types.Signature, params []*Parameter) {
func writeSignature(w io.Writer, pkg *types.Package, name string, sig *types.Signature, params []*Parameter) {
io.WriteString(w, "func ")
if recv := sig.Recv(); recv != nil {
io.WriteString(w, "(")
@ -533,16 +533,26 @@ func writeSignature(w io.Writer, pkg *Package, name string, sig *types.Signature
if n == 1 && r.At(0).Name() == "" {
io.WriteString(w, relType(r.At(0).Type(), pkg))
} else {
io.WriteString(w, r.String()) // TODO(adonovan): use relType
io.WriteString(w, relType(r, pkg))
}
}
}
func (f *Function) pkgobj() *types.Package {
if f.Pkg != nil {
return f.Pkg.Object
}
return nil
}
// DumpTo prints to w a human readable "disassembly" of the SSA code of
// all basic blocks of function f.
//
func (f *Function) DumpTo(w io.Writer) {
fmt.Fprintf(w, "# Name: %s\n", f.String())
if f.Pkg != nil {
fmt.Fprintf(w, "# Package: %s\n", f.Pkg.Object.Path())
}
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(w, "# Synthetic:", syn)
}
@ -558,21 +568,22 @@ func (f *Function) DumpTo(w io.Writer) {
fmt.Fprintf(w, "# Recover: %s\n", f.Recover)
}
pkgobj := f.pkgobj()
if f.FreeVars != nil {
io.WriteString(w, "# Free variables:\n")
for i, fv := range f.FreeVars {
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), f.Pkg))
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), pkgobj))
}
}
if len(f.Locals) > 0 {
io.WriteString(w, "# Locals:\n")
for i, l := range f.Locals {
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), f.Pkg))
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), pkgobj))
}
}
writeSignature(w, f.Pkg, f.Name(), f.Signature, f.Params)
writeSignature(w, pkgobj, f.Name(), f.Signature, f.Params)
io.WriteString(w, ":\n")
if f.Blocks == nil {
@ -608,7 +619,7 @@ func (f *Function) DumpTo(w io.Writer) {
l -= n
// Right-align the type.
if t := v.Type(); t != nil {
fmt.Fprintf(w, " %*s", l-10, relType(t, f.Pkg))
fmt.Fprintf(w, " %*s", l-10, relType(t, pkgobj))
}
case nil:
// Be robust against bad transforms.

View File

@ -14,57 +14,41 @@ import (
"io"
"reflect"
"sort"
"strings"
"code.google.com/p/go.tools/go/types"
)
// relName returns the name of v relative to i.
// In most cases, this is identical to v.Name(), but for references to
// Functions (including methods) and Globals, the FullName is used
// instead, explicitly package-qualified for cross-package references.
// 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 {
switch v := v.(type) {
case *Global:
if i != nil && v.Pkg == i.Parent().Pkg {
return v.Name()
}
return v.FullName()
case *Function:
var pkg *Package
var from *types.Package
if i != nil {
pkg = i.Parent().Pkg
from = i.Parent().pkgobj()
}
return v.fullName(pkg)
switch v := v.(type) {
case Member: // *Function or *Global
return v.RelString(from)
case *Const:
return v.valstring() + ":" + relType(v.Type(), from)
}
return v.Name()
}
// relType is like t.String(), but if t is a Named type belonging to
// package from, optionally wrapped by one or more Pointer
// constructors, package qualification is suppressed.
//
// TODO(gri): provide this functionality in go/types (using a
// *types.Package, obviously).
//
func relType(t types.Type, from *Package) string {
if from != nil {
t2 := t
var nptr int // number of Pointers stripped off
for {
ptr, ok := t2.(*types.Pointer)
if !ok {
break
func relType(t types.Type, from *types.Package) string {
return types.TypeString(from, t)
}
t2 = ptr.Elem()
nptr++
func relString(m Member, from *types.Package) string {
// NB: not all globals have an Object (e.g. init$guard),
// so use Package().Object not Object.Package().
if obj := m.Package().Object; obj != nil && obj != from {
return fmt.Sprintf("%s.%s", obj.Path(), m.Name())
}
if n, ok := t2.(*types.Named); ok && n.Obj().Pkg() == from.Object {
return strings.Repeat("*", nptr) + n.Obj().Name()
}
}
return t.String()
return m.Name()
}
// Value.String()
@ -72,10 +56,6 @@ func relType(t types.Type, from *Package) string {
// This method is provided only for debugging.
// It never appears in disassembly, which uses Value.Name().
func (v *Const) String() string {
return v.Name()
}
func (v *Parameter) String() string {
return fmt.Sprintf("parameter %s : %s", v.Name(), v.Type())
}
@ -84,23 +64,10 @@ func (v *Capture) String() string {
return fmt.Sprintf("capture %s : %s", v.Name(), v.Type())
}
func (v *Global) String() string {
return v.FullName()
}
func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s", v.Name())
}
func (v *Function) String() string {
return v.fullName(nil)
}
// FullName returns g's package-qualified name.
func (g *Global) FullName() string {
return fmt.Sprintf("%s.%s", g.Pkg.Object.Path(), g.name)
}
// Instruction.String()
func (v *Alloc) String() string {
@ -108,7 +75,7 @@ func (v *Alloc) String() string {
if v.Heap {
op = "new"
}
return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), v.Parent().Pkg), v.Comment)
return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), v.Parent().pkgobj()), v.Comment)
}
func (v *Phi) String() string {
@ -170,7 +137,7 @@ func (v *Call) String() string {
}
func (v *ChangeType) String() string {
return fmt.Sprintf("changetype %s <- %s (%s)", relType(v.Type(), v.Parent().Pkg), v.X.Type(), relName(v.X, v))
return fmt.Sprintf("changetype %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), v.X.Type(), relName(v.X, v))
}
func (v *BinOp) String() string {
@ -182,7 +149,7 @@ func (v *UnOp) String() string {
}
func (v *Convert) String() string {
return fmt.Sprintf("convert %s <- %s (%s)", relType(v.Type(), v.Parent().Pkg), v.X.Type(), relName(v.X, v))
return fmt.Sprintf("convert %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), v.X.Type(), relName(v.X, v))
}
func (v *ChangeInterface) String() string {
@ -190,7 +157,7 @@ func (v *ChangeInterface) String() string {
}
func (v *MakeInterface) String() string {
return fmt.Sprintf("make %s <- %s (%s)", relType(v.Type(), v.Parent().Pkg), relType(v.X.Type(), v.Parent().Pkg), relName(v.X, v))
return fmt.Sprintf("make %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), relType(v.X.Type(), v.Parent().pkgobj()), relName(v.X, v))
}
func (v *MakeClosure) String() string {
@ -289,7 +256,7 @@ func (v *Next) String() string {
}
func (v *TypeAssert) String() string {
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), v.AssertedType)
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, v.Parent().pkgobj()))
}
func (v *Extract) String() string {

View File

@ -367,6 +367,10 @@ func (s *sanity) checkFunction(fn *Function) bool {
if fn.Prog == nil {
s.errorf("nil Prog")
}
fn.String() // must not crash
fn.RelString(fn.pkgobj()) // must not crash
// All functions have a package, except wrappers (which are
// shared across packages, or duplicated as weak symbols in a
// separate-compilation model), and error.Error.
@ -430,6 +434,11 @@ func (s *sanity) checkFunction(fn *Function) bool {
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
if pkg.Object == nil {
panic(fmt.Sprintf("Package %s has no Object", pkg))
}
pkg.String() // must not crash
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",

View File

@ -63,10 +63,12 @@ type Package struct {
type Member interface {
Name() string // declared name of the package member
String() string // package-qualified name of the package member
RelString(*types.Package) string // like String, but relative refs are unqualified
Object() types.Object // typechecker's object for this member, if any
Pos() token.Pos // position of member's declaration, if known
Type() types.Type // type of the package member
Token() token.Token // token.{VAR,FUNC,CONST,TYPE}
Package() *Package // returns the containing package. (TODO: rename Pkg)
}
// A Type is a Member of a Package representing a package-level named type.
@ -75,6 +77,7 @@ type Member interface {
//
type Type struct {
object *types.TypeName
pkg *Package
}
// A NamedConst is a Member of Package representing a package-level
@ -90,6 +93,7 @@ type NamedConst struct {
object *types.Const
Value *Const
pos token.Pos
pkg *Package
}
// An SSA value that can be referenced by an instruction.
@ -1149,7 +1153,7 @@ type MapUpdate struct {
}
// A DebugRef instruction maps a source-level expression Expr to the
// SSA value that represents the value (!IsAddr) or address (IsAddr)
// SSA value X that represents the value (!IsAddr) or address (IsAddr)
// of that expression.
//
// DebugRef is a pseudo-instruction: it has no dynamic effect.
@ -1364,15 +1368,20 @@ func (v *Capture) Parent() *Function { return v.parent }
func (v *Global) Type() types.Type { return v.typ }
func (v *Global) Name() string { return v.name }
func (v *Global) Pos() token.Pos { return v.pos }
func (*Global) Referrers() *[]Instruction { return nil }
func (v *Global) Referrers() *[]Instruction { return nil }
func (v *Global) Token() token.Token { return token.VAR }
func (v *Global) Object() types.Object { return v.object }
func (v *Global) String() string { return v.RelString(nil) }
func (v *Global) Package() *Package { return v.Pkg }
func (v *Global) RelString(from *types.Package) string { return relString(v, from) }
func (v *Function) Name() string { return v.name }
func (v *Function) Type() types.Type { return v.Signature }
func (v *Function) Pos() token.Pos { return v.pos }
func (v *Function) Token() token.Token { return token.FUNC }
func (v *Function) Object() types.Object { return v.object }
func (v *Function) String() string { return v.RelString(nil) }
func (v *Function) Package() *Package { return v.Pkg }
func (v *Function) Referrers() *[]Instruction {
if v.Enclosing != nil {
return &v.referrers
@ -1408,18 +1417,18 @@ func (t *Type) Pos() token.Pos { return t.object.Pos() }
func (t *Type) Type() types.Type { return t.object.Type() }
func (t *Type) Token() token.Token { return token.TYPE }
func (t *Type) Object() types.Object { return t.object }
func (t *Type) String() string {
return fmt.Sprintf("%s.%s", t.object.Pkg().Path(), t.object.Name())
}
func (t *Type) String() string { return t.RelString(nil) }
func (t *Type) Package() *Package { return t.pkg }
func (t *Type) RelString(from *types.Package) string { return relString(t, from) }
func (c *NamedConst) Name() string { return c.object.Name() }
func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
func (c *NamedConst) String() string {
return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name())
}
func (c *NamedConst) String() string { return c.RelString(nil) }
func (c *NamedConst) Type() types.Type { return c.object.Type() }
func (c *NamedConst) Token() token.Token { return token.CONST }
func (c *NamedConst) Object() types.Object { return c.object }
func (c *NamedConst) Package() *Package { return c.pkg }
func (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) }
// Func returns the package-level function of the specified name,
// or nil if not found.