golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/create.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 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, &pkg.created) 100 fn.Pkg = pkg 101 pkg.objects[obj] = fn 102 if name != "_" && sig.Recv() == nil { 103 pkg.Members[name] = fn // package-level function 104 } 105 106 default: // (incl. *types.Package) 107 panic("unexpected Object type: " + obj.String()) 108 } 109 } 110 111 // createFunction creates a function or method. It supports both 112 // CreatePackage (with or without syntax) and the on-demand creation 113 // of methods in non-created packages based on their types.Func. 114 func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string, cr *creator) *Function { 115 sig := obj.Type().(*types.Signature) 116 117 // Collect type parameters. 118 var tparams *types.TypeParamList 119 if rtparams := sig.RecvTypeParams(); rtparams.Len() > 0 { 120 tparams = rtparams // method of generic type 121 } else if sigparams := sig.TypeParams(); sigparams.Len() > 0 { 122 tparams = sigparams // generic function 123 } 124 125 /* declared function/method (from syntax or export data) */ 126 fn := &Function{ 127 name: name, 128 object: obj, 129 Signature: sig, 130 build: (*builder).buildFromSyntax, 131 syntax: syntax, 132 info: info, 133 goversion: goversion, 134 pos: obj.Pos(), 135 Pkg: nil, // may be set by caller 136 Prog: prog, 137 typeparams: tparams, 138 } 139 if fn.syntax == nil { 140 fn.Synthetic = "from type information" 141 fn.build = (*builder).buildParamsOnly 142 } 143 if tparams.Len() > 0 { 144 fn.generic = new(generic) 145 } 146 cr.Add(fn) 147 return fn 148 } 149 150 // membersFromDecl populates package pkg with members for each 151 // typechecker object (var, func, const or type) associated with the 152 // specified decl. 153 func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) { 154 switch decl := decl.(type) { 155 case *ast.GenDecl: // import, const, type or var 156 switch decl.Tok { 157 case token.CONST: 158 for _, spec := range decl.Specs { 159 for _, id := range spec.(*ast.ValueSpec).Names { 160 memberFromObject(pkg, pkg.info.Defs[id], nil, "") 161 } 162 } 163 164 case token.VAR: 165 for _, spec := range decl.Specs { 166 for _, rhs := range spec.(*ast.ValueSpec).Values { 167 pkg.initVersion[rhs] = goversion 168 } 169 for _, id := range spec.(*ast.ValueSpec).Names { 170 memberFromObject(pkg, pkg.info.Defs[id], spec, goversion) 171 } 172 } 173 174 case token.TYPE: 175 for _, spec := range decl.Specs { 176 id := spec.(*ast.TypeSpec).Name 177 memberFromObject(pkg, pkg.info.Defs[id], nil, "") 178 } 179 } 180 181 case *ast.FuncDecl: 182 id := decl.Name 183 memberFromObject(pkg, pkg.info.Defs[id], decl, goversion) 184 } 185 } 186 187 // creator tracks functions that have finished their CREATE phases. 188 // 189 // All Functions belong to the same Program. May have differing packages. 190 // 191 // creators are not thread-safe. 192 type creator []*Function 193 194 func (c *creator) Add(fn *Function) { 195 *c = append(*c, fn) 196 } 197 func (c *creator) At(i int) *Function { return (*c)[i] } 198 func (c *creator) Len() int { return len(*c) } 199 200 // CreatePackage creates and returns an SSA Package from the 201 // specified type-checked, error-free file ASTs, and populates its 202 // Members mapping. 203 // 204 // importable determines whether this package should be returned by a 205 // subsequent call to ImportedPackage(pkg.Path()). 206 // 207 // The real work of building SSA form for each function is not done 208 // until a subsequent call to Package.Build. 209 // 210 // CreatePackage should not be called after building any package in 211 // the program. 212 func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package { 213 // TODO(adonovan): assert that no package has yet been built. 214 if pkg == nil { 215 panic("nil pkg") // otherwise pkg.Scope below returns types.Universe! 216 } 217 p := &Package{ 218 Prog: prog, 219 Members: make(map[string]Member), 220 objects: make(map[types.Object]Member), 221 Pkg: pkg, 222 syntax: info != nil, 223 // transient values (cleared after Package.Build) 224 info: info, 225 files: files, 226 initVersion: make(map[ast.Expr]string), 227 } 228 229 /* synthesized package initializer */ 230 p.init = &Function{ 231 name: "init", 232 Signature: new(types.Signature), 233 Synthetic: "package initializer", 234 Pkg: p, 235 Prog: prog, 236 build: (*builder).buildPackageInit, 237 info: p.info, 238 goversion: "", // See Package.build for details. 239 } 240 p.Members[p.init.name] = p.init 241 p.created.Add(p.init) 242 243 // Allocate all package members: vars, funcs, consts and types. 244 if len(files) > 0 { 245 // Go source package. 246 for _, file := range files { 247 goversion := versions.Lang(versions.FileVersion(p.info, file)) 248 for _, decl := range file.Decls { 249 membersFromDecl(p, decl, goversion) 250 } 251 } 252 } else { 253 // GC-compiled binary package (or "unsafe") 254 // No code. 255 // No position information. 256 scope := p.Pkg.Scope() 257 for _, name := range scope.Names() { 258 obj := scope.Lookup(name) 259 memberFromObject(p, obj, nil, "") 260 if obj, ok := obj.(*types.TypeName); ok { 261 // No Unalias: aliases should not duplicate methods. 262 if named, ok := obj.Type().(*types.Named); ok { 263 for i, n := 0, named.NumMethods(); i < n; i++ { 264 memberFromObject(p, named.Method(i), nil, "") 265 } 266 } 267 } 268 } 269 } 270 271 if prog.mode&BareInits == 0 { 272 // Add initializer guard variable. 273 initguard := &Global{ 274 Pkg: p, 275 name: "init$guard", 276 typ: types.NewPointer(tBool), 277 } 278 p.Members[initguard.Name()] = initguard 279 } 280 281 if prog.mode&GlobalDebug != 0 { 282 p.SetDebugMode(true) 283 } 284 285 if prog.mode&PrintPackages != 0 { 286 printMu.Lock() 287 p.WriteTo(os.Stdout) 288 printMu.Unlock() 289 } 290 291 if importable { 292 prog.imported[p.Pkg.Path()] = p 293 } 294 prog.packages[p.Pkg] = p 295 296 return p 297 } 298 299 // printMu serializes printing of Packages/Functions to stdout. 300 var printMu sync.Mutex 301 302 // AllPackages returns a new slice containing all packages created by 303 // prog.CreatePackage in unspecified order. 304 func (prog *Program) AllPackages() []*Package { 305 pkgs := make([]*Package, 0, len(prog.packages)) 306 for _, pkg := range prog.packages { 307 pkgs = append(pkgs, pkg) 308 } 309 return pkgs 310 } 311 312 // ImportedPackage returns the importable Package whose PkgPath 313 // is path, or nil if no such Package has been created. 314 // 315 // A parameter to CreatePackage determines whether a package should be 316 // considered importable. For example, no import declaration can resolve 317 // to the ad-hoc main package created by 'go build foo.go'. 318 // 319 // TODO(adonovan): rethink this function and the "importable" concept; 320 // most packages are importable. This function assumes that all 321 // types.Package.Path values are unique within the ssa.Program, which is 322 // false---yet this function remains very convenient. 323 // Clients should use (*Program).Package instead where possible. 324 // SSA doesn't really need a string-keyed map of packages. 325 // 326 // Furthermore, the graph of packages may contain multiple variants 327 // (e.g. "p" vs "p as compiled for q.test"), and each has a different 328 // view of its dependencies. 329 func (prog *Program) ImportedPackage(path string) *Package { 330 return prog.imported[path] 331 }