|
|
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file implements the CREATE phase of SSA construction.
// See builder.go for explanation.
import ( "fmt" "go/ast" "go/token" "go/types" "os" "sync"
"golang.org/x/tools/go/types/typeutil" )
// NewProgram returns a new SSA Program.
//
// mode controls diagnostics and checking during SSA construction.
//
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { prog := &Program{ Fset: fset, imported: make(map[string]*Package), packages: make(map[*types.Package]*Package), thunks: make(map[selectionKey]*Function), bounds: make(map[*types.Func]*Function), mode: mode, }
h := typeutil.MakeHasher() // protected by methodsMu, in effect
prog.methodSets.SetHasher(h) prog.canon.SetHasher(h)
return prog }
// memberFromObject populates package pkg with a member for the
// typechecker object obj.
//
// For objects from Go source code, syntax is the associated syntax
// tree (for funcs and vars only); it will be used during the build
// phase.
//
func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.Builtin: if pkg.Pkg != types.Unsafe { panic("unexpected builtin object: " + obj.String()) }
case *types.TypeName: pkg.Members[name] = &Type{ object: obj, pkg: pkg, }
case *types.Const: c := &NamedConst{ object: obj, Value: NewConst(obj.Val(), obj.Type()), pkg: pkg, } pkg.values[obj] = c.Value pkg.Members[name] = c
case *types.Var: g := &Global{ Pkg: pkg, name: name, object: obj, typ: types.NewPointer(obj.Type()), // address
pos: obj.Pos(), } pkg.values[obj] = g pkg.Members[name] = g
case *types.Func: sig := obj.Type().(*types.Signature) if sig.Recv() == nil && name == "init" { pkg.ninit++ name = fmt.Sprintf("init#%d", pkg.ninit) } fn := &Function{ name: name, object: obj, Signature: sig, syntax: syntax, pos: obj.Pos(), Pkg: pkg, Prog: pkg.Prog, } if syntax == nil { fn.Synthetic = "loaded from gc object file" }
pkg.values[obj] = fn if sig.Recv() == nil { pkg.Members[name] = fn // package-level function
}
default: // (incl. *types.Package)
panic("unexpected Object type: " + obj.String()) } }
// membersFromDecl populates package pkg with members for each
// typechecker object (var, func, const or type) associated with the
// specified decl.
//
func membersFromDecl(pkg *Package, decl ast.Decl) { switch decl := decl.(type) { case *ast.GenDecl: // import, const, type or var
switch decl.Tok { case token.CONST: for _, spec := range decl.Specs { for _, id := range spec.(*ast.ValueSpec).Names { if !isBlankIdent(id) { memberFromObject(pkg, pkg.info.Defs[id], nil) } } }
case token.VAR: for _, spec := range decl.Specs { for _, id := range spec.(*ast.ValueSpec).Names { if !isBlankIdent(id) { memberFromObject(pkg, pkg.info.Defs[id], spec) } } }
case token.TYPE: for _, spec := range decl.Specs { id := spec.(*ast.TypeSpec).Name if !isBlankIdent(id) { memberFromObject(pkg, pkg.info.Defs[id], nil) } } }
case *ast.FuncDecl: id := decl.Name if !isBlankIdent(id) { memberFromObject(pkg, pkg.info.Defs[id], decl) } } }
// CreatePackage constructs and returns an SSA Package from the
// specified type-checked, error-free file ASTs, and populates its
// Members mapping.
//
// importable determines whether this package should be returned by a
// subsequent call to ImportedPackage(pkg.Path()).
//
// The real work of building SSA form for each function is not done
// until a subsequent call to Package.Build().
//
func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package { p := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Pkg: pkg, info: info, // transient (CREATE and BUILD phases)
files: files, // transient (CREATE and BUILD phases)
}
// Add init() function.
p.init = &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: p, Prog: prog, } p.Members[p.init.name] = p.init
// CREATE phase.
// Allocate all package members: vars, funcs, consts and types.
if len(files) > 0 { // Go source package.
for _, file := range files { for _, decl := range file.Decls { membersFromDecl(p, decl) } } } else { // GC-compiled binary package (or "unsafe")
// No code.
// No position information.
scope := p.Pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) memberFromObject(p, obj, nil) if obj, ok := obj.(*types.TypeName); ok { if named, ok := obj.Type().(*types.Named); ok { for i, n := 0, named.NumMethods(); i < n; i++ { memberFromObject(p, named.Method(i), nil) } } } } }
if prog.mode&BareInits == 0 { // Add initializer guard variable.
initguard := &Global{ Pkg: p, name: "init$guard", typ: types.NewPointer(tBool), } p.Members[initguard.Name()] = initguard }
if prog.mode&GlobalDebug != 0 { p.SetDebugMode(true) }
if prog.mode&PrintPackages != 0 { printMu.Lock() p.WriteTo(os.Stdout) printMu.Unlock() }
if importable { prog.imported[p.Pkg.Path()] = p } prog.packages[p.Pkg] = p
return p }
// printMu serializes printing of Packages/Functions to stdout.
var printMu sync.Mutex
// AllPackages returns a new slice containing all packages in the
// program prog in unspecified order.
//
func (prog *Program) AllPackages() []*Package { pkgs := make([]*Package, 0, len(prog.packages)) for _, pkg := range prog.packages { pkgs = append(pkgs, pkg) } return pkgs }
// ImportedPackage returns the importable SSA Package whose import
// path is path, or nil if no such SSA package has been created.
//
// Not all packages are importable. For example, no import
// declaration can resolve to the x_test package created by 'go test'
// or the ad-hoc main package created 'go build foo.go'.
//
func (prog *Program) ImportedPackage(path string) *Package { return prog.imported[path] }
|