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