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