src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

create.go (9215B)


      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/internal/versions"
     19 )
     20 
     21 // NewProgram returns a new SSA Program.
     22 //
     23 // mode controls diagnostics and checking during SSA construction.
     24 //
     25 // To construct an SSA program:
     26 //
     27 //   - Call NewProgram to create an empty Program.
     28 //   - Call CreatePackage providing typed syntax for each package
     29 //     you want to build, and call it with types but not
     30 //     syntax for each of those package's direct dependencies.
     31 //   - Call [Package.Build] on each syntax package you wish to build,
     32 //     or [Program.Build] to build all of them.
     33 //
     34 // See the Example tests for simple examples.
     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 // 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) and goversion defines the
     51 // appropriate interpretation; they will be used during the build
     52 // phase.
     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()), // address
     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)
    100 		fn.Pkg = pkg
    101 		pkg.created = append(pkg.created, fn)
    102 		pkg.objects[obj] = fn
    103 		if name != "_" && 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 // createFunction creates a function or method. It supports both
    113 // CreatePackage (with or without syntax) and the on-demand creation
    114 // of methods in non-created packages based on their types.Func.
    115 func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string) *Function {
    116 	sig := obj.Type().(*types.Signature)
    117 
    118 	/* declared function/method (from syntax or export data) */
    119 	fn := &Function{
    120 		name:           name,
    121 		object:         obj,
    122 		Signature:      sig,
    123 		build:          (*builder).buildFromSyntax,
    124 		syntax:         syntax,
    125 		info:           info,
    126 		goversion:      goversion,
    127 		pos:            obj.Pos(),
    128 		Pkg:            nil, // may be set by caller
    129 		Prog:           prog,
    130 		recvtypeparams: sig.RecvTypeParams(),
    131 		typeparams:     sig.TypeParams(),
    132 	}
    133 	if fn.syntax == nil {
    134 		fn.Synthetic = "from type information"
    135 		fn.build = (*builder).buildParamsOnly
    136 	}
    137 	if fn.hasTypeParams() {
    138 		fn.generic = new(generic)
    139 	}
    140 	return fn
    141 }
    142 
    143 // membersFromDecl populates package pkg with members for each
    144 // typechecker object (var, func, const or type) associated with the
    145 // specified decl.
    146 func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
    147 	switch decl := decl.(type) {
    148 	case *ast.GenDecl: // import, const, type or var
    149 		switch decl.Tok {
    150 		case token.CONST:
    151 			for _, spec := range decl.Specs {
    152 				for _, id := range spec.(*ast.ValueSpec).Names {
    153 					memberFromObject(pkg, pkg.info.Defs[id], nil, "")
    154 				}
    155 			}
    156 
    157 		case token.VAR:
    158 			for _, spec := range decl.Specs {
    159 				for _, rhs := range spec.(*ast.ValueSpec).Values {
    160 					pkg.initVersion[rhs] = goversion
    161 				}
    162 				for _, id := range spec.(*ast.ValueSpec).Names {
    163 					memberFromObject(pkg, pkg.info.Defs[id], spec, goversion)
    164 				}
    165 			}
    166 
    167 		case token.TYPE:
    168 			for _, spec := range decl.Specs {
    169 				id := spec.(*ast.TypeSpec).Name
    170 				memberFromObject(pkg, pkg.info.Defs[id], nil, "")
    171 			}
    172 		}
    173 
    174 	case *ast.FuncDecl:
    175 		id := decl.Name
    176 		memberFromObject(pkg, pkg.info.Defs[id], decl, goversion)
    177 	}
    178 }
    179 
    180 // CreatePackage creates and returns an SSA Package from the
    181 // specified type-checked, error-free file ASTs, and populates its
    182 // Members mapping.
    183 //
    184 // importable determines whether this package should be returned by a
    185 // subsequent call to ImportedPackage(pkg.Path()).
    186 //
    187 // The real work of building SSA form for each function is not done
    188 // until a subsequent call to Package.Build.
    189 func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
    190 	if pkg == nil {
    191 		panic("nil pkg") // otherwise pkg.Scope below returns types.Universe!
    192 	}
    193 	p := &Package{
    194 		Prog:    prog,
    195 		Members: make(map[string]Member),
    196 		objects: make(map[types.Object]Member),
    197 		Pkg:     pkg,
    198 		syntax:  info != nil,
    199 		// transient values (cleared after Package.Build)
    200 		info:        info,
    201 		files:       files,
    202 		initVersion: make(map[ast.Expr]string),
    203 	}
    204 
    205 	/* synthesized package initializer */
    206 	p.init = &Function{
    207 		name:      "init",
    208 		Signature: new(types.Signature),
    209 		Synthetic: "package initializer",
    210 		Pkg:       p,
    211 		Prog:      prog,
    212 		build:     (*builder).buildPackageInit,
    213 		info:      p.info,
    214 		goversion: "", // See Package.build for details.
    215 	}
    216 	p.Members[p.init.name] = p.init
    217 	p.created = append(p.created, p.init)
    218 
    219 	// Allocate all package members: vars, funcs, consts and types.
    220 	if len(files) > 0 {
    221 		// Go source package.
    222 		for _, file := range files {
    223 			goversion := versions.Lang(versions.FileVersion(p.info, file))
    224 			for _, decl := range file.Decls {
    225 				membersFromDecl(p, decl, goversion)
    226 			}
    227 		}
    228 	} else {
    229 		// GC-compiled binary package (or "unsafe")
    230 		// No code.
    231 		// No position information.
    232 		scope := p.Pkg.Scope()
    233 		for _, name := range scope.Names() {
    234 			obj := scope.Lookup(name)
    235 			memberFromObject(p, obj, nil, "")
    236 			if obj, ok := obj.(*types.TypeName); ok {
    237 				// No Unalias: aliases should not duplicate methods.
    238 				if named, ok := obj.Type().(*types.Named); ok {
    239 					for i, n := 0, named.NumMethods(); i < n; i++ {
    240 						memberFromObject(p, named.Method(i), nil, "")
    241 					}
    242 				}
    243 			}
    244 		}
    245 	}
    246 
    247 	if prog.mode&BareInits == 0 {
    248 		// Add initializer guard variable.
    249 		initguard := &Global{
    250 			Pkg:  p,
    251 			name: "init$guard",
    252 			typ:  types.NewPointer(tBool),
    253 		}
    254 		p.Members[initguard.Name()] = initguard
    255 	}
    256 
    257 	if prog.mode&GlobalDebug != 0 {
    258 		p.SetDebugMode(true)
    259 	}
    260 
    261 	if prog.mode&PrintPackages != 0 {
    262 		printMu.Lock()
    263 		p.WriteTo(os.Stdout)
    264 		printMu.Unlock()
    265 	}
    266 
    267 	if importable {
    268 		prog.imported[p.Pkg.Path()] = p
    269 	}
    270 	prog.packages[p.Pkg] = p
    271 
    272 	return p
    273 }
    274 
    275 // printMu serializes printing of Packages/Functions to stdout.
    276 var printMu sync.Mutex
    277 
    278 // AllPackages returns a new slice containing all packages created by
    279 // prog.CreatePackage in unspecified order.
    280 func (prog *Program) AllPackages() []*Package {
    281 	pkgs := make([]*Package, 0, len(prog.packages))
    282 	for _, pkg := range prog.packages {
    283 		pkgs = append(pkgs, pkg)
    284 	}
    285 	return pkgs
    286 }
    287 
    288 // ImportedPackage returns the importable Package whose PkgPath
    289 // is path, or nil if no such Package has been created.
    290 //
    291 // A parameter to CreatePackage determines whether a package should be
    292 // considered importable. For example, no import declaration can resolve
    293 // to the ad-hoc main package created by 'go build foo.go'.
    294 //
    295 // TODO(adonovan): rethink this function and the "importable" concept;
    296 // most packages are importable. This function assumes that all
    297 // types.Package.Path values are unique within the ssa.Program, which is
    298 // false---yet this function remains very convenient.
    299 // Clients should use (*Program).Package instead where possible.
    300 // SSA doesn't really need a string-keyed map of packages.
    301 //
    302 // Furthermore, the graph of packages may contain multiple variants
    303 // (e.g. "p" vs "p as compiled for q.test"), and each has a different
    304 // view of its dependencies.
    305 func (prog *Program) ImportedPackage(path string) *Package {
    306 	return prog.imported[path]
    307 }
    308 
    309 // SetNoReturn sets the predicate used when building the ssa.Program
    310 // prog that reports whether a given function cannot return.
    311 // This may be used to prune spurious control flow edges
    312 // after (e.g.) log.Fatal, improving the precision of analyses.
    313 //
    314 // A typical implementation is the [ctrlflow.CFGs.NoReturn] method from
    315 // [golang.org/x/tools/go/analysis/passes/ctrlflow].
    316 func (prog *Program) SetNoReturn(noReturn func(*types.Func) bool) {
    317 	prog.noReturn = noReturn
    318 }