mirror of
https://github.com/golang/go
synced 2024-11-18 16:24:42 -07:00
02990bd494
Clients such as compilers need this information in order to correctly link against imported packages. This also adds support for the condensed import data format where the priority information is stored as a suffix of the condensed import data, as well as support for archive files. LGTM=gri R=gri CC=golang-codereviews, iant https://golang.org/cl/78740043
369 lines
8.9 KiB
Go
369 lines
8.9 KiB
Go
// 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/token"
|
|
"io"
|
|
"math/big"
|
|
|
|
"code.google.com/p/go.tools/go/exact"
|
|
"code.google.com/p/go.tools/go/types"
|
|
)
|
|
|
|
// 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())
|
|
p.writeType(p.pkg, obj.Type().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 exact.Value) *big.Int {
|
|
// compute big-endian representation of v
|
|
b := exact.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 exact.Value) string {
|
|
if exact.Sign(v) == 0 {
|
|
return "0.0"
|
|
}
|
|
// x != 0
|
|
|
|
// convert |v| into a big.Rat x
|
|
x := new(big.Rat).SetFrac(absInt(exact.Num(v)), absInt(exact.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 exact.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 exact.
|
|
func valString(v exact.Value, floatFmt bool) string {
|
|
switch v.Kind() {
|
|
case exact.Int:
|
|
if floatFmt {
|
|
return floatString(v)
|
|
}
|
|
case exact.Float:
|
|
return floatString(v)
|
|
case exact.Complex:
|
|
re := exact.Real(v)
|
|
im := exact.Imag(v)
|
|
var s string
|
|
if exact.Sign(re) != 0 {
|
|
s = floatString(re)
|
|
if exact.Sign(im) >= 0 {
|
|
s += " + "
|
|
} else {
|
|
s += " - "
|
|
im = exact.UnaryOp(token.SUB, im, 0) // negate im
|
|
}
|
|
}
|
|
// im != 0, otherwise v would be exact.Int or exact.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
|
|
}
|