+++ /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
-}