1
2
3
4
5 package ssa
6
7
8
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
22
23
24
25
26
27
28
29
30
31
32
33
34
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
47
48
49
50
51
52
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()),
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
104 }
105
106 default:
107 panic("unexpected Object type: " + obj.String())
108 }
109 }
110
111
112
113
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
118 var tparams *types.TypeParamList
119 if rtparams := sig.RecvTypeParams(); rtparams.Len() > 0 {
120 tparams = rtparams
121 } else if sigparams := sig.TypeParams(); sigparams.Len() > 0 {
122 tparams = sigparams
123 }
124
125
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,
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
151
152
153 func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
154 switch decl := decl.(type) {
155 case *ast.GenDecl:
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
188
189
190
191
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
201
202
203
204
205
206
207
208
209
210
211
212 func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
213
214 if pkg == nil {
215 panic("nil pkg")
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
224 info: info,
225 files: files,
226 initVersion: make(map[ast.Expr]string),
227 }
228
229
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: "",
239 }
240 p.Members[p.init.name] = p.init
241 p.created.Add(p.init)
242
243
244 if len(files) > 0 {
245
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
254
255
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
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
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
300 var printMu sync.Mutex
301
302
303
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 func (prog *Program) ImportedPackage(path string) *Package {
330 return prog.imported[path]
331 }
332
View as plain text