github.com/april1989/origin-go-tools@v0.0.32/cmd/godex/print.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/constant" 11 "go/token" 12 "go/types" 13 "io" 14 "math/big" 15 ) 16 17 // TODO(gri) use tabwriter for alignment? 18 19 func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) { 20 var p printer 21 p.pkg = pkg 22 p.printPackage(pkg, filter) 23 p.printGccgoExtra(pkg) 24 io.Copy(w, &p.buf) 25 } 26 27 type printer struct { 28 pkg *types.Package 29 buf bytes.Buffer 30 indent int // current indentation level 31 last byte // last byte written 32 } 33 34 func (p *printer) print(s string) { 35 // Write the string one byte at a time. We care about the presence of 36 // newlines for indentation which we will see even in the presence of 37 // (non-corrupted) Unicode; no need to read one rune at a time. 38 for i := 0; i < len(s); i++ { 39 ch := s[i] 40 if ch != '\n' && p.last == '\n' { 41 // Note: This could lead to a range overflow for very large 42 // indentations, but it's extremely unlikely to happen for 43 // non-pathological code. 44 p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent]) 45 } 46 p.buf.WriteByte(ch) 47 p.last = ch 48 } 49 } 50 51 func (p *printer) printf(format string, args ...interface{}) { 52 p.print(fmt.Sprintf(format, args...)) 53 } 54 55 // methodsFor returns the named type and corresponding methods if the type 56 // denoted by obj is not an interface and has methods. Otherwise it returns 57 // the zero value. 58 func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) { 59 named, _ := obj.Type().(*types.Named) 60 if named == nil { 61 // A type name's type can also be the 62 // exported basic type unsafe.Pointer. 63 return nil, nil 64 } 65 if _, ok := named.Underlying().(*types.Interface); ok { 66 // ignore interfaces 67 return nil, nil 68 } 69 methods := combinedMethodSet(named) 70 if len(methods) == 0 { 71 return nil, nil 72 } 73 return named, methods 74 } 75 76 func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) { 77 // collect objects by kind 78 var ( 79 consts []*types.Const 80 typem []*types.Named // non-interface types with methods 81 typez []*types.TypeName // interfaces or types without methods 82 vars []*types.Var 83 funcs []*types.Func 84 builtins []*types.Builtin 85 methods = make(map[*types.Named][]*types.Selection) // method sets for named types 86 ) 87 scope := pkg.Scope() 88 for _, name := range scope.Names() { 89 obj := scope.Lookup(name) 90 if obj.Exported() { 91 // collect top-level exported and possibly filtered objects 92 if filter == nil || filter(obj) { 93 switch obj := obj.(type) { 94 case *types.Const: 95 consts = append(consts, obj) 96 case *types.TypeName: 97 // group into types with methods and types without 98 if named, m := methodsFor(obj); named != nil { 99 typem = append(typem, named) 100 methods[named] = m 101 } else { 102 typez = append(typez, obj) 103 } 104 case *types.Var: 105 vars = append(vars, obj) 106 case *types.Func: 107 funcs = append(funcs, obj) 108 case *types.Builtin: 109 // for unsafe.Sizeof, etc. 110 builtins = append(builtins, obj) 111 } 112 } 113 } else if filter == nil { 114 // no filtering: collect top-level unexported types with methods 115 if obj, _ := obj.(*types.TypeName); obj != nil { 116 // see case *types.TypeName above 117 if named, m := methodsFor(obj); named != nil { 118 typem = append(typem, named) 119 methods[named] = m 120 } 121 } 122 } 123 } 124 125 p.printf("package %s // %q\n", pkg.Name(), pkg.Path()) 126 127 p.printDecl("const", len(consts), func() { 128 for _, obj := range consts { 129 p.printObj(obj) 130 p.print("\n") 131 } 132 }) 133 134 p.printDecl("var", len(vars), func() { 135 for _, obj := range vars { 136 p.printObj(obj) 137 p.print("\n") 138 } 139 }) 140 141 p.printDecl("type", len(typez), func() { 142 for _, obj := range typez { 143 p.printf("%s ", obj.Name()) 144 typ := obj.Type() 145 if isAlias(obj) { 146 p.print("= ") 147 p.writeType(p.pkg, typ) 148 } else { 149 p.writeType(p.pkg, typ.Underlying()) 150 } 151 p.print("\n") 152 } 153 }) 154 155 // non-interface types with methods 156 for _, named := range typem { 157 first := true 158 if obj := named.Obj(); obj.Exported() { 159 if first { 160 p.print("\n") 161 first = false 162 } 163 p.printf("type %s ", obj.Name()) 164 p.writeType(p.pkg, named.Underlying()) 165 p.print("\n") 166 } 167 for _, m := range methods[named] { 168 if obj := m.Obj(); obj.Exported() { 169 if first { 170 p.print("\n") 171 first = false 172 } 173 p.printFunc(m.Recv(), obj.(*types.Func)) 174 p.print("\n") 175 } 176 } 177 } 178 179 if len(funcs) > 0 { 180 p.print("\n") 181 for _, obj := range funcs { 182 p.printFunc(nil, obj) 183 p.print("\n") 184 } 185 } 186 187 // TODO(gri) better handling of builtins (package unsafe only) 188 if len(builtins) > 0 { 189 p.print("\n") 190 for _, obj := range builtins { 191 p.printf("func %s() // builtin\n", obj.Name()) 192 } 193 } 194 195 p.print("\n") 196 } 197 198 func (p *printer) printDecl(keyword string, n int, printGroup func()) { 199 switch n { 200 case 0: 201 // nothing to do 202 case 1: 203 p.printf("\n%s ", keyword) 204 printGroup() 205 default: 206 p.printf("\n%s (\n", keyword) 207 p.indent++ 208 printGroup() 209 p.indent-- 210 p.print(")\n") 211 } 212 } 213 214 // absInt returns the absolute value of v as a *big.Int. 215 // v must be a numeric value. 216 func absInt(v constant.Value) *big.Int { 217 // compute big-endian representation of v 218 b := constant.Bytes(v) // little-endian 219 for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { 220 b[i], b[j] = b[j], b[i] 221 } 222 return new(big.Int).SetBytes(b) 223 } 224 225 var ( 226 one = big.NewRat(1, 1) 227 ten = big.NewRat(10, 1) 228 ) 229 230 // floatString returns the string representation for a 231 // numeric value v in normalized floating-point format. 232 func floatString(v constant.Value) string { 233 if constant.Sign(v) == 0 { 234 return "0.0" 235 } 236 // x != 0 237 238 // convert |v| into a big.Rat x 239 x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v))) 240 241 // normalize x and determine exponent e 242 // (This is not very efficient, but also not speed-critical.) 243 var e int 244 for x.Cmp(ten) >= 0 { 245 x.Quo(x, ten) 246 e++ 247 } 248 for x.Cmp(one) < 0 { 249 x.Mul(x, ten) 250 e-- 251 } 252 253 // TODO(gri) Values such as 1/2 are easier to read in form 0.5 254 // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as 255 // 10.0. Fine-tune best exponent range for readability. 256 257 s := x.FloatString(100) // good-enough precision 258 259 // trim trailing 0's 260 i := len(s) 261 for i > 0 && s[i-1] == '0' { 262 i-- 263 } 264 s = s[:i] 265 266 // add a 0 if the number ends in decimal point 267 if len(s) > 0 && s[len(s)-1] == '.' { 268 s += "0" 269 } 270 271 // add exponent and sign 272 if e != 0 { 273 s += fmt.Sprintf("e%+d", e) 274 } 275 if constant.Sign(v) < 0 { 276 s = "-" + s 277 } 278 279 // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator 280 // are just a small number of decimal digits), add the exact fraction as 281 // a comment. For instance: 3.3333...e-1 /* = 1/3 */ 282 283 return s 284 } 285 286 // valString returns the string representation for the value v. 287 // Setting floatFmt forces an integer value to be formatted in 288 // normalized floating-point format. 289 // TODO(gri) Move this code into package constant. 290 func valString(v constant.Value, floatFmt bool) string { 291 switch v.Kind() { 292 case constant.Int: 293 if floatFmt { 294 return floatString(v) 295 } 296 case constant.Float: 297 return floatString(v) 298 case constant.Complex: 299 re := constant.Real(v) 300 im := constant.Imag(v) 301 var s string 302 if constant.Sign(re) != 0 { 303 s = floatString(re) 304 if constant.Sign(im) >= 0 { 305 s += " + " 306 } else { 307 s += " - " 308 im = constant.UnaryOp(token.SUB, im, 0) // negate im 309 } 310 } 311 // im != 0, otherwise v would be constant.Int or constant.Float 312 return s + floatString(im) + "i" 313 } 314 return v.String() 315 } 316 317 func (p *printer) printObj(obj types.Object) { 318 p.print(obj.Name()) 319 320 typ, basic := obj.Type().Underlying().(*types.Basic) 321 if basic && typ.Info()&types.IsUntyped != 0 { 322 // don't write untyped types 323 } else { 324 p.print(" ") 325 p.writeType(p.pkg, obj.Type()) 326 } 327 328 if obj, ok := obj.(*types.Const); ok { 329 floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0 330 p.print(" = ") 331 p.print(valString(obj.Val(), floatFmt)) 332 } 333 } 334 335 func (p *printer) printFunc(recvType types.Type, obj *types.Func) { 336 p.print("func ") 337 sig := obj.Type().(*types.Signature) 338 if recvType != nil { 339 p.print("(") 340 p.writeType(p.pkg, recvType) 341 p.print(") ") 342 } 343 p.print(obj.Name()) 344 p.writeSignature(p.pkg, sig) 345 } 346 347 // combinedMethodSet returns the method set for a named type T 348 // merged with all the methods of *T that have different names than 349 // the methods of T. 350 // 351 // combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet 352 // but doesn't require a MethodSetCache. 353 // TODO(gri) If this functionality doesn't change over time, consider 354 // just calling IntuitiveMethodSet eventually. 355 func combinedMethodSet(T *types.Named) []*types.Selection { 356 // method set for T 357 mset := types.NewMethodSet(T) 358 var res []*types.Selection 359 for i, n := 0, mset.Len(); i < n; i++ { 360 res = append(res, mset.At(i)) 361 } 362 363 // add all *T methods with names different from T methods 364 pmset := types.NewMethodSet(types.NewPointer(T)) 365 for i, n := 0, pmset.Len(); i < n; i++ { 366 pm := pmset.At(i) 367 if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil { 368 res = append(res, pm) 369 } 370 } 371 372 return res 373 }