--- /dev/null
+// 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 (
+ "bytes"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "io"
+ "math/big"
+)
+
+// TODO(gri) use tabwriter for alignment?
+
+func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
+ var p printer
+ p.pkg = pkg
+ p.printPackage(pkg, filter)
+ p.printGccgoExtra(pkg)
+ io.Copy(w, &p.buf)
+}
+
+type printer struct {
+ pkg *types.Package
+ buf bytes.Buffer
+ indent int // current indentation level
+ last byte // last byte written
+}
+
+func (p *printer) print(s string) {
+ // Write the string one byte at a time. We care about the presence of
+ // newlines for indentation which we will see even in the presence of
+ // (non-corrupted) Unicode; no need to read one rune at a time.
+ for i := 0; i < len(s); i++ {
+ ch := s[i]
+ if ch != '\n' && p.last == '\n' {
+ // Note: This could lead to a range overflow for very large
+ // indentations, but it's extremely unlikely to happen for
+ // non-pathological code.
+ p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent])
+ }
+ p.buf.WriteByte(ch)
+ p.last = ch
+ }
+}
+
+func (p *printer) printf(format string, args ...interface{}) {
+ p.print(fmt.Sprintf(format, args...))
+}
+
+// methodsFor returns the named type and corresponding methods if the type
+// denoted by obj is not an interface and has methods. Otherwise it returns
+// the zero value.
+func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
+ named, _ := obj.Type().(*types.Named)
+ if named == nil {
+ // A type name's type can also be the
+ // exported basic type unsafe.Pointer.
+ return nil, nil
+ }
+ if _, ok := named.Underlying().(*types.Interface); ok {
+ // ignore interfaces
+ return nil, nil
+ }
+ methods := combinedMethodSet(named)
+ if len(methods) == 0 {
+ return nil, nil
+ }
+ return named, methods
+}
+
+func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
+ // collect objects by kind
+ var (
+ consts []*types.Const
+ typem []*types.Named // non-interface types with methods
+ typez []*types.TypeName // interfaces or types without methods
+ vars []*types.Var
+ funcs []*types.Func
+ builtins []*types.Builtin
+ methods = make(map[*types.Named][]*types.Selection) // method sets for named types
+ )
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Exported() {
+ // collect top-level exported and possibly filtered objects
+ if filter == nil || filter(obj) {
+ switch obj := obj.(type) {
+ case *types.Const:
+ consts = append(consts, obj)
+ case *types.TypeName:
+ // group into types with methods and types without
+ if named, m := methodsFor(obj); named != nil {
+ typem = append(typem, named)
+ methods[named] = m
+ } else {
+ typez = append(typez, obj)
+ }
+ case *types.Var:
+ vars = append(vars, obj)
+ case *types.Func:
+ funcs = append(funcs, obj)
+ case *types.Builtin:
+ // for unsafe.Sizeof, etc.
+ builtins = append(builtins, obj)
+ }
+ }
+ } else if filter == nil {
+ // no filtering: collect top-level unexported types with methods
+ if obj, _ := obj.(*types.TypeName); obj != nil {
+ // see case *types.TypeName above
+ if named, m := methodsFor(obj); named != nil {
+ typem = append(typem, named)
+ methods[named] = m
+ }
+ }
+ }
+ }
+
+ p.printf("package %s // %q\n", pkg.Name(), pkg.Path())
+
+ p.printDecl("const", len(consts), func() {
+ for _, obj := range consts {
+ p.printObj(obj)
+ p.print("\n")
+ }
+ })
+
+ p.printDecl("var", len(vars), func() {
+ for _, obj := range vars {
+ p.printObj(obj)
+ p.print("\n")
+ }
+ })
+
+ p.printDecl("type", len(typez), func() {
+ for _, obj := range typez {
+ p.printf("%s ", obj.Name())
+ typ := obj.Type()
+ if isAlias(obj) {
+ p.print("= ")
+ p.writeType(p.pkg, typ)
+ } else {
+ p.writeType(p.pkg, typ.Underlying())
+ }
+ p.print("\n")
+ }
+ })
+
+ // non-interface types with methods
+ for _, named := range typem {
+ first := true
+ if obj := named.Obj(); obj.Exported() {
+ if first {
+ p.print("\n")
+ first = false
+ }
+ p.printf("type %s ", obj.Name())
+ p.writeType(p.pkg, named.Underlying())
+ p.print("\n")
+ }
+ for _, m := range methods[named] {
+ if obj := m.Obj(); obj.Exported() {
+ if first {
+ p.print("\n")
+ first = false
+ }
+ p.printFunc(m.Recv(), obj.(*types.Func))
+ p.print("\n")
+ }
+ }
+ }
+
+ if len(funcs) > 0 {
+ p.print("\n")
+ for _, obj := range funcs {
+ p.printFunc(nil, obj)
+ p.print("\n")
+ }
+ }
+
+ // TODO(gri) better handling of builtins (package unsafe only)
+ if len(builtins) > 0 {
+ p.print("\n")
+ for _, obj := range builtins {
+ p.printf("func %s() // builtin\n", obj.Name())
+ }
+ }
+
+ p.print("\n")
+}
+
+func (p *printer) printDecl(keyword string, n int, printGroup func()) {
+ switch n {
+ case 0:
+ // nothing to do
+ case 1:
+ p.printf("\n%s ", keyword)
+ printGroup()
+ default:
+ p.printf("\n%s (\n", keyword)
+ p.indent++
+ printGroup()
+ p.indent--
+ p.print(")\n")
+ }
+}
+
+// absInt returns the absolute value of v as a *big.Int.
+// v must be a numeric value.
+func absInt(v constant.Value) *big.Int {
+ // compute big-endian representation of v
+ b := constant.Bytes(v) // little-endian
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+ return new(big.Int).SetBytes(b)
+}
+
+var (
+ one = big.NewRat(1, 1)
+ ten = big.NewRat(10, 1)
+)
+
+// floatString returns the string representation for a
+// numeric value v in normalized floating-point format.
+func floatString(v constant.Value) string {
+ if constant.Sign(v) == 0 {
+ return "0.0"
+ }
+ // x != 0
+
+ // convert |v| into a big.Rat x
+ x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v)))
+
+ // normalize x and determine exponent e
+ // (This is not very efficient, but also not speed-critical.)
+ var e int
+ for x.Cmp(ten) >= 0 {
+ x.Quo(x, ten)
+ e++
+ }
+ for x.Cmp(one) < 0 {
+ x.Mul(x, ten)
+ e--
+ }
+
+ // TODO(gri) Values such as 1/2 are easier to read in form 0.5
+ // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as
+ // 10.0. Fine-tune best exponent range for readability.
+
+ s := x.FloatString(100) // good-enough precision
+
+ // trim trailing 0's
+ i := len(s)
+ for i > 0 && s[i-1] == '0' {
+ i--
+ }
+ s = s[:i]
+
+ // add a 0 if the number ends in decimal point
+ if len(s) > 0 && s[len(s)-1] == '.' {
+ s += "0"
+ }
+
+ // add exponent and sign
+ if e != 0 {
+ s += fmt.Sprintf("e%+d", e)
+ }
+ if constant.Sign(v) < 0 {
+ s = "-" + s
+ }
+
+ // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator
+ // are just a small number of decimal digits), add the exact fraction as
+ // a comment. For instance: 3.3333...e-1 /* = 1/3 */
+
+ return s
+}
+
+// valString returns the string representation for the value v.
+// Setting floatFmt forces an integer value to be formatted in
+// normalized floating-point format.
+// TODO(gri) Move this code into package constant.
+func valString(v constant.Value, floatFmt bool) string {
+ switch v.Kind() {
+ case constant.Int:
+ if floatFmt {
+ return floatString(v)
+ }
+ case constant.Float:
+ return floatString(v)
+ case constant.Complex:
+ re := constant.Real(v)
+ im := constant.Imag(v)
+ var s string
+ if constant.Sign(re) != 0 {
+ s = floatString(re)
+ if constant.Sign(im) >= 0 {
+ s += " + "
+ } else {
+ s += " - "
+ im = constant.UnaryOp(token.SUB, im, 0) // negate im
+ }
+ }
+ // im != 0, otherwise v would be constant.Int or constant.Float
+ return s + floatString(im) + "i"
+ }
+ return v.String()
+}
+
+func (p *printer) printObj(obj types.Object) {
+ p.print(obj.Name())
+
+ typ, basic := obj.Type().Underlying().(*types.Basic)
+ if basic && typ.Info()&types.IsUntyped != 0 {
+ // don't write untyped types
+ } else {
+ p.print(" ")
+ p.writeType(p.pkg, obj.Type())
+ }
+
+ if obj, ok := obj.(*types.Const); ok {
+ floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0
+ p.print(" = ")
+ p.print(valString(obj.Val(), floatFmt))
+ }
+}
+
+func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
+ p.print("func ")
+ sig := obj.Type().(*types.Signature)
+ if recvType != nil {
+ p.print("(")
+ p.writeType(p.pkg, recvType)
+ p.print(") ")
+ }
+ p.print(obj.Name())
+ p.writeSignature(p.pkg, sig)
+}
+
+// combinedMethodSet returns the method set for a named type T
+// merged with all the methods of *T that have different names than
+// the methods of T.
+//
+// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
+// but doesn't require a MethodSetCache.
+// TODO(gri) If this functionality doesn't change over time, consider
+// just calling IntuitiveMethodSet eventually.
+func combinedMethodSet(T *types.Named) []*types.Selection {
+ // method set for T
+ mset := types.NewMethodSet(T)
+ var res []*types.Selection
+ for i, n := 0, mset.Len(); i < n; i++ {
+ res = append(res, mset.At(i))
+ }
+
+ // add all *T methods with names different from T methods
+ pmset := types.NewMethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ pm := pmset.At(i)
+ if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
+ res = append(res, pm)
+ }
+ }
+
+ return res
+}