diff --git a/cmd/godex/godex.go b/cmd/godex/godex.go index 21f7552090..6ce40c914e 100644 --- a/cmd/godex/godex.go +++ b/cmd/godex/godex.go @@ -14,6 +14,8 @@ import ( "code.google.com/p/go.tools/go/types" ) +// BUG(gri) cannot specify package paths with dots (code.google.com/p/go.tools/cmd/ssadump) + var ( source = flag.String("s", "", "only consider packages from this source") verbose = flag.Bool("v", false, "verbose mode") diff --git a/cmd/godex/print.go b/cmd/godex/print.go index 438b3f7941..48cd5e4299 100644 --- a/cmd/godex/print.go +++ b/cmd/godex/print.go @@ -12,7 +12,6 @@ import ( "code.google.com/p/go.tools/go/types" ) -// TODO(gri) handle indentation // TODO(gri) filter unexported fields of struct types? // TODO(gri) use tabwriter for alignment? @@ -26,25 +25,40 @@ func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) { type printer struct { pkg *types.Package buf bytes.Buffer - indent int + indent int // current indentation level + last byte // last byte written } func (p *printer) print(s string) { - p.buf.WriteString(s) + // Write the string one byte at a time. We care about the presence of + // newlines for indentation which we will see even in the presence of + // (non-corrupted) Unicode; no need to read one rune at a time. + for i := 0; i < len(s); i++ { + ch := s[i] + if ch != '\n' && p.last == '\n' { + // Note: This could lead to a range overflow for very large + // indentations, but it's extremely unlikely to happen for + // non-pathological code. + p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent]) + } + p.buf.WriteByte(ch) + p.last = ch + } } func (p *printer) printf(format string, args ...interface{}) { - fmt.Fprintf(&p.buf, format, args...) + p.print(fmt.Sprintf(format, args...)) } func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) { // collect objects by kind var ( - consts []*types.Const - typez []*types.TypeName // types without methods - typem []*types.TypeName // types with methods - vars []*types.Var - funcs []*types.Func + consts []*types.Const + typez []*types.TypeName // types without methods + typem []*types.TypeName // types with methods + vars []*types.Var + funcs []*types.Func + builtins []*types.Builtin ) scope := pkg.Scope() for _, name := range scope.Names() { @@ -56,7 +70,7 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo case *types.Const: consts = append(consts, obj) case *types.TypeName: - if obj.Type().(*types.Named).NumMethods() > 0 { + if named, _ := obj.Type().(*types.Named); named != nil && named.NumMethods() > 0 { typem = append(typem, obj) } else { typez = append(typez, obj) @@ -65,6 +79,9 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo vars = append(vars, obj) case *types.Func: funcs = append(funcs, obj) + case *types.Builtin: + // for unsafe.Sizeof, etc. + builtins = append(builtins, obj) } } @@ -72,36 +89,42 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo if len(consts) > 0 { p.print("const (\n") + p.indent++ for _, obj := range consts { p.printObj(obj) p.print("\n") } + p.indent-- p.print(")\n\n") } if len(vars) > 0 { p.print("var (\n") + p.indent++ for _, obj := range vars { p.printObj(obj) p.print("\n") } + p.indent-- p.print(")\n\n") } if len(typez) > 0 { p.print("type (\n") + p.indent++ for _, obj := range typez { - p.printf("\t%s ", obj.Name()) - types.WriteType(&p.buf, p.pkg, obj.Type().Underlying()) + p.printf("%s ", obj.Name()) + p.writeType(p.pkg, obj.Type().Underlying()) p.print("\n") } + p.indent-- p.print(")\n\n") } for _, obj := range typem { p.printf("type %s ", obj.Name()) typ := obj.Type().(*types.Named) - types.WriteType(&p.buf, p.pkg, typ.Underlying()) + p.writeType(p.pkg, typ.Underlying()) p.print("\n") for i, n := 0, typ.NumMethods(); i < n; i++ { p.printFunc(typ.Method(i)) @@ -115,15 +138,20 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo p.print("\n") } + // TODO(gri) better handling of builtins (package unsafe only) + for _, obj := range builtins { + p.printf("func %s() // builtin\n", obj.Name()) + } + p.print("\n") } func (p *printer) printObj(obj types.Object) { - p.printf("\t %s", obj.Name()) + p.printf("%s", obj.Name()) // don't write untyped types (for constants) if typ := obj.Type(); typed(typ) { p.print(" ") - types.WriteType(&p.buf, p.pkg, typ) + p.writeType(p.pkg, typ) } // write constant value if obj, ok := obj.(*types.Const); ok { @@ -140,11 +168,11 @@ func (p *printer) printFunc(obj *types.Func) { p.print(name) p.print(" ") } - types.WriteType(&p.buf, p.pkg, recv.Type()) + p.writeType(p.pkg, recv.Type()) p.print(") ") } p.print(obj.Name()) - types.WriteSignature(&p.buf, p.pkg, sig) + p.writeSignature(p.pkg, sig) } func typed(typ types.Type) bool { diff --git a/cmd/godex/writetype.go b/cmd/godex/writetype.go new file mode 100644 index 0000000000..12da1d4272 --- /dev/null +++ b/cmd/godex/writetype.go @@ -0,0 +1,246 @@ +// 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 main + +import ( + "code.google.com/p/go.tools/go/types" +) + +// This file implements writing of types. The functionlity is lifted +// directly from go/types, but now contains various modifications for +// nicer output. +// +// TODO(gri) back-port once we have a fixed interface and once the +// go/types API is not frozen anymore for the 1.3 release; and remove +// this implementation if possible. + +func (p *printer) writeType(this *types.Package, typ types.Type) { + p.writeTypeInternal(this, typ, make([]types.Type, 8)) +} + +// From go/types - leave for now to ease back-porting this code. +const GcCompatibilityMode = false + +func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) { + // Theoretically, this is a quadratic lookup algorithm, but in + // practice deeply nested composite types with unnamed component + // types are uncommon. This code is likely more efficient than + // using a map. + for _, t := range visited { + if t == typ { + p.printf("○%T", typ) // cycle to typ + return + } + } + visited = append(visited, typ) + + switch t := typ.(type) { + case nil: + p.print("") + + case *types.Basic: + if t.Kind() == types.UnsafePointer { + p.print("unsafe.") + } + if GcCompatibilityMode { + // forget the alias names + switch t.Kind() { + case types.Byte: + t = types.Typ[types.Uint8] + case types.Rune: + t = types.Typ[types.Int32] + } + } + p.print(t.Name()) + + case *types.Array: + p.printf("[%d]", t.Len()) + p.writeTypeInternal(this, t.Elem(), visited) + + case *types.Slice: + p.print("[]") + p.writeTypeInternal(this, t.Elem(), visited) + + case *types.Struct: + // TODO(gri) filter fields? + n := t.NumFields() + if n == 0 { + p.print("struct{}") + return + } + + p.print("struct {\n") + p.indent++ + for i := 0; i < n; i++ { + f := t.Field(i) + if !f.Anonymous() { + p.printf("%s ", f.Name()) + } + p.writeTypeInternal(this, f.Type(), visited) + if tag := t.Tag(i); tag != "" { + p.printf(" %q", tag) + } + p.print("\n") + } + p.indent-- + p.print("}") + + case *types.Pointer: + p.print("*") + p.writeTypeInternal(this, t.Elem(), visited) + + case *types.Tuple: + p.writeTuple(this, t, false, visited) + + case *types.Signature: + p.print("func") + p.writeSignatureInternal(this, t, visited) + + case *types.Interface: + // We write the source-level methods and embedded types rather + // than the actual method set since resolved method signatures + // may have non-printable cycles if parameters have anonymous + // interface types that (directly or indirectly) embed the + // current interface. For instance, consider the result type + // of m: + // + // type T interface{ + // m() interface{ T } + // } + // + // TODO(gri) filter methods? + n := t.NumMethods() + if n == 0 { + p.print("interface{}") + return + } + + p.print("interface {\n") + p.indent++ + if GcCompatibilityMode { + // print flattened interface + // (useful to compare against gc-generated interfaces) + for i := 0; i < n; i++ { + m := t.Method(i) + p.print(m.Name()) + p.writeSignatureInternal(this, m.Type().(*types.Signature), visited) + p.print("\n") + } + } else { + // print explicit interface methods and embedded types + for i, n := 0, t.NumExplicitMethods(); i < n; i++ { + m := t.ExplicitMethod(i) + p.print(m.Name()) + p.writeSignatureInternal(this, m.Type().(*types.Signature), visited) + p.print("\n") + } + for i, n := 0, t.NumEmbeddeds(); i < n; i++ { + typ := t.Embedded(i) + p.writeTypeInternal(this, typ, visited) + p.print("\n") + } + } + p.indent-- + p.print("}") + + case *types.Map: + p.print("map[") + p.writeTypeInternal(this, t.Key(), visited) + p.print("]") + p.writeTypeInternal(this, t.Elem(), visited) + + case *types.Chan: + var s string + var parens bool + switch t.Dir() { + case types.SendRecv: + s = "chan " + // chan (<-chan T) requires parentheses + if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly { + parens = true + } + case types.SendOnly: + s = "chan<- " + case types.RecvOnly: + s = "<-chan " + default: + panic("unreachable") + } + p.print(s) + if parens { + p.print("(") + } + p.writeTypeInternal(this, t.Elem(), visited) + if parens { + p.print(")") + } + + case *types.Named: + s := "" + if obj := t.Obj(); obj != nil { + if pkg := obj.Pkg(); pkg != nil { + if pkg != this { + p.print(pkg.Path()) + p.print(".") + } + // TODO(gri): function-local named types should be displayed + // differently from named types at package level to avoid + // ambiguity. + } + s = obj.Name() + } + p.print(s) + + default: + // For externally defined implementations of Type. + p.print(t.String()) + } +} + +func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) { + p.print("(") + for i, n := 0, tup.Len(); i < n; i++ { + if i > 0 { + p.print(", ") + } + v := tup.At(i) + if name := v.Name(); name != "" { + p.print(name) + p.print(" ") + } + typ := v.Type() + if variadic && i == n-1 { + p.print("...") + typ = typ.(*types.Slice).Elem() + } + p.writeTypeInternal(this, typ, visited) + } + p.print(")") +} + +func (p *printer) writeSignature(this *types.Package, sig *types.Signature) { + p.writeSignatureInternal(this, sig, make([]types.Type, 8)) +} + +func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) { + p.writeTuple(this, sig.Params(), sig.Variadic(), visited) + + res := sig.Results() + n := res.Len() + if n == 0 { + // no result + return + } + + p.print(" ") + if n == 1 && res.At(0).Name() == "" { + // single unnamed result + p.writeTypeInternal(this, res.At(0).Type(), visited) + return + } + + // multiple or named result(s) + p.writeTuple(this, res, false, visited) +}