gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/simpler/ssa/builder_test.go (about) 1 // Copyright 2013 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 // +build go1.5 6 7 package ssa_test 8 9 import ( 10 "bytes" 11 "go/ast" 12 "go/importer" 13 "go/parser" 14 "go/token" 15 "go/types" 16 "reflect" 17 "sort" 18 "strings" 19 "testing" 20 21 "github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/ssa" 22 "github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/ssa/ssautil" 23 "golang.org/x/tools/go/loader" 24 ) 25 26 func isEmpty(f *ssa.Function) bool { return f.Blocks == nil } 27 28 // Tests that programs partially loaded from gc object files contain 29 // functions with no code for the external portions, but are otherwise ok. 30 func TestBuildPackage(t *testing.T) { 31 input := ` 32 package main 33 34 import ( 35 "bytes" 36 "io" 37 "testing" 38 ) 39 40 func main() { 41 var t testing.T 42 t.Parallel() // static call to external declared method 43 t.Fail() // static call to promoted external declared method 44 testing.Short() // static call to external package-level function 45 46 var w io.Writer = new(bytes.Buffer) 47 w.Write(nil) // interface invoke of external declared method 48 } 49 ` 50 51 // Parse the file. 52 fset := token.NewFileSet() 53 f, err := parser.ParseFile(fset, "input.go", input, 0) 54 if err != nil { 55 t.Error(err) 56 return 57 } 58 59 // Build an SSA program from the parsed file. 60 // Load its dependencies from gc binary export data. 61 mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, 62 types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions) 63 if err != nil { 64 t.Error(err) 65 return 66 } 67 68 // The main package, its direct and indirect dependencies are loaded. 69 deps := []string{ 70 // directly imported dependencies: 71 "bytes", "io", "testing", 72 // indirect dependencies mentioned by 73 // the direct imports' export data 74 "sync", "unicode", "time", 75 } 76 77 prog := mainPkg.Prog 78 all := prog.AllPackages() 79 if len(all) <= len(deps) { 80 t.Errorf("unexpected set of loaded packages: %q", all) 81 } 82 for _, path := range deps { 83 pkg := prog.ImportedPackage(path) 84 if pkg == nil { 85 t.Errorf("package not loaded: %q", path) 86 continue 87 } 88 89 // External packages should have no function bodies (except for wrappers). 90 isExt := pkg != mainPkg 91 92 // init() 93 if isExt && !isEmpty(pkg.Func("init")) { 94 t.Errorf("external package %s has non-empty init", pkg) 95 } else if !isExt && isEmpty(pkg.Func("init")) { 96 t.Errorf("main package %s has empty init", pkg) 97 } 98 99 for _, mem := range pkg.Members { 100 switch mem := mem.(type) { 101 case *ssa.Function: 102 // Functions at package level. 103 if isExt && !isEmpty(mem) { 104 t.Errorf("external function %s is non-empty", mem) 105 } else if !isExt && isEmpty(mem) { 106 t.Errorf("function %s is empty", mem) 107 } 108 109 case *ssa.Type: 110 // Methods of named types T. 111 // (In this test, all exported methods belong to *T not T.) 112 if !isExt { 113 t.Fatalf("unexpected name type in main package: %s", mem) 114 } 115 mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) 116 for i, n := 0, mset.Len(); i < n; i++ { 117 m := prog.MethodValue(mset.At(i)) 118 // For external types, only synthetic wrappers have code. 119 expExt := !strings.Contains(m.Synthetic, "wrapper") 120 if expExt && !isEmpty(m) { 121 t.Errorf("external method %s is non-empty: %s", 122 m, m.Synthetic) 123 } else if !expExt && isEmpty(m) { 124 t.Errorf("method function %s is empty: %s", 125 m, m.Synthetic) 126 } 127 } 128 } 129 } 130 } 131 132 expectedCallee := []string{ 133 "(*testing.T).Parallel", 134 "(*testing.common).Fail", 135 "testing.Short", 136 "N/A", 137 } 138 callNum := 0 139 for _, b := range mainPkg.Func("main").Blocks { 140 for _, instr := range b.Instrs { 141 switch instr := instr.(type) { 142 case ssa.CallInstruction: 143 call := instr.Common() 144 if want := expectedCallee[callNum]; want != "N/A" { 145 got := call.StaticCallee().String() 146 if want != got { 147 t.Errorf("call #%d from main.main: got callee %s, want %s", 148 callNum, got, want) 149 } 150 } 151 callNum++ 152 } 153 } 154 } 155 if callNum != 4 { 156 t.Errorf("in main.main: got %d calls, want %d", callNum, 4) 157 } 158 } 159 160 // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types. 161 func TestRuntimeTypes(t *testing.T) { 162 tests := []struct { 163 input string 164 want []string 165 }{ 166 // An exported package-level type is needed. 167 {`package A; type T struct{}; func (T) f() {}`, 168 []string{"*p.T", "p.T"}, 169 }, 170 // An unexported package-level type is not needed. 171 {`package B; type t struct{}; func (t) f() {}`, 172 nil, 173 }, 174 // Subcomponents of type of exported package-level var are needed. 175 {`package C; import "bytes"; var V struct {*bytes.Buffer}`, 176 []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"}, 177 }, 178 // Subcomponents of type of unexported package-level var are not needed. 179 {`package D; import "bytes"; var v struct {*bytes.Buffer}`, 180 nil, 181 }, 182 // Subcomponents of type of exported package-level function are needed. 183 {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`, 184 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, 185 }, 186 // Subcomponents of type of unexported package-level function are not needed. 187 {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`, 188 nil, 189 }, 190 // Subcomponents of type of exported method of uninstantiated unexported type are not needed. 191 {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`, 192 nil, 193 }, 194 // ...unless used by MakeInterface. 195 {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`, 196 []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"}, 197 }, 198 // Subcomponents of type of unexported method are not needed. 199 {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`, 200 []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"}, 201 }, 202 // Local types aren't needed. 203 {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, 204 nil, 205 }, 206 // ...unless used by MakeInterface. 207 {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, 208 []string{"*bytes.Buffer", "*p.T", "p.T"}, 209 }, 210 // Types used as operand of MakeInterface are needed. 211 {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, 212 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, 213 }, 214 // MakeInterface is optimized away when storing to a blank. 215 {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, 216 nil, 217 }, 218 } 219 for _, test := range tests { 220 // Parse the file. 221 fset := token.NewFileSet() 222 f, err := parser.ParseFile(fset, "input.go", test.input, 0) 223 if err != nil { 224 t.Errorf("test %q: %s", test.input[:15], err) 225 continue 226 } 227 228 // Create a single-file main package. 229 // Load dependencies from gc binary export data. 230 ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, 231 types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions) 232 if err != nil { 233 t.Errorf("test %q: %s", test.input[:15], err) 234 continue 235 } 236 237 var typstrs []string 238 for _, T := range ssapkg.Prog.RuntimeTypes() { 239 typstrs = append(typstrs, T.String()) 240 } 241 sort.Strings(typstrs) 242 243 if !reflect.DeepEqual(typstrs, test.want) { 244 t.Errorf("test 'package %s': got %q, want %q", 245 f.Name.Name, typstrs, test.want) 246 } 247 } 248 } 249 250 // TestInit tests that synthesized init functions are correctly formed. 251 // Bare init functions omit calls to dependent init functions and the use of 252 // an init guard. They are useful in cases where the client uses a different 253 // calling convention for init functions, or cases where it is easier for a 254 // client to analyze bare init functions. Both of these aspects are used by 255 // the llgo compiler for simpler integration with gccgo's runtime library, 256 // and to simplify the analysis whereby it deduces which stores to globals 257 // can be lowered to global initializers. 258 func TestInit(t *testing.T) { 259 tests := []struct { 260 mode ssa.BuilderMode 261 input, want string 262 }{ 263 {0, `package A; import _ "errors"; var i int = 42`, 264 `# Name: A.init 265 # Package: A 266 # Synthetic: package initializer 267 func init(): 268 0: entry P:0 S:2 269 t0 = *init$guard bool 270 if t0 goto 2 else 1 271 1: init.start P:1 S:1 272 *init$guard = true:bool 273 t1 = errors.init() () 274 *i = 42:int 275 jump 2 276 2: init.done P:2 S:0 277 return 278 279 `}, 280 {ssa.BareInits, `package B; import _ "errors"; var i int = 42`, 281 `# Name: B.init 282 # Package: B 283 # Synthetic: package initializer 284 func init(): 285 0: entry P:0 S:0 286 *i = 42:int 287 return 288 289 `}, 290 } 291 for _, test := range tests { 292 // Create a single-file main package. 293 var conf loader.Config 294 f, err := conf.ParseFile("<input>", test.input) 295 if err != nil { 296 t.Errorf("test %q: %s", test.input[:15], err) 297 continue 298 } 299 conf.CreateFromFiles(f.Name.Name, f) 300 301 lprog, err := conf.Load() 302 if err != nil { 303 t.Errorf("test 'package %s': Load: %s", f.Name.Name, err) 304 continue 305 } 306 prog := ssautil.CreateProgram(lprog, test.mode) 307 mainPkg := prog.Package(lprog.Created[0].Pkg) 308 prog.Build() 309 initFunc := mainPkg.Func("init") 310 if initFunc == nil { 311 t.Errorf("test 'package %s': no init function", f.Name.Name) 312 continue 313 } 314 315 var initbuf bytes.Buffer 316 _, err = initFunc.WriteTo(&initbuf) 317 if err != nil { 318 t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err) 319 continue 320 } 321 322 if initbuf.String() != test.want { 323 t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want) 324 } 325 } 326 } 327 328 // TestSyntheticFuncs checks that the expected synthetic functions are 329 // created, reachable, and not duplicated. 330 func TestSyntheticFuncs(t *testing.T) { 331 const input = `package P 332 type T int 333 func (T) f() int 334 func (*T) g() int 335 var ( 336 // thunks 337 a = T.f 338 b = T.f 339 c = (struct{T}).f 340 d = (struct{T}).f 341 e = (*T).g 342 f = (*T).g 343 g = (struct{*T}).g 344 h = (struct{*T}).g 345 346 // bounds 347 i = T(0).f 348 j = T(0).f 349 k = new(T).g 350 l = new(T).g 351 352 // wrappers 353 m interface{} = struct{T}{} 354 n interface{} = struct{T}{} 355 o interface{} = struct{*T}{} 356 p interface{} = struct{*T}{} 357 q interface{} = new(struct{T}) 358 r interface{} = new(struct{T}) 359 s interface{} = new(struct{*T}) 360 t interface{} = new(struct{*T}) 361 ) 362 ` 363 // Parse 364 var conf loader.Config 365 f, err := conf.ParseFile("<input>", input) 366 if err != nil { 367 t.Fatalf("parse: %v", err) 368 } 369 conf.CreateFromFiles(f.Name.Name, f) 370 371 // Load 372 lprog, err := conf.Load() 373 if err != nil { 374 t.Fatalf("Load: %v", err) 375 } 376 377 // Create and build SSA 378 prog := ssautil.CreateProgram(lprog, 0) 379 prog.Build() 380 381 // Enumerate reachable synthetic functions 382 want := map[string]string{ 383 "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int", 384 "(P.T).f$bound": "bound method wrapper for func (P.T).f() int", 385 386 "(*P.T).g$thunk": "thunk for func (*P.T).g() int", 387 "(P.T).f$thunk": "thunk for func (P.T).f() int", 388 "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int", 389 "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int", 390 391 "(*P.T).f": "wrapper for func (P.T).f() int", 392 "(*struct{*P.T}).f": "wrapper for func (P.T).f() int", 393 "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int", 394 "(*struct{P.T}).f": "wrapper for func (P.T).f() int", 395 "(*struct{P.T}).g": "wrapper for func (*P.T).g() int", 396 "(struct{*P.T}).f": "wrapper for func (P.T).f() int", 397 "(struct{*P.T}).g": "wrapper for func (*P.T).g() int", 398 "(struct{P.T}).f": "wrapper for func (P.T).f() int", 399 400 "P.init": "package initializer", 401 } 402 for fn := range ssautil.AllFunctions(prog) { 403 if fn.Synthetic == "" { 404 continue 405 } 406 name := fn.String() 407 wantDescr, ok := want[name] 408 if !ok { 409 t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic) 410 continue 411 } 412 delete(want, name) 413 414 if wantDescr != fn.Synthetic { 415 t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr) 416 } 417 } 418 for fn, descr := range want { 419 t.Errorf("want func: %q: %q", fn, descr) 420 } 421 }