create.go (9215B)
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 package ssa 6 7 // This file implements the CREATE phase of SSA construction. 8 // See builder.go for explanation. 9 10 import ( 11 "fmt" 12 "go/ast" 13 "go/token" 14 "go/types" 15 "os" 16 "sync" 17 18 "golang.org/x/tools/internal/versions" 19 ) 20 21 // NewProgram returns a new SSA Program. 22 // 23 // mode controls diagnostics and checking during SSA construction. 24 // 25 // To construct an SSA program: 26 // 27 // - Call NewProgram to create an empty Program. 28 // - Call CreatePackage providing typed syntax for each package 29 // you want to build, and call it with types but not 30 // syntax for each of those package's direct dependencies. 31 // - Call [Package.Build] on each syntax package you wish to build, 32 // or [Program.Build] to build all of them. 33 // 34 // See the Example tests for simple examples. 35 func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { 36 return &Program{ 37 Fset: fset, 38 imported: make(map[string]*Package), 39 packages: make(map[*types.Package]*Package), 40 mode: mode, 41 canon: newCanonizer(), 42 ctxt: types.NewContext(), 43 } 44 } 45 46 // memberFromObject populates package pkg with a member for the 47 // typechecker object obj. 48 // 49 // For objects from Go source code, syntax is the associated syntax 50 // tree (for funcs and vars only) and goversion defines the 51 // appropriate interpretation; they will be used during the build 52 // phase. 53 func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) { 54 name := obj.Name() 55 switch obj := obj.(type) { 56 case *types.Builtin: 57 if pkg.Pkg != types.Unsafe { 58 panic("unexpected builtin object: " + obj.String()) 59 } 60 61 case *types.TypeName: 62 if name != "_" { 63 pkg.Members[name] = &Type{ 64 object: obj, 65 pkg: pkg, 66 } 67 } 68 69 case *types.Const: 70 c := &NamedConst{ 71 object: obj, 72 Value: NewConst(obj.Val(), obj.Type()), 73 pkg: pkg, 74 } 75 pkg.objects[obj] = c 76 if name != "_" { 77 pkg.Members[name] = c 78 } 79 80 case *types.Var: 81 g := &Global{ 82 Pkg: pkg, 83 name: name, 84 object: obj, 85 typ: types.NewPointer(obj.Type()), // address 86 pos: obj.Pos(), 87 } 88 pkg.objects[obj] = g 89 if name != "_" { 90 pkg.Members[name] = g 91 } 92 93 case *types.Func: 94 sig := obj.Type().(*types.Signature) 95 if sig.Recv() == nil && name == "init" { 96 pkg.ninit++ 97 name = fmt.Sprintf("init#%d", pkg.ninit) 98 } 99 fn := createFunction(pkg.Prog, obj, name, syntax, pkg.info, goversion) 100 fn.Pkg = pkg 101 pkg.created = append(pkg.created, fn) 102 pkg.objects[obj] = fn 103 if name != "_" && sig.Recv() == nil { 104 pkg.Members[name] = fn // package-level function 105 } 106 107 default: // (incl. *types.Package) 108 panic("unexpected Object type: " + obj.String()) 109 } 110 } 111 112 // createFunction creates a function or method. It supports both 113 // CreatePackage (with or without syntax) and the on-demand creation 114 // of methods in non-created packages based on their types.Func. 115 func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string) *Function { 116 sig := obj.Type().(*types.Signature) 117 118 /* declared function/method (from syntax or export data) */ 119 fn := &Function{ 120 name: name, 121 object: obj, 122 Signature: sig, 123 build: (*builder).buildFromSyntax, 124 syntax: syntax, 125 info: info, 126 goversion: goversion, 127 pos: obj.Pos(), 128 Pkg: nil, // may be set by caller 129 Prog: prog, 130 recvtypeparams: sig.RecvTypeParams(), 131 typeparams: sig.TypeParams(), 132 } 133 if fn.syntax == nil { 134 fn.Synthetic = "from type information" 135 fn.build = (*builder).buildParamsOnly 136 } 137 if fn.hasTypeParams() { 138 fn.generic = new(generic) 139 } 140 return fn 141 } 142 143 // membersFromDecl populates package pkg with members for each 144 // typechecker object (var, func, const or type) associated with the 145 // specified decl. 146 func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) { 147 switch decl := decl.(type) { 148 case *ast.GenDecl: // import, const, type or var 149 switch decl.Tok { 150 case token.CONST: 151 for _, spec := range decl.Specs { 152 for _, id := range spec.(*ast.ValueSpec).Names { 153 memberFromObject(pkg, pkg.info.Defs[id], nil, "") 154 } 155 } 156 157 case token.VAR: 158 for _, spec := range decl.Specs { 159 for _, rhs := range spec.(*ast.ValueSpec).Values { 160 pkg.initVersion[rhs] = goversion 161 } 162 for _, id := range spec.(*ast.ValueSpec).Names { 163 memberFromObject(pkg, pkg.info.Defs[id], spec, goversion) 164 } 165 } 166 167 case token.TYPE: 168 for _, spec := range decl.Specs { 169 id := spec.(*ast.TypeSpec).Name 170 memberFromObject(pkg, pkg.info.Defs[id], nil, "") 171 } 172 } 173 174 case *ast.FuncDecl: 175 id := decl.Name 176 memberFromObject(pkg, pkg.info.Defs[id], decl, goversion) 177 } 178 } 179 180 // CreatePackage creates and returns an SSA Package from the 181 // specified type-checked, error-free file ASTs, and populates its 182 // Members mapping. 183 // 184 // importable determines whether this package should be returned by a 185 // subsequent call to ImportedPackage(pkg.Path()). 186 // 187 // The real work of building SSA form for each function is not done 188 // until a subsequent call to Package.Build. 189 func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package { 190 if pkg == nil { 191 panic("nil pkg") // otherwise pkg.Scope below returns types.Universe! 192 } 193 p := &Package{ 194 Prog: prog, 195 Members: make(map[string]Member), 196 objects: make(map[types.Object]Member), 197 Pkg: pkg, 198 syntax: info != nil, 199 // transient values (cleared after Package.Build) 200 info: info, 201 files: files, 202 initVersion: make(map[ast.Expr]string), 203 } 204 205 /* synthesized package initializer */ 206 p.init = &Function{ 207 name: "init", 208 Signature: new(types.Signature), 209 Synthetic: "package initializer", 210 Pkg: p, 211 Prog: prog, 212 build: (*builder).buildPackageInit, 213 info: p.info, 214 goversion: "", // See Package.build for details. 215 } 216 p.Members[p.init.name] = p.init 217 p.created = append(p.created, p.init) 218 219 // Allocate all package members: vars, funcs, consts and types. 220 if len(files) > 0 { 221 // Go source package. 222 for _, file := range files { 223 goversion := versions.Lang(versions.FileVersion(p.info, file)) 224 for _, decl := range file.Decls { 225 membersFromDecl(p, decl, goversion) 226 } 227 } 228 } else { 229 // GC-compiled binary package (or "unsafe") 230 // No code. 231 // No position information. 232 scope := p.Pkg.Scope() 233 for _, name := range scope.Names() { 234 obj := scope.Lookup(name) 235 memberFromObject(p, obj, nil, "") 236 if obj, ok := obj.(*types.TypeName); ok { 237 // No Unalias: aliases should not duplicate methods. 238 if named, ok := obj.Type().(*types.Named); ok { 239 for i, n := 0, named.NumMethods(); i < n; i++ { 240 memberFromObject(p, named.Method(i), nil, "") 241 } 242 } 243 } 244 } 245 } 246 247 if prog.mode&BareInits == 0 { 248 // Add initializer guard variable. 249 initguard := &Global{ 250 Pkg: p, 251 name: "init$guard", 252 typ: types.NewPointer(tBool), 253 } 254 p.Members[initguard.Name()] = initguard 255 } 256 257 if prog.mode&GlobalDebug != 0 { 258 p.SetDebugMode(true) 259 } 260 261 if prog.mode&PrintPackages != 0 { 262 printMu.Lock() 263 p.WriteTo(os.Stdout) 264 printMu.Unlock() 265 } 266 267 if importable { 268 prog.imported[p.Pkg.Path()] = p 269 } 270 prog.packages[p.Pkg] = p 271 272 return p 273 } 274 275 // printMu serializes printing of Packages/Functions to stdout. 276 var printMu sync.Mutex 277 278 // AllPackages returns a new slice containing all packages created by 279 // prog.CreatePackage in unspecified order. 280 func (prog *Program) AllPackages() []*Package { 281 pkgs := make([]*Package, 0, len(prog.packages)) 282 for _, pkg := range prog.packages { 283 pkgs = append(pkgs, pkg) 284 } 285 return pkgs 286 } 287 288 // ImportedPackage returns the importable Package whose PkgPath 289 // is path, or nil if no such Package has been created. 290 // 291 // A parameter to CreatePackage determines whether a package should be 292 // considered importable. For example, no import declaration can resolve 293 // to the ad-hoc main package created by 'go build foo.go'. 294 // 295 // TODO(adonovan): rethink this function and the "importable" concept; 296 // most packages are importable. This function assumes that all 297 // types.Package.Path values are unique within the ssa.Program, which is 298 // false---yet this function remains very convenient. 299 // Clients should use (*Program).Package instead where possible. 300 // SSA doesn't really need a string-keyed map of packages. 301 // 302 // Furthermore, the graph of packages may contain multiple variants 303 // (e.g. "p" vs "p as compiled for q.test"), and each has a different 304 // view of its dependencies. 305 func (prog *Program) ImportedPackage(path string) *Package { 306 return prog.imported[path] 307 } 308 309 // SetNoReturn sets the predicate used when building the ssa.Program 310 // prog that reports whether a given function cannot return. 311 // This may be used to prune spurious control flow edges 312 // after (e.g.) log.Fatal, improving the precision of analyses. 313 // 314 // A typical implementation is the [ctrlflow.CFGs.NoReturn] method from 315 // [golang.org/x/tools/go/analysis/passes/ctrlflow]. 316 func (prog *Program) SetNoReturn(noReturn func(*types.Func) bool) { 317 prog.noReturn = noReturn 318 }