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