| 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/go/types/typeutil" |
| 19 | "golang.org/x/tools/internal/typeparams" |
| 20 | ) |
| 21 | |
| 22 | // NewProgram returns a new SSA Program. |
| 23 | // |
| 24 | // mode controls diagnostics and checking during SSA construction. |
| 25 | func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { |
| 26 | prog := &Program{ |
| 27 | Fset: fset, |
| 28 | imported: make(map[string]*Package), |
| 29 | packages: make(map[*types.Package]*Package), |
| 30 | thunks: make(map[selectionKey]*Function), |
| 31 | bounds: make(map[boundsKey]*Function), |
| 32 | mode: mode, |
| 33 | canon: newCanonizer(), |
| 34 | ctxt: typeparams.NewContext(), |
| 35 | instances: make(map[*Function]*instanceSet), |
| 36 | parameterized: tpWalker{seen: make(map[types.Type]bool)}, |
| 37 | } |
| 38 | |
| 39 | h := typeutil.MakeHasher() // protected by methodsMu, in effect |
| 40 | prog.methodSets.SetHasher(h) |
| 41 | prog.runtimeTypes.SetHasher(h) |
| 42 | |
| 43 | return prog |
| 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); it will be used during the build |
| 51 | // phase. |
| 52 | func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { |
| 53 | name := obj.Name() |
| 54 | switch obj := obj.(type) { |
| 55 | case *types.Builtin: |
| 56 | if pkg.Pkg != types.Unsafe { |
| 57 | panic("unexpected builtin object: " + obj.String()) |
| 58 | } |
| 59 | |
| 60 | case *types.TypeName: |
| 61 | pkg.Members[name] = &Type{ |
| 62 | object: obj, |
| 63 | pkg: pkg, |
| 64 | } |
| 65 | |
| 66 | case *types.Const: |
| 67 | c := &NamedConst{ |
| 68 | object: obj, |
| 69 | Value: NewConst(obj.Val(), obj.Type()), |
| 70 | pkg: pkg, |
| 71 | } |
| 72 | pkg.objects[obj] = c |
| 73 | pkg.Members[name] = c |
| 74 | |
| 75 | case *types.Var: |
| 76 | g := &Global{ |
| 77 | Pkg: pkg, |
| 78 | name: name, |
| 79 | object: obj, |
| 80 | typ: types.NewPointer(obj.Type()), // address |
| 81 | pos: obj.Pos(), |
| 82 | } |
| 83 | pkg.objects[obj] = g |
| 84 | pkg.Members[name] = g |
| 85 | |
| 86 | case *types.Func: |
| 87 | sig := obj.Type().(*types.Signature) |
| 88 | if sig.Recv() == nil && name == "init" { |
| 89 | pkg.ninit++ |
| 90 | name = fmt.Sprintf("init#%d", pkg.ninit) |
| 91 | } |
| 92 | |
| 93 | // Collect type parameters if this is a generic function/method. |
| 94 | var tparams *typeparams.TypeParamList |
| 95 | if rtparams := typeparams.RecvTypeParams(sig); rtparams.Len() > 0 { |
| 96 | tparams = rtparams |
| 97 | } else if sigparams := typeparams.ForSignature(sig); sigparams.Len() > 0 { |
| 98 | tparams = sigparams |
| 99 | } |
| 100 | |
| 101 | fn := &Function{ |
| 102 | name: name, |
| 103 | object: obj, |
| 104 | Signature: sig, |
| 105 | syntax: syntax, |
| 106 | pos: obj.Pos(), |
| 107 | Pkg: pkg, |
| 108 | Prog: pkg.Prog, |
| 109 | typeparams: tparams, |
| 110 | info: pkg.info, |
| 111 | } |
| 112 | pkg.created.Add(fn) |
| 113 | if syntax == nil { |
| 114 | fn.Synthetic = "loaded from gc object file" |
| 115 | } |
| 116 | if tparams.Len() > 0 { |
| 117 | fn.Prog.createInstanceSet(fn) |
| 118 | } |
| 119 | |
| 120 | pkg.objects[obj] = fn |
| 121 | if sig.Recv() == nil { |
| 122 | pkg.Members[name] = fn // package-level function |
| 123 | } |
| 124 | |
| 125 | default: // (incl. *types.Package) |
| 126 | panic("unexpected Object type: " + obj.String()) |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | // membersFromDecl populates package pkg with members for each |
| 131 | // typechecker object (var, func, const or type) associated with the |
| 132 | // specified decl. |
| 133 | func membersFromDecl(pkg *Package, decl ast.Decl) { |
| 134 | switch decl := decl.(type) { |
| 135 | case *ast.GenDecl: // import, const, type or var |
| 136 | switch decl.Tok { |
| 137 | case token.CONST: |
| 138 | for _, spec := range decl.Specs { |
| 139 | for _, id := range spec.(*ast.ValueSpec).Names { |
| 140 | if !isBlankIdent(id) { |
| 141 | memberFromObject(pkg, pkg.info.Defs[id], nil) |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | case token.VAR: |
| 147 | for _, spec := range decl.Specs { |
| 148 | for _, id := range spec.(*ast.ValueSpec).Names { |
| 149 | if !isBlankIdent(id) { |
| 150 | memberFromObject(pkg, pkg.info.Defs[id], spec) |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | case token.TYPE: |
| 156 | for _, spec := range decl.Specs { |
| 157 | id := spec.(*ast.TypeSpec).Name |
| 158 | if !isBlankIdent(id) { |
| 159 | memberFromObject(pkg, pkg.info.Defs[id], nil) |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | case *ast.FuncDecl: |
| 165 | id := decl.Name |
| 166 | if !isBlankIdent(id) { |
| 167 | memberFromObject(pkg, pkg.info.Defs[id], decl) |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | // creator tracks functions that have finished their CREATE phases. |
| 173 | // |
| 174 | // All Functions belong to the same Program. May have differing packages. |
| 175 | // |
| 176 | // creators are not thread-safe. |
| 177 | type creator []*Function |
| 178 | |
| 179 | func (c *creator) Add(fn *Function) { |
| 180 | *c = append(*c, fn) |
| 181 | } |
| 182 | func (c *creator) At(i int) *Function { return (*c)[i] } |
| 183 | func (c *creator) Len() int { return len(*c) } |
| 184 | |
| 185 | // CreatePackage constructs and returns an SSA Package from the |
| 186 | // specified type-checked, error-free file ASTs, and populates its |
| 187 | // Members mapping. |
| 188 | // |
| 189 | // importable determines whether this package should be returned by a |
| 190 | // subsequent call to ImportedPackage(pkg.Path()). |
| 191 | // |
| 192 | // The real work of building SSA form for each function is not done |
| 193 | // until a subsequent call to Package.Build(). |
| 194 | func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package { |
| 195 | p := &Package{ |
| 196 | Prog: prog, |
| 197 | Members: make(map[string]Member), |
| 198 | objects: make(map[types.Object]Member), |
| 199 | Pkg: pkg, |
| 200 | info: info, // transient (CREATE and BUILD phases) |
| 201 | files: files, // transient (CREATE and BUILD phases) |
| 202 | } |
| 203 | |
| 204 | // Add init() function. |
| 205 | p.init = &Function{ |
| 206 | name: "init", |
| 207 | Signature: new(types.Signature), |
| 208 | Synthetic: "package initializer", |
| 209 | Pkg: p, |
| 210 | Prog: prog, |
| 211 | info: p.info, |
| 212 | } |
| 213 | p.Members[p.init.name] = p.init |
| 214 | p.created.Add(p.init) |
| 215 | |
| 216 | // CREATE phase. |
| 217 | // Allocate all package members: vars, funcs, consts and types. |
| 218 | if len(files) > 0 { |
| 219 | // Go source package. |
| 220 | for _, file := range files { |
| 221 | for _, decl := range file.Decls { |
| 222 | membersFromDecl(p, decl) |
| 223 | } |
| 224 | } |
| 225 | } else { |
| 226 | // GC-compiled binary package (or "unsafe") |
| 227 | // No code. |
| 228 | // No position information. |
| 229 | scope := p.Pkg.Scope() |
| 230 | for _, name := range scope.Names() { |
| 231 | obj := scope.Lookup(name) |
| 232 | memberFromObject(p, obj, nil) |
| 233 | if obj, ok := obj.(*types.TypeName); ok { |
| 234 | if named, ok := obj.Type().(*types.Named); ok { |
| 235 | for i, n := 0, named.NumMethods(); i < n; i++ { |
| 236 | memberFromObject(p, named.Method(i), nil) |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | if prog.mode&BareInits == 0 { |
| 244 | // Add initializer guard variable. |
| 245 | initguard := &Global{ |
| 246 | Pkg: p, |
| 247 | name: "init$guard", |
| 248 | typ: types.NewPointer(tBool), |
| 249 | } |
| 250 | p.Members[initguard.Name()] = initguard |
| 251 | } |
| 252 | |
| 253 | if prog.mode&GlobalDebug != 0 { |
| 254 | p.SetDebugMode(true) |
| 255 | } |
| 256 | |
| 257 | if prog.mode&PrintPackages != 0 { |
| 258 | printMu.Lock() |
| 259 | p.WriteTo(os.Stdout) |
| 260 | printMu.Unlock() |
| 261 | } |
| 262 | |
| 263 | if importable { |
| 264 | prog.imported[p.Pkg.Path()] = p |
| 265 | } |
| 266 | prog.packages[p.Pkg] = p |
| 267 | |
| 268 | return p |
| 269 | } |
| 270 | |
| 271 | // printMu serializes printing of Packages/Functions to stdout. |
| 272 | var printMu sync.Mutex |
| 273 | |
| 274 | // AllPackages returns a new slice containing all packages in the |
| 275 | // program prog in unspecified order. |
| 276 | func (prog *Program) AllPackages() []*Package { |
| 277 | pkgs := make([]*Package, 0, len(prog.packages)) |
| 278 | for _, pkg := range prog.packages { |
| 279 | pkgs = append(pkgs, pkg) |
| 280 | } |
| 281 | return pkgs |
| 282 | } |
| 283 | |
| 284 | // ImportedPackage returns the importable Package whose PkgPath |
| 285 | // is path, or nil if no such Package has been created. |
| 286 | // |
| 287 | // A parameter to CreatePackage determines whether a package should be |
| 288 | // considered importable. For example, no import declaration can resolve |
| 289 | // to the ad-hoc main package created by 'go build foo.go'. |
| 290 | // |
| 291 | // TODO(adonovan): rethink this function and the "importable" concept; |
| 292 | // most packages are importable. This function assumes that all |
| 293 | // types.Package.Path values are unique within the ssa.Program, which is |
| 294 | // false---yet this function remains very convenient. |
| 295 | // Clients should use (*Program).Package instead where possible. |
| 296 | // SSA doesn't really need a string-keyed map of packages. |
| 297 | func (prog *Program) ImportedPackage(path string) *Package { |
| 298 | return prog.imported[path] |
| 299 | } |
| 300 |
Members