mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
go.types/ssa: split the load/parse/typecheck logic off into a separate package.
PLEASE NOTE: the APIs for both "importer" and "ssa" packages will continue to evolve and both need some polishing; the key thing is that this CL splits them. The go.types/importer package contains contains the Importer, which takes care of the mechanics of loading a set of packages and type-checking them. It exposes for each package a PackageInfo containing: - the package's ASTs (i.e. the input to the typechecker) - the types.Package object - the memoization of the typechecker callbacks for identifier resolution, constant folding and expression type inference. Method-set computation (and hence bridge-method creation) is now moved to after creation of all packages: since they are no longer created in topological order, we can't guarantee the needed delegate methods exist yet. ssa.Package no longer has public TypeOf, ObjectOf, ValueOf methods. The private counterparts are valid only during the build phase. Also: - added to go/types an informative error (not crash) for an importer() returning nil without error. - removed Package.Name(), barely needed. - changed Package.String() slightly. - flag what looks like a bug in makeBridgeMethod. Will follow up. R=golang-dev, gri CC=golang-dev https://golang.org/cl/9898043
This commit is contained in:
parent
710a117e33
commit
be28dbb86f
@ -5,6 +5,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
@ -104,6 +105,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
}
|
||||
path, _ := strconv.Unquote(s.Path.Value)
|
||||
imp, err := importer(pkg.imports, path)
|
||||
if imp == nil && err == nil {
|
||||
err = errors.New("Context.Import returned niil")
|
||||
}
|
||||
if err != nil {
|
||||
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
|
||||
continue
|
||||
|
184
importer/importer.go
Normal file
184
importer/importer.go
Normal file
@ -0,0 +1,184 @@
|
||||
// Package importer defines the Importer, which loads, parses and
|
||||
// type-checks packages of Go code plus their transitive closure, and
|
||||
// retains both the ASTs and the derived facts.
|
||||
//
|
||||
// TODO(adonovan): document and test this package better, with examples.
|
||||
// Currently it's covered by the ssa/ tests.
|
||||
//
|
||||
package importer
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// An Importer's methods are not thread-safe.
|
||||
type Importer struct {
|
||||
context *Context // the client context
|
||||
Fset *token.FileSet // position info for all files seen
|
||||
Packages map[string]*PackageInfo // keys are import paths
|
||||
errors map[string]error // cache of errors by import path
|
||||
}
|
||||
|
||||
// Context specifies the supporting context for the importer.
|
||||
//
|
||||
type Context struct {
|
||||
// TypeChecker contains options relating to the type checker.
|
||||
// The Importer will override any user-supplied values for its
|
||||
// Expr, Ident, ImplicitObj and Import fields; other fields
|
||||
// will be passed through to the type checker.
|
||||
TypeChecker types.Context
|
||||
|
||||
// If Loader is non-nil, it is used to satisfy imports.
|
||||
//
|
||||
// If it is nil, binary object files produced by the gc
|
||||
// compiler will be loaded instead of source code for all
|
||||
// imported packages. Such files supply only the types of
|
||||
// package-level declarations and values of constants, but no
|
||||
// code, so this mode will not yield a whole program. It is
|
||||
// intended for analyses that perform intraprocedural analysis
|
||||
// of a single package.
|
||||
Loader SourceLoader
|
||||
}
|
||||
|
||||
// SourceLoader is the signature of a function that locates, reads and
|
||||
// parses a set of source files given an import path.
|
||||
//
|
||||
// fset is the fileset to which the ASTs should be added.
|
||||
// path is the imported path, e.g. "sync/atomic".
|
||||
//
|
||||
// On success, the function returns files, the set of ASTs produced,
|
||||
// or the first error encountered.
|
||||
//
|
||||
// The MakeGoBuildLoader utility can be used to construct a
|
||||
// SourceLoader based on go/build.
|
||||
//
|
||||
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)
|
||||
|
||||
// New returns a new, empty Importer using configuration options
|
||||
// specified by context.
|
||||
//
|
||||
func New(context *Context) *Importer {
|
||||
imp := &Importer{
|
||||
context: context,
|
||||
Fset: token.NewFileSet(),
|
||||
Packages: make(map[string]*PackageInfo),
|
||||
errors: make(map[string]error),
|
||||
}
|
||||
imp.context.TypeChecker.Import = imp.doImport
|
||||
return imp
|
||||
}
|
||||
|
||||
// doImport loads the typechecker package identified by path
|
||||
// Implements the types.Importer prototype.
|
||||
//
|
||||
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
|
||||
// Package unsafe is handled specially, and has no PackageInfo.
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
|
||||
if info, ok := imp.Packages[path]; ok {
|
||||
imports[path] = info.Pkg
|
||||
pkg = info.Pkg
|
||||
return // positive cache hit
|
||||
}
|
||||
|
||||
if err = imp.errors[path]; err != nil {
|
||||
return // negative cache hit
|
||||
}
|
||||
|
||||
// Load the source/binary for 'path', type-check it, construct
|
||||
// a PackageInfo and update our map (imp.Packages) and the
|
||||
// type-checker's map (imports).
|
||||
var info *PackageInfo
|
||||
if imp.context.Loader != nil {
|
||||
info, err = imp.LoadPackage(path)
|
||||
} else if pkg, err = types.GcImport(imports, path); err == nil {
|
||||
info = &PackageInfo{Pkg: pkg}
|
||||
imp.Packages[path] = info
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Cache success.
|
||||
pkg = info.Pkg
|
||||
imports[path] = pkg
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
// Cache failure
|
||||
imp.errors[path] = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// LoadPackage loads the package of the specified import-path,
|
||||
// performs type-checking, and returns the corresponding
|
||||
// PackageInfo.
|
||||
//
|
||||
// Not idempotent!
|
||||
// Precondition: Importer.context.Loader != nil.
|
||||
// Not thread-safe!
|
||||
// TODO(adonovan): rethink this API.
|
||||
//
|
||||
func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
|
||||
if imp.context.Loader == nil {
|
||||
panic("Importer.LoadPackage without a SourceLoader")
|
||||
}
|
||||
files, err := imp.context.Loader(imp.Fset, importPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return imp.CreateSourcePackage(importPath, files)
|
||||
}
|
||||
|
||||
// CreateSourcePackage invokes the type-checker on files and returns a
|
||||
// PackageInfo containing the resulting type-checker package, the
|
||||
// ASTs, and other type information.
|
||||
//
|
||||
// The order of files determines the package initialization order.
|
||||
//
|
||||
// importPath is the full name under which this package is known, such
|
||||
// as appears in an import declaration. e.g. "sync/atomic".
|
||||
//
|
||||
// The ParseFiles utility may be helpful for parsing a set of Go
|
||||
// source files.
|
||||
//
|
||||
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) (*PackageInfo, error) {
|
||||
info := &PackageInfo{
|
||||
Files: files,
|
||||
types: make(map[ast.Expr]types.Type),
|
||||
idents: make(map[*ast.Ident]types.Object),
|
||||
constants: make(map[ast.Expr]exact.Value),
|
||||
typecases: make(map[*ast.CaseClause]*types.Var),
|
||||
}
|
||||
tc := imp.context.TypeChecker
|
||||
tc.Expr = func(x ast.Expr, typ types.Type, val exact.Value) {
|
||||
info.types[x] = typ
|
||||
if val != nil {
|
||||
info.constants[x] = val
|
||||
}
|
||||
}
|
||||
tc.Ident = func(ident *ast.Ident, obj types.Object) {
|
||||
// Invariants:
|
||||
// - obj is non-nil.
|
||||
// - isBlankIdent(ident) <=> obj.GetType()==nil
|
||||
info.idents[ident] = obj
|
||||
}
|
||||
tc.ImplicitObj = func(node ast.Node, obj types.Object) {
|
||||
if cc, ok := node.(*ast.CaseClause); ok {
|
||||
info.typecases[cc] = obj.(*types.Var)
|
||||
}
|
||||
}
|
||||
var firstErr error
|
||||
info.Pkg, firstErr = tc.Check(importPath, imp.Fset, files...)
|
||||
tc.Expr = nil
|
||||
tc.Ident = nil
|
||||
if firstErr != nil {
|
||||
return nil, firstErr
|
||||
}
|
||||
imp.Packages[importPath] = info
|
||||
return info, nil
|
||||
}
|
@ -1,28 +1,33 @@
|
||||
package ssa
|
||||
package importer
|
||||
|
||||
// This file defines utilities for querying the results of typechecker:
|
||||
// types of expressions, values of constant expressions, referents of identifiers.
|
||||
// TODO(gri): absorb this into go/types.
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// TypeInfo contains information provided by the type checker about
|
||||
// the abstract syntax for a single package.
|
||||
type TypeInfo struct {
|
||||
fset *token.FileSet
|
||||
// PackageInfo holds the ASTs and facts derived by the type-checker
|
||||
// for a single package.
|
||||
//
|
||||
// Not mutated once constructed.
|
||||
//
|
||||
type PackageInfo struct {
|
||||
Pkg *types.Package
|
||||
Files []*ast.File // abstract syntax for the package's files
|
||||
|
||||
// Type-checker deductions.
|
||||
types map[ast.Expr]types.Type // inferred types of expressions
|
||||
constants map[ast.Expr]*Literal // values of constant expressions
|
||||
idents map[*ast.Ident]types.Object // canonical type objects for named entities
|
||||
constants map[ast.Expr]exact.Value // values of constant expressions
|
||||
idents map[*ast.Ident]types.Object // resoved objects for named entities
|
||||
typecases map[*ast.CaseClause]*types.Var // implicit vars for single-type typecases
|
||||
}
|
||||
|
||||
// TypeOf returns the type of expression e.
|
||||
// Precondition: e belongs to the package's ASTs.
|
||||
func (info *TypeInfo) TypeOf(e ast.Expr) types.Type {
|
||||
//
|
||||
func (info *PackageInfo) TypeOf(e ast.Expr) types.Type {
|
||||
// For Ident, b.types may be more specific than
|
||||
// b.obj(id.(*ast.Ident)).GetType(),
|
||||
// e.g. in the case of typeswitch.
|
||||
@ -40,21 +45,18 @@ func (info *TypeInfo) TypeOf(e ast.Expr) types.Type {
|
||||
panic("no type for expression")
|
||||
}
|
||||
|
||||
// ValueOf returns the value of expression e if it is a constant,
|
||||
// nil otherwise.
|
||||
// ValueOf returns the value of expression e if it is a constant, nil
|
||||
// otherwise.
|
||||
//
|
||||
func (info *TypeInfo) ValueOf(e ast.Expr) *Literal {
|
||||
func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value {
|
||||
return info.constants[e]
|
||||
}
|
||||
|
||||
// ObjectOf returns the typechecker object denoted by the specified id.
|
||||
// Precondition: id belongs to the package's ASTs.
|
||||
//
|
||||
func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object {
|
||||
if obj, ok := info.idents[id]; ok {
|
||||
return obj
|
||||
}
|
||||
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s", id.Name, info.fset.Position(id.Pos())))
|
||||
func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object {
|
||||
return info.idents[id]
|
||||
}
|
||||
|
||||
// IsType returns true iff expression e denotes a type.
|
||||
@ -62,10 +64,10 @@ func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object {
|
||||
// e must be a true expression, not a KeyValueExpr, or an Ident
|
||||
// appearing in a SelectorExpr or declaration.
|
||||
//
|
||||
func (info *TypeInfo) IsType(e ast.Expr) bool {
|
||||
func (info *PackageInfo) IsType(e ast.Expr) bool {
|
||||
switch e := e.(type) {
|
||||
case *ast.SelectorExpr: // pkg.Type
|
||||
if obj := info.isPackageRef(e); obj != nil {
|
||||
if obj := info.IsPackageRef(e); obj != nil {
|
||||
_, isType := obj.(*types.TypeName)
|
||||
return isType
|
||||
}
|
||||
@ -82,12 +84,12 @@ func (info *TypeInfo) IsType(e ast.Expr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isPackageRef returns the identity of the object if sel is a
|
||||
// IsPackageRef returns the identity of the object if sel is a
|
||||
// package-qualified reference to a named const, var, func or type.
|
||||
// Otherwise it returns nil.
|
||||
// Precondition: sel belongs to the package's ASTs.
|
||||
//
|
||||
func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
|
||||
func (info *PackageInfo) IsPackageRef(sel *ast.SelectorExpr) types.Object {
|
||||
if id, ok := sel.X.(*ast.Ident); ok {
|
||||
if pkg, ok := info.ObjectOf(id).(*types.Package); ok {
|
||||
return pkg.Scope().Lookup(nil, sel.Sel.Name)
|
||||
@ -96,7 +98,22 @@ func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// builtinCallSignature returns a new Signature describing the
|
||||
// TypeCaseVar returns the implicit variable created by a single-type
|
||||
// case clause in a type switch, or nil if not found.
|
||||
//
|
||||
func (info *PackageInfo) TypeCaseVar(cc *ast.CaseClause) *types.Var {
|
||||
return info.typecases[cc]
|
||||
}
|
||||
|
||||
var (
|
||||
tEface = new(types.Interface)
|
||||
tComplex64 = types.Typ[types.Complex64]
|
||||
tComplex128 = types.Typ[types.Complex128]
|
||||
tFloat32 = types.Typ[types.Float32]
|
||||
tFloat64 = types.Typ[types.Float64]
|
||||
)
|
||||
|
||||
// BuiltinCallSignature returns a new Signature describing the
|
||||
// effective type of a builtin operator for the particular call e.
|
||||
//
|
||||
// This requires ad-hoc typing rules for all variadic (append, print,
|
||||
@ -108,11 +125,11 @@ func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
|
||||
// The returned Signature is degenerate and only intended for use by
|
||||
// emitCallArgs.
|
||||
//
|
||||
func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature {
|
||||
func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature {
|
||||
var params []*types.Var
|
||||
var isVariadic bool
|
||||
|
||||
switch builtin := noparens(e.Fun).(*ast.Ident).Name; builtin {
|
||||
switch builtin := unparen(e.Fun).(*ast.Ident).Name; builtin {
|
||||
case "append":
|
||||
var t0, t1 types.Type
|
||||
t0 = info.TypeOf(e) // infer arg[0] type from result type
|
@ -1,8 +1,7 @@
|
||||
package ssa
|
||||
package importer
|
||||
|
||||
// This file defines an implementation of the types.Importer interface
|
||||
// (func) that loads the transitive closure of dependencies of a
|
||||
// "main" package.
|
||||
// This file defines various utility functions exposed by the package
|
||||
// and used by it.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -13,60 +12,44 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// Prototype of a function that locates, reads and parses a set of
|
||||
// source files given an import path.
|
||||
// CreatePackageFromArgs builds an initial Package from a list of
|
||||
// command-line arguments.
|
||||
// If args is a list of *.go files, they are parsed and type-checked.
|
||||
// If args is a Go package import path, that package is imported.
|
||||
// The rest result contains the suffix of args that were not consumed.
|
||||
//
|
||||
// fset is the fileset to which the ASTs should be added.
|
||||
// path is the imported path, e.g. "sync/atomic".
|
||||
// This utility is provided to facilitate construction of command-line
|
||||
// tools with a consistent user interface.
|
||||
//
|
||||
// On success, the function returns files, the set of ASTs produced,
|
||||
// or the first error encountered.
|
||||
//
|
||||
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)
|
||||
func CreatePackageFromArgs(imp *Importer, args []string) (info *PackageInfo, rest []string, err error) {
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
return nil, nil, errors.New("No *.go source files nor package name was specified.")
|
||||
|
||||
// doImport loads the typechecker package identified by path
|
||||
// Implements the types.Importer prototype.
|
||||
//
|
||||
func (b *Builder) doImport(imports map[string]*types.Package, path string) (typkg *types.Package, err error) {
|
||||
// Package unsafe is handled specially, and has no ssa.Package.
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
|
||||
if pkg := b.Prog.Packages[path]; pkg != nil {
|
||||
typkg = pkg.Types
|
||||
imports[path] = typkg
|
||||
return // positive cache hit
|
||||
}
|
||||
|
||||
if err = b.importErrs[path]; err != nil {
|
||||
return // negative cache hit
|
||||
}
|
||||
var files []*ast.File
|
||||
var info *TypeInfo
|
||||
if b.Context.Mode&UseGCImporter != 0 {
|
||||
typkg, err = types.GcImport(imports, path)
|
||||
} else {
|
||||
files, err = b.Context.Loader(b.Prog.Files, path)
|
||||
if err == nil {
|
||||
typkg, info, err = b.typecheck(path, files)
|
||||
case strings.HasSuffix(args[0], ".go"):
|
||||
// % tool a.go b.go ...
|
||||
// Leading consecutive *.go arguments constitute main package.
|
||||
i := 1
|
||||
for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ {
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// Cache failure
|
||||
b.importErrs[path] = err
|
||||
return nil, err
|
||||
var files []*ast.File
|
||||
files, err = ParseFiles(imp.Fset, ".", args[:i]...)
|
||||
rest = args[i:]
|
||||
if err == nil {
|
||||
info, err = imp.CreateSourcePackage("main", files)
|
||||
}
|
||||
|
||||
default:
|
||||
// % tool my/package ...
|
||||
// First argument is import path of main package.
|
||||
pkgname := args[0]
|
||||
info, err = imp.LoadPackage(pkgname)
|
||||
rest = args[1:]
|
||||
}
|
||||
|
||||
// Cache success
|
||||
imports[path] = typkg // cache for just this package.
|
||||
b.Prog.Packages[path] = b.createPackageImpl(typkg, path, files, info) // cache across all packages
|
||||
|
||||
return typkg, nil
|
||||
return
|
||||
}
|
||||
|
||||
// MakeGoBuildLoader returns an implementation of the SourceLoader
|
||||
@ -121,42 +104,20 @@ func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast
|
||||
return
|
||||
}
|
||||
|
||||
// CreatePackageFromArgs builds an initial Package from a list of
|
||||
// command-line arguments.
|
||||
// If args is a list of *.go files, they are parsed and type-checked.
|
||||
// If args is a Go package import path, that package is imported.
|
||||
// rest is the suffix of args that were not consumed.
|
||||
//
|
||||
// This utility is provided to facilitate construction of command-line
|
||||
// tools with a consistent user interface.
|
||||
//
|
||||
func CreatePackageFromArgs(builder *Builder, args []string) (pkg *Package, rest []string, err error) {
|
||||
var pkgname string
|
||||
var files []*ast.File
|
||||
// ---------- Internal helpers ----------
|
||||
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
err = errors.New("No *.go source files nor package name was specified.")
|
||||
|
||||
case strings.HasSuffix(args[0], ".go"):
|
||||
// % tool a.go b.go ...
|
||||
// Leading consecutive *.go arguments constitute main package.
|
||||
pkgname = "main"
|
||||
i := 1
|
||||
for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ {
|
||||
// unparen returns e with any enclosing parentheses stripped.
|
||||
func unparen(e ast.Expr) ast.Expr {
|
||||
for {
|
||||
p, ok := e.(*ast.ParenExpr)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
files, err = ParseFiles(builder.Prog.Files, ".", args[:i]...)
|
||||
rest = args[i:]
|
||||
|
||||
default:
|
||||
// % tool my/package ...
|
||||
// First argument is import path of main package.
|
||||
pkgname = args[0]
|
||||
rest = args[1:]
|
||||
files, err = builder.Context.Loader(builder.Prog.Files, pkgname)
|
||||
e = p.X
|
||||
}
|
||||
if err == nil {
|
||||
pkg, err = builder.CreatePackage(pkgname, files)
|
||||
}
|
||||
return
|
||||
return e
|
||||
}
|
||||
|
||||
func unreachable() {
|
||||
panic("unreachable")
|
||||
}
|
403
ssa/builder.go
403
ssa/builder.go
@ -32,6 +32,7 @@ import (
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/importer"
|
||||
)
|
||||
|
||||
type opaqueType struct {
|
||||
@ -47,10 +48,6 @@ var (
|
||||
// Type constants.
|
||||
tBool = types.Typ[types.Bool]
|
||||
tByte = types.Typ[types.Byte]
|
||||
tFloat32 = types.Typ[types.Float32]
|
||||
tFloat64 = types.Typ[types.Float64]
|
||||
tComplex64 = types.Typ[types.Complex64]
|
||||
tComplex128 = types.Typ[types.Complex128]
|
||||
tInt = types.Typ[types.Int]
|
||||
tInvalid = types.Typ[types.Invalid]
|
||||
tUntypedNil = types.Typ[types.UntypedNil]
|
||||
@ -72,33 +69,10 @@ var (
|
||||
|
||||
// A Context specifies the supporting context for SSA construction.
|
||||
//
|
||||
// TODO(adonovan): make it so empty => default behaviours?
|
||||
// Currently not the case for Loader.
|
||||
//
|
||||
type Context struct {
|
||||
// Mode is a bitfield of options controlling verbosity,
|
||||
// logging and additional sanity checks.
|
||||
Mode BuilderMode
|
||||
|
||||
// Loader is a SourceLoader function that finds, loads and
|
||||
// parses Go source files for a given import path. (It is
|
||||
// ignored if the mode bits include UseGCImporter.)
|
||||
// See (e.g.) MakeGoBuildLoader.
|
||||
Loader SourceLoader
|
||||
|
||||
// RetainAST is an optional user predicate that determines
|
||||
// whether to retain (true) or discard (false) the AST and its
|
||||
// type information for each package after BuildPackage has
|
||||
// finished.
|
||||
// Implementations must be thread-safe.
|
||||
// If RetainAST is nil, all ASTs and TypeInfos are discarded.
|
||||
RetainAST func(*Package) bool
|
||||
|
||||
// TypeChecker contains options relating to the type checker.
|
||||
// The SSA Builder will override any user-supplied values for
|
||||
// its Expr, Ident and Import fields; other fields will be
|
||||
// passed through to the type checker.
|
||||
TypeChecker types.Context
|
||||
}
|
||||
|
||||
// BuilderMode is a bitmask of options for diagnostics and checking.
|
||||
@ -109,63 +83,52 @@ const (
|
||||
LogFunctions // Dump function SSA code to stderr
|
||||
LogSource // Show source locations as SSA builder progresses
|
||||
SanityCheckFunctions // Perform sanity checking of function bodies
|
||||
UseGCImporter // Ignore SourceLoader; use gc-compiled object code for all imports
|
||||
NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
|
||||
BuildSerially // Build packages serially, not in parallel.
|
||||
)
|
||||
|
||||
// A Builder creates the SSA representation of a single program.
|
||||
// Instances may be created using NewBuilder.
|
||||
// A Builder builds the SSA representation of a single Go program: a
|
||||
// Program containing one or more Packages.
|
||||
//
|
||||
// The SSA Builder constructs a Program containing Package instances
|
||||
// for packages of Go source code, loading, parsing and recursively
|
||||
// constructing packages for all imported dependencies as well.
|
||||
//
|
||||
// If the UseGCImporter mode flag is specified, binary object files
|
||||
// produced by the gc compiler will be loaded instead of source code
|
||||
// for all imported packages. Such files supply only the types of
|
||||
// package-level declarations and values of constants, but no code, so
|
||||
// this mode will not yield a whole program. It is intended for
|
||||
// analyses that perform intraprocedural analysis of a single package.
|
||||
//
|
||||
// A typical client will create a Builder with NewBuilder; call
|
||||
// CreatePackage for the "root" package(s), e.g. main; then call
|
||||
// BuildPackage on the same set of packages to construct SSA-form code
|
||||
// for functions and methods. After that, the representation of the
|
||||
// program (Builder.Prog) is complete and transitively closed, and the
|
||||
// Builder object can be discarded to reclaim its memory. The
|
||||
// client's analysis may then begin.
|
||||
// Use NewBuilder to create a Builder.
|
||||
//
|
||||
type Builder struct {
|
||||
Prog *Program // the program being built
|
||||
Context *Context // the client context
|
||||
|
||||
importErrs map[string]error // across-packages import cache of failures
|
||||
packages map[*types.Package]*Package // SSA packages by types.Package
|
||||
globals map[types.Object]Value // all package-level funcs and vars, and universal built-ins
|
||||
imp *importer.Importer // ASTs and types for loaded packages
|
||||
packages map[*types.Package]*Package // SSA packages by types.Package
|
||||
globals map[types.Object]Value // all package-level funcs and vars, and universal built-ins
|
||||
}
|
||||
|
||||
// NewBuilder creates and returns a new SSA builder with options
|
||||
// specified by context.
|
||||
//
|
||||
func NewBuilder(context *Context) *Builder {
|
||||
// For each package loaded by imp, a new SSA Package is created for it
|
||||
// and added to the Program. All such packages must be error-free.
|
||||
//
|
||||
// A typical client will create a Builder with NewBuilder; this causes
|
||||
// all SSA Packages to be created; then call BuildPackage or
|
||||
// BuildAllPackages to construct SSA-form code for all functions and
|
||||
// methods in one or more packages. After that, the representation of
|
||||
// the program (Builder.Prog) is complete and transitively closed, and
|
||||
// the Builder and Importer objects can be discarded. The client's
|
||||
// analysis may then begin.
|
||||
//
|
||||
func NewBuilder(context *Context, imp *importer.Importer) *Builder {
|
||||
b := &Builder{
|
||||
Prog: &Program{
|
||||
Files: token.NewFileSet(),
|
||||
Files: imp.Fset,
|
||||
Packages: make(map[string]*Package),
|
||||
Builtins: make(map[types.Object]*Builtin),
|
||||
methodSets: make(map[types.Type]MethodSet),
|
||||
concreteMethods: make(map[*types.Func]*Function),
|
||||
mode: context.Mode,
|
||||
},
|
||||
Context: context,
|
||||
globals: make(map[types.Object]Value),
|
||||
importErrs: make(map[string]error),
|
||||
packages: make(map[*types.Package]*Package),
|
||||
}
|
||||
|
||||
b.Context.TypeChecker.Import = func(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
|
||||
return b.doImport(imports, path)
|
||||
Context: context,
|
||||
imp: imp,
|
||||
globals: make(map[types.Object]Value),
|
||||
packages: make(map[*types.Package]*Package),
|
||||
}
|
||||
|
||||
// Create Values for built-in functions.
|
||||
@ -177,9 +140,39 @@ func NewBuilder(context *Context) *Builder {
|
||||
b.Prog.Builtins[obj] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Create ssa.Package for each types.Package.
|
||||
for path, info := range imp.Packages {
|
||||
p := b.createPackage(info)
|
||||
b.Prog.Packages[path] = p
|
||||
b.packages[p.Types] = p
|
||||
}
|
||||
|
||||
// Compute the method sets, now that we have all packages' methods.
|
||||
for _, pkg := range b.Prog.Packages {
|
||||
for _, mem := range pkg.Members {
|
||||
switch t := mem.(type) {
|
||||
case *Type:
|
||||
t.Methods = b.Prog.MethodSet(t.NamedType)
|
||||
t.PtrMethods = b.Prog.MethodSet(pointer(t.NamedType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// PackageFor returns the ssa.Package corresponding to the specified
|
||||
// types.Package, or nil if this builder has not created such a
|
||||
// package.
|
||||
//
|
||||
// TODO(adonovan): better name?
|
||||
// TODO(adonovan): can we make this a method of Program?
|
||||
//
|
||||
func (b *Builder) PackageFor(p *types.Package) *Package {
|
||||
return b.packages[p]
|
||||
}
|
||||
|
||||
// lookup returns the package-level *Function or *Global (or universal
|
||||
// *Builtin) for the named object obj.
|
||||
//
|
||||
@ -325,11 +318,11 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value {
|
||||
// cases for single-valued CallExpr.
|
||||
var c Call
|
||||
b.setCall(fn, e, &c.Call)
|
||||
c.typ = fn.Pkg.TypeOf(e)
|
||||
c.typ = fn.Pkg.typeOf(e)
|
||||
return fn.emit(&c)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map)
|
||||
mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
|
||||
typ = mapt.Elem()
|
||||
lookup := &Lookup{
|
||||
X: b.expr(fn, e.X),
|
||||
@ -340,10 +333,10 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value {
|
||||
tuple = fn.emit(lookup)
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.TypeOf(e))
|
||||
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e))
|
||||
|
||||
case *ast.UnaryExpr: // must be receive <-
|
||||
typ = fn.Pkg.TypeOf(e.X).Underlying().(*types.Chan).Elem()
|
||||
typ = fn.Pkg.typeOf(e.X).Underlying().(*types.Chan).Elem()
|
||||
unop := &UnOp{
|
||||
Op: token.ARROW,
|
||||
X: b.expr(fn, e.X),
|
||||
@ -425,7 +418,7 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
|
||||
// We must still evaluate the value, though. (If it
|
||||
// was side-effect free, the whole call would have
|
||||
// been constant-folded.)
|
||||
t := fn.Pkg.TypeOf(args[0]).Deref().Underlying()
|
||||
t := fn.Pkg.typeOf(args[0]).Deref().Underlying()
|
||||
if at, ok := t.(*types.Array); ok {
|
||||
b.expr(fn, args[0]) // for effects only
|
||||
return intLiteral(at.Len())
|
||||
@ -459,12 +452,12 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
|
||||
Bindings: []Value{recv},
|
||||
}
|
||||
c.setPos(e.Sel.Pos())
|
||||
c.setType(fn.Pkg.TypeOf(e))
|
||||
c.setType(fn.Pkg.typeOf(e))
|
||||
return fn.emit(c)
|
||||
}
|
||||
}
|
||||
|
||||
st := fn.Pkg.TypeOf(e.X).Deref().Underlying().(*types.Struct)
|
||||
st := fn.Pkg.typeOf(e.X).Deref().Underlying().(*types.Struct)
|
||||
index := -1
|
||||
for i, n := 0, st.NumFields(); i < n; i++ {
|
||||
f := st.Field(i)
|
||||
@ -481,7 +474,7 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
|
||||
panic("field not found, even with promotion: " + e.Sel.Name)
|
||||
}
|
||||
}
|
||||
fieldType := fn.Pkg.TypeOf(e)
|
||||
fieldType := fn.Pkg.typeOf(e)
|
||||
pos := e.Sel.Pos()
|
||||
if wantAddr {
|
||||
return b.fieldAddr(fn, e.X, path, index, fieldType, pos, escaping)
|
||||
@ -506,7 +499,7 @@ func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, in
|
||||
x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos)
|
||||
}
|
||||
} else {
|
||||
switch fn.Pkg.TypeOf(base).Underlying().(type) {
|
||||
switch fn.Pkg.typeOf(base).Underlying().(type) {
|
||||
case *types.Struct:
|
||||
x = b.addr(fn, base, escaping).(address).addr
|
||||
case *types.Pointer:
|
||||
@ -584,7 +577,7 @@ func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, in
|
||||
func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
||||
switch e := e.(type) {
|
||||
case *ast.Ident:
|
||||
obj := fn.Pkg.ObjectOf(e)
|
||||
obj := fn.Pkg.objectOf(e)
|
||||
v, ok := b.lookup(fn.Pkg, obj) // var (address)
|
||||
if !ok {
|
||||
v = fn.lookup(obj, escaping)
|
||||
@ -592,7 +585,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
||||
return address{addr: v}
|
||||
|
||||
case *ast.CompositeLit:
|
||||
t := fn.Pkg.TypeOf(e).Deref()
|
||||
t := fn.Pkg.typeOf(e).Deref()
|
||||
var v Value
|
||||
if escaping {
|
||||
v = emitNew(fn, t, e.Lbrace)
|
||||
@ -607,7 +600,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
// p.M where p is a package.
|
||||
if obj := fn.Pkg.isPackageRef(e); obj != nil {
|
||||
if obj := fn.Pkg.info.IsPackageRef(e); obj != nil {
|
||||
if v, ok := b.lookup(fn.Pkg, obj); ok {
|
||||
return address{addr: v}
|
||||
}
|
||||
@ -620,7 +613,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
||||
case *ast.IndexExpr:
|
||||
var x Value
|
||||
var et types.Type
|
||||
switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) {
|
||||
switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
|
||||
case *types.Array:
|
||||
x = b.addr(fn, e.X, escaping).(address).addr
|
||||
et = pointer(t.Elem())
|
||||
@ -688,8 +681,8 @@ func (b *Builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
|
||||
// to fn and returning the Value defined by the expression.
|
||||
//
|
||||
func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
if lit := fn.Pkg.ValueOf(e); lit != nil {
|
||||
return lit
|
||||
if v := fn.Pkg.info.ValueOf(e); v != nil {
|
||||
return newLiteral(v, fn.Pkg.typeOf(e))
|
||||
}
|
||||
|
||||
switch e := e.(type) {
|
||||
@ -700,7 +693,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
posn := b.Prog.Files.Position(e.Type.Func)
|
||||
fn2 := &Function{
|
||||
name: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column),
|
||||
Signature: fn.Pkg.TypeOf(e.Type).Underlying().(*types.Signature),
|
||||
Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
|
||||
pos: e.Type.Func,
|
||||
Enclosing: fn,
|
||||
Pkg: fn.Pkg,
|
||||
@ -717,7 +710,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
return fn2
|
||||
}
|
||||
v := &MakeClosure{Fn: fn2}
|
||||
v.setType(fn.Pkg.TypeOf(e))
|
||||
v.setType(fn.Pkg.typeOf(e))
|
||||
for _, fv := range fn2.FreeVars {
|
||||
v.Bindings = append(v.Bindings, fv.outer)
|
||||
fv.outer = nil
|
||||
@ -728,11 +721,11 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
return b.expr(fn, e.X)
|
||||
|
||||
case *ast.TypeAssertExpr: // single-result form only
|
||||
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.TypeOf(e))
|
||||
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e))
|
||||
|
||||
case *ast.CallExpr:
|
||||
typ := fn.Pkg.TypeOf(e)
|
||||
if fn.Pkg.IsType(e.Fun) {
|
||||
typ := fn.Pkg.typeOf(e)
|
||||
if fn.Pkg.info.IsType(e.Fun) {
|
||||
// Explicit type conversion, e.g. string(x) or big.Int(x)
|
||||
x := b.expr(fn, e.Args[0])
|
||||
y := emitConv(fn, x, typ)
|
||||
@ -750,7 +743,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
}
|
||||
// Call to "intrinsic" built-ins, e.g. new, make, panic.
|
||||
if id, ok := e.Fun.(*ast.Ident); ok {
|
||||
obj := fn.Pkg.ObjectOf(id)
|
||||
obj := fn.Pkg.objectOf(id)
|
||||
if _, ok := fn.Prog.Builtins[obj]; ok {
|
||||
if v := b.builtin(fn, id.Name, e.Args, typ, e.Lparen); v != nil {
|
||||
return v
|
||||
@ -775,7 +768,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
X: b.expr(fn, e.X),
|
||||
}
|
||||
v.setPos(e.OpPos)
|
||||
v.setType(fn.Pkg.TypeOf(e))
|
||||
v.setType(fn.Pkg.typeOf(e))
|
||||
return fn.emit(v)
|
||||
default:
|
||||
panic(e.Op)
|
||||
@ -788,7 +781,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
case token.SHL, token.SHR:
|
||||
fallthrough
|
||||
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
|
||||
return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.Pkg.TypeOf(e), e.OpPos)
|
||||
return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.Pkg.typeOf(e), e.OpPos)
|
||||
|
||||
case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
|
||||
return emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos)
|
||||
@ -799,7 +792,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
case *ast.SliceExpr:
|
||||
var low, high Value
|
||||
var x Value
|
||||
switch fn.Pkg.TypeOf(e.X).Underlying().(type) {
|
||||
switch fn.Pkg.typeOf(e.X).Underlying().(type) {
|
||||
case *types.Array:
|
||||
// Potentially escaping.
|
||||
x = b.addr(fn, e.X, true).(address).addr
|
||||
@ -819,11 +812,11 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
Low: low,
|
||||
High: high,
|
||||
}
|
||||
v.setType(fn.Pkg.TypeOf(e))
|
||||
v.setType(fn.Pkg.typeOf(e))
|
||||
return fn.emit(v)
|
||||
|
||||
case *ast.Ident:
|
||||
obj := fn.Pkg.ObjectOf(e)
|
||||
obj := fn.Pkg.objectOf(e)
|
||||
// Global or universal?
|
||||
if v, ok := b.lookup(fn.Pkg, obj); ok {
|
||||
if _, ok := obj.(*types.Var); ok {
|
||||
@ -836,14 +829,14 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
// p.M where p is a package.
|
||||
if obj := fn.Pkg.isPackageRef(e); obj != nil {
|
||||
if obj := fn.Pkg.info.IsPackageRef(e); obj != nil {
|
||||
return b.expr(fn, e.Sel)
|
||||
}
|
||||
|
||||
// (*T).f or T.f, the method f from the method-set of type T.
|
||||
if fn.Pkg.IsType(e.X) {
|
||||
if fn.Pkg.info.IsType(e.X) {
|
||||
id := MakeId(e.Sel.Name, fn.Pkg.Types)
|
||||
typ := fn.Pkg.TypeOf(e.X)
|
||||
typ := fn.Pkg.typeOf(e.X)
|
||||
if m := b.Prog.MethodSet(typ)[id]; m != nil {
|
||||
return m
|
||||
}
|
||||
@ -856,7 +849,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
return b.selector(fn, e, false, false)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) {
|
||||
switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
|
||||
case *types.Array:
|
||||
// Non-addressable array (in a register).
|
||||
v := &Index{
|
||||
@ -868,7 +861,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
|
||||
case *types.Map:
|
||||
// Maps are not addressable.
|
||||
mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map)
|
||||
mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
|
||||
v := &Lookup{
|
||||
X: b.expr(fn, e.X),
|
||||
Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
|
||||
@ -918,7 +911,7 @@ func (b *Builder) stmtList(fn *Function, list []ast.Stmt) {
|
||||
// findMethod returns (nil, nil) if no such method was found.
|
||||
//
|
||||
func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Value) {
|
||||
typ := fn.Pkg.TypeOf(base)
|
||||
typ := fn.Pkg.typeOf(base)
|
||||
|
||||
// Consult method-set of X.
|
||||
if m := b.Prog.MethodSet(typ)[id]; m != nil {
|
||||
@ -954,7 +947,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||
c.HasEllipsis = e.Ellipsis != 0
|
||||
|
||||
// Is the call of the form x.f()?
|
||||
sel, ok := noparens(e.Fun).(*ast.SelectorExpr)
|
||||
sel, ok := unparen(e.Fun).(*ast.SelectorExpr)
|
||||
|
||||
// Case 0: e.Fun evaluates normally to a function.
|
||||
if !ok {
|
||||
@ -963,7 +956,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||
}
|
||||
|
||||
// Case 1: call of form x.F() where x is a package name.
|
||||
if obj := fn.Pkg.isPackageRef(sel); obj != nil {
|
||||
if obj := fn.Pkg.info.IsPackageRef(sel); obj != nil {
|
||||
// This is a specialization of expr(ast.Ident(obj)).
|
||||
if v, ok := b.lookup(fn.Pkg, obj); ok {
|
||||
if _, ok := v.(*Function); !ok {
|
||||
@ -980,7 +973,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||
// an interface. Treat like case 0.
|
||||
// TODO(adonovan): opt: inline expr() here, to make the call static
|
||||
// and to avoid generation of a stub for an interface method.
|
||||
if fn.Pkg.IsType(sel.X) {
|
||||
if fn.Pkg.info.IsType(sel.X) {
|
||||
c.Func = b.expr(fn, e.Fun)
|
||||
return
|
||||
}
|
||||
@ -998,7 +991,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||
return
|
||||
}
|
||||
|
||||
switch t := fn.Pkg.TypeOf(sel.X).Underlying().(type) {
|
||||
switch t := fn.Pkg.typeOf(sel.X).Underlying().(type) {
|
||||
case *types.Struct, *types.Pointer:
|
||||
// Case 3: x.f() where x.f is a function value in a
|
||||
// struct field f; not a method call. f is a 'var'
|
||||
@ -1102,9 +1095,9 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||
b.setCallFunc(fn, e, c)
|
||||
|
||||
// Then append the other actual parameters.
|
||||
sig, _ := fn.Pkg.TypeOf(e.Fun).Underlying().(*types.Signature)
|
||||
sig, _ := fn.Pkg.typeOf(e.Fun).Underlying().(*types.Signature)
|
||||
if sig == nil {
|
||||
sig = builtinCallSignature(fn.Pkg.TypeInfo, e)
|
||||
sig = fn.Pkg.info.BuiltinCallSignature(e)
|
||||
}
|
||||
c.Args = b.emitCallArgs(fn, sig, e, c.Args)
|
||||
}
|
||||
@ -1180,7 +1173,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
|
||||
var lval lvalue = blank{}
|
||||
if g != nil {
|
||||
// Mode A: initialized only a single global, g
|
||||
if isBlankIdent(id) || init.Pkg.ObjectOf(id) != obj {
|
||||
if isBlankIdent(id) || init.Pkg.objectOf(id) != obj {
|
||||
continue
|
||||
}
|
||||
g.spec = nil
|
||||
@ -1188,7 +1181,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
|
||||
} else {
|
||||
// Mode B: initialize all globals.
|
||||
if !isBlankIdent(id) {
|
||||
g2 := b.globals[init.Pkg.ObjectOf(id)].(*Global)
|
||||
g2 := b.globals[init.Pkg.objectOf(id)].(*Global)
|
||||
if g2.spec == nil {
|
||||
continue // already done
|
||||
}
|
||||
@ -1222,7 +1215,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
|
||||
result := tuple.Type().(*types.Tuple)
|
||||
for i, id := range spec.Names {
|
||||
if !isBlankIdent(id) {
|
||||
g := b.globals[init.Pkg.ObjectOf(id)].(*Global)
|
||||
g := b.globals[init.Pkg.objectOf(id)].(*Global)
|
||||
g.spec = nil // just an optimization
|
||||
emitStore(init, g, emitExtract(init, tuple, i, result.At(i).Type()))
|
||||
}
|
||||
@ -1246,7 +1239,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
||||
for i, id := range spec.Names {
|
||||
var lval lvalue = blank{}
|
||||
if !isBlankIdent(id) {
|
||||
lval = address{addr: fn.addNamedLocal(fn.Pkg.ObjectOf(id))}
|
||||
lval = address{addr: fn.addNamedLocal(fn.Pkg.objectOf(id))}
|
||||
}
|
||||
b.exprInPlace(fn, lval, spec.Values[i])
|
||||
}
|
||||
@ -1256,7 +1249,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
||||
// Locals are implicitly zero-initialized.
|
||||
for _, id := range spec.Names {
|
||||
if !isBlankIdent(id) {
|
||||
fn.addNamedLocal(fn.Pkg.ObjectOf(id))
|
||||
fn.addNamedLocal(fn.Pkg.objectOf(id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1266,7 +1259,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
||||
result := tuple.Type().(*types.Tuple)
|
||||
for i, id := range spec.Names {
|
||||
if !isBlankIdent(id) {
|
||||
lhs := fn.addNamedLocal(fn.Pkg.ObjectOf(id))
|
||||
lhs := fn.addNamedLocal(fn.Pkg.objectOf(id))
|
||||
emitStore(fn, lhs, emitExtract(fn, tuple, i, result.At(i).Type()))
|
||||
}
|
||||
}
|
||||
@ -1287,7 +1280,7 @@ func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
|
||||
if isDef {
|
||||
// Local may be "redeclared" in the same
|
||||
// scope, so don't blindly create anew.
|
||||
obj := fn.Pkg.ObjectOf(lhs.(*ast.Ident))
|
||||
obj := fn.Pkg.objectOf(lhs.(*ast.Ident))
|
||||
if _, ok := fn.objects[obj]; !ok {
|
||||
fn.addNamedLocal(obj)
|
||||
}
|
||||
@ -1350,7 +1343,7 @@ func (b *Builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
|
||||
//
|
||||
func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) {
|
||||
// TODO(adonovan): document how and why typ ever differs from
|
||||
// fn.Pkg.TypeOf(e).
|
||||
// fn.Pkg.typeOf(e).
|
||||
|
||||
switch t := typ.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
@ -1577,11 +1570,11 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||
var id *ast.Ident
|
||||
switch ass := s.Assign.(type) {
|
||||
case *ast.ExprStmt: // x.(type)
|
||||
x = b.expr(fn, noparens(ass.X).(*ast.TypeAssertExpr).X)
|
||||
x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
|
||||
case *ast.AssignStmt: // y := x.(type)
|
||||
x = b.expr(fn, noparens(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
|
||||
x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
|
||||
id = ass.Lhs[0].(*ast.Ident)
|
||||
y = fn.addNamedLocal(fn.Pkg.ObjectOf(id))
|
||||
y = fn.addNamedLocal(fn.Pkg.objectOf(id))
|
||||
emitStore(fn, y, x)
|
||||
}
|
||||
|
||||
@ -1602,7 +1595,7 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||
var ti Value // t_i, ok := typeassert,ok x <T_i>
|
||||
for _, cond := range cc.List {
|
||||
next = fn.newBasicBlock("typeswitch.next")
|
||||
casetype = fn.Pkg.TypeOf(cond)
|
||||
casetype = fn.Pkg.typeOf(cond)
|
||||
var condv Value
|
||||
if casetype == tUntypedNil {
|
||||
condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos)
|
||||
@ -1618,8 +1611,9 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||
if id != nil && len(cc.List) == 1 && casetype != tUntypedNil {
|
||||
// Declare a new shadow local variable of the
|
||||
// same name but a more specific type.
|
||||
y2 := fn.addNamedLocal(fn.Pkg.TypeInfo.typecases[cc])
|
||||
y2 := fn.addNamedLocal(fn.Pkg.info.TypeCaseVar(cc))
|
||||
y2.name += "'" // debugging aid
|
||||
y2.typ = pointer(casetype)
|
||||
emitStore(fn, y2, ti)
|
||||
}
|
||||
fn.targets = &targets{
|
||||
@ -1628,6 +1622,9 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
||||
}
|
||||
b.stmtList(fn, cc.Body)
|
||||
fn.targets = fn.targets.tail
|
||||
if id != nil {
|
||||
fn.objects[fn.Pkg.objectOf(id)] = y // restore previous y binding
|
||||
}
|
||||
emitJump(fn, done)
|
||||
fn.currentBlock = next
|
||||
}
|
||||
@ -1689,13 +1686,13 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
||||
case *ast.AssignStmt: // x := <-ch
|
||||
states = append(states, SelectState{
|
||||
Dir: ast.RECV,
|
||||
Chan: b.expr(fn, noparens(comm.Rhs[0]).(*ast.UnaryExpr).X),
|
||||
Chan: b.expr(fn, unparen(comm.Rhs[0]).(*ast.UnaryExpr).X),
|
||||
})
|
||||
|
||||
case *ast.ExprStmt: // <-ch
|
||||
states = append(states, SelectState{
|
||||
Dir: ast.RECV,
|
||||
Chan: b.expr(fn, noparens(comm.X).(*ast.UnaryExpr).X),
|
||||
Chan: b.expr(fn, unparen(comm.X).(*ast.UnaryExpr).X),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1744,12 +1741,12 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
||||
}
|
||||
switch comm := clause.Comm.(type) {
|
||||
case *ast.AssignStmt: // x := <-states[state].Chan
|
||||
xdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[0].(*ast.Ident)))
|
||||
xdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[0].(*ast.Ident)))
|
||||
recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref())
|
||||
emitStore(fn, xdecl, recv)
|
||||
|
||||
if len(comm.Lhs) == 2 { // x, ok := ...
|
||||
okdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[1].(*ast.Ident)))
|
||||
okdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[1].(*ast.Ident)))
|
||||
emitStore(fn, okdecl, emitExtract(fn, triple, 2, okdecl.Type().Deref()))
|
||||
}
|
||||
}
|
||||
@ -2025,10 +2022,10 @@ func (b *Builder) rangeChan(fn *Function, x Value, tk types.Type) (k Value, loop
|
||||
func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) {
|
||||
var tk, tv types.Type
|
||||
if !isBlankIdent(s.Key) {
|
||||
tk = fn.Pkg.TypeOf(s.Key)
|
||||
tk = fn.Pkg.typeOf(s.Key)
|
||||
}
|
||||
if s.Value != nil && !isBlankIdent(s.Value) {
|
||||
tv = fn.Pkg.TypeOf(s.Value)
|
||||
tv = fn.Pkg.typeOf(s.Value)
|
||||
}
|
||||
|
||||
// If iteration variables are defined (:=), this
|
||||
@ -2039,10 +2036,10 @@ func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) {
|
||||
// always creates a new one.
|
||||
if s.Tok == token.DEFINE {
|
||||
if tk != nil {
|
||||
fn.addNamedLocal(fn.Pkg.ObjectOf(s.Key.(*ast.Ident)))
|
||||
fn.addNamedLocal(fn.Pkg.objectOf(s.Key.(*ast.Ident)))
|
||||
}
|
||||
if tv != nil {
|
||||
fn.addNamedLocal(fn.Pkg.ObjectOf(s.Value.(*ast.Ident)))
|
||||
fn.addNamedLocal(fn.Pkg.objectOf(s.Value.(*ast.Ident)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -2129,7 +2126,7 @@ start:
|
||||
fn.emit(&Send{
|
||||
Chan: b.expr(fn, s.Chan),
|
||||
X: emitConv(fn, b.expr(fn, s.Value),
|
||||
fn.Pkg.TypeOf(s.Chan).Underlying().(*types.Chan).Elem()),
|
||||
fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()),
|
||||
pos: s.Arrow,
|
||||
})
|
||||
|
||||
@ -2336,7 +2333,7 @@ func (b *Builder) buildFunction(fn *Function) {
|
||||
fn.FullName(), fn.Prog.Files.Position(fn.pos))()
|
||||
}
|
||||
fn.startBody()
|
||||
fn.createSyntacticParams(fn.Pkg.idents)
|
||||
fn.createSyntacticParams()
|
||||
b.stmt(fn, fn.syntax.body)
|
||||
if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
|
||||
// Run function calls deferred in this function when
|
||||
@ -2354,6 +2351,8 @@ func (b *Builder) buildFunction(fn *Function) {
|
||||
// tree (for funcs and vars only); it will be used during the build
|
||||
// phase.
|
||||
//
|
||||
// (CREATE phase.)
|
||||
//
|
||||
func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
||||
name := obj.Name()
|
||||
switch obj := obj.(type) {
|
||||
@ -2418,6 +2417,8 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No
|
||||
// typechecker object (var, func, const or type) associated with the
|
||||
// specified decl.
|
||||
//
|
||||
// (CREATE phase.)
|
||||
//
|
||||
func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl: // import, const, type or var
|
||||
@ -2426,7 +2427,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
|
||||
for _, spec := range decl.Specs {
|
||||
for _, id := range spec.(*ast.ValueSpec).Names {
|
||||
if !isBlankIdent(id) {
|
||||
b.memberFromObject(pkg, pkg.ObjectOf(id), nil)
|
||||
b.memberFromObject(pkg, pkg.objectOf(id), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2435,7 +2436,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
|
||||
for _, spec := range decl.Specs {
|
||||
for _, id := range spec.(*ast.ValueSpec).Names {
|
||||
if !isBlankIdent(id) {
|
||||
b.memberFromObject(pkg, pkg.ObjectOf(id), spec)
|
||||
b.memberFromObject(pkg, pkg.objectOf(id), spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2444,7 +2445,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
|
||||
for _, spec := range decl.Specs {
|
||||
id := spec.(*ast.TypeSpec).Name
|
||||
if !isBlankIdent(id) {
|
||||
b.memberFromObject(pkg, pkg.ObjectOf(id), nil)
|
||||
b.memberFromObject(pkg, pkg.objectOf(id), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2458,97 +2459,27 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
|
||||
return // init blocks aren't functions
|
||||
}
|
||||
if !isBlankIdent(id) {
|
||||
b.memberFromObject(pkg, pkg.ObjectOf(id), decl)
|
||||
b.memberFromObject(pkg, pkg.objectOf(id), decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// typecheck invokes the type-checker on files and returns the
|
||||
// type-checker's package so formed, plus the AST type information.
|
||||
//
|
||||
func (b *Builder) typecheck(importPath string, files []*ast.File) (*types.Package, *TypeInfo, error) {
|
||||
info := &TypeInfo{
|
||||
fset: b.Prog.Files,
|
||||
types: make(map[ast.Expr]types.Type),
|
||||
idents: make(map[*ast.Ident]types.Object),
|
||||
constants: make(map[ast.Expr]*Literal),
|
||||
typecases: make(map[*ast.CaseClause]*types.Var),
|
||||
}
|
||||
tc := b.Context.TypeChecker
|
||||
tc.Expr = func(x ast.Expr, typ types.Type, val exact.Value) {
|
||||
info.types[x] = typ
|
||||
if val != nil {
|
||||
info.constants[x] = newLiteral(val, typ)
|
||||
}
|
||||
}
|
||||
tc.Ident = func(ident *ast.Ident, obj types.Object) {
|
||||
// Invariants:
|
||||
// - obj is non-nil.
|
||||
// - isBlankIdent(ident) <=> obj.GetType()==nil
|
||||
info.idents[ident] = obj
|
||||
}
|
||||
tc.ImplicitObj = func(node ast.Node, obj types.Object) {
|
||||
if cc, ok := node.(*ast.CaseClause); ok {
|
||||
info.typecases[cc] = obj.(*types.Var)
|
||||
}
|
||||
}
|
||||
typkg, firstErr := tc.Check(importPath, b.Prog.Files, files...)
|
||||
tc.Expr = nil
|
||||
tc.Ident = nil
|
||||
if firstErr != nil {
|
||||
return nil, nil, firstErr
|
||||
}
|
||||
return typkg, info, nil
|
||||
}
|
||||
|
||||
// CreatePackage creates a package from the specified set of files,
|
||||
// performs type-checking, and allocates all global SSA Values for the
|
||||
// package. It returns a new SSA Package providing access to these
|
||||
// values. The order of files determines the package initialization order.
|
||||
//
|
||||
// importPath is the full name under which this package is known, such
|
||||
// as appears in an import declaration. e.g. "sync/atomic".
|
||||
//
|
||||
// The ParseFiles() utility may be helpful for parsing a set of Go
|
||||
// source files.
|
||||
//
|
||||
func (b *Builder) CreatePackage(importPath string, files []*ast.File) (*Package, error) {
|
||||
typkg, info, err := b.typecheck(importPath, files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.createPackageImpl(typkg, importPath, files, info), nil
|
||||
}
|
||||
|
||||
// createPackageImpl constructs an SSA Package from an error-free
|
||||
// types.Package typkg and populates its Members mapping. It returns
|
||||
// the newly constructed ssa.Package.
|
||||
// createPackage constructs an SSA Package from an error-free
|
||||
// package described by info, and populates its Members mapping.
|
||||
//
|
||||
// The real work of building SSA form for each function is not done
|
||||
// until a subsequent call to BuildPackage.
|
||||
//
|
||||
// If files is non-nil, its declarations will be used to generate code
|
||||
// for functions, methods and init blocks in a subsequent call to
|
||||
// BuildPackage; info must contains the type information for those files.
|
||||
// Otherwise, typkg is assumed to have been imported
|
||||
// from the gc compiler's object files; no code will be available.
|
||||
// (CREATE phase.)
|
||||
//
|
||||
func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, files []*ast.File, info *TypeInfo) *Package {
|
||||
func (b *Builder) createPackage(info *importer.PackageInfo) *Package {
|
||||
p := &Package{
|
||||
Prog: b.Prog,
|
||||
Types: typkg,
|
||||
Members: make(map[string]Member),
|
||||
Files: files,
|
||||
nTo1Vars: make(map[*ast.ValueSpec]bool),
|
||||
Prog: b.Prog,
|
||||
Members: make(map[string]Member),
|
||||
Types: info.Pkg,
|
||||
info: info, // transient (CREATE and BUILD phases)
|
||||
}
|
||||
|
||||
if files != nil {
|
||||
p.TypeInfo = info
|
||||
}
|
||||
|
||||
b.packages[typkg] = p
|
||||
b.Prog.Packages[importPath] = p
|
||||
|
||||
// Add init() function (but not to Members since it can't be referenced).
|
||||
p.Init = &Function{
|
||||
name: "init",
|
||||
@ -2559,10 +2490,9 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
|
||||
|
||||
// CREATE phase.
|
||||
// Allocate all package members: vars, funcs and consts and types.
|
||||
if len(files) > 0 {
|
||||
if len(info.Files) > 0 {
|
||||
// Go source package.
|
||||
|
||||
for _, file := range p.Files {
|
||||
for _, file := range info.Files {
|
||||
for _, decl := range file.Decls {
|
||||
b.membersFromDecl(p, decl)
|
||||
}
|
||||
@ -2571,22 +2501,12 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
|
||||
// GC-compiled binary package.
|
||||
// No code.
|
||||
// No position information.
|
||||
|
||||
scope := p.Types.Scope()
|
||||
for i, n := 0, scope.NumEntries(); i < n; i++ {
|
||||
b.memberFromObject(p, scope.At(i), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the method sets
|
||||
for _, mem := range p.Members {
|
||||
switch t := mem.(type) {
|
||||
case *Type:
|
||||
t.Methods = b.Prog.MethodSet(t.NamedType)
|
||||
t.PtrMethods = b.Prog.MethodSet(pointer(t.NamedType))
|
||||
}
|
||||
}
|
||||
|
||||
// Add initializer guard variable.
|
||||
initguard := &Global{
|
||||
Pkg: p,
|
||||
@ -2599,6 +2519,8 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
|
||||
p.DumpTo(os.Stderr)
|
||||
}
|
||||
|
||||
p.info = nil
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
@ -2620,7 +2542,7 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) {
|
||||
if isBlankIdent(id) {
|
||||
continue
|
||||
}
|
||||
nt := pkg.ObjectOf(id).Type().(*types.Named)
|
||||
nt := pkg.objectOf(id).Type().(*types.Named)
|
||||
for i, n := 0, nt.NumMethods(); i < n; i++ {
|
||||
b.buildFunction(b.Prog.concreteMethods[nt.Method(i)])
|
||||
}
|
||||
@ -2656,7 +2578,7 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) {
|
||||
init.targets = init.targets.tail
|
||||
init.currentBlock = next
|
||||
|
||||
} else if m, ok := b.globals[pkg.ObjectOf(id)]; ok {
|
||||
} else if m, ok := b.globals[pkg.objectOf(id)]; ok {
|
||||
// Package-level function.
|
||||
b.buildFunction(m.(*Function))
|
||||
}
|
||||
@ -2694,11 +2616,23 @@ func (b *Builder) BuildPackage(p *Package) {
|
||||
if !atomic.CompareAndSwapInt32(&p.started, 0, 1) {
|
||||
return // already started
|
||||
}
|
||||
if p.Files == nil {
|
||||
info := b.imp.Packages[p.Types.Path()]
|
||||
if info == nil {
|
||||
panic("no PackageInfo for " + p.Types.Path())
|
||||
}
|
||||
if info.Files == nil {
|
||||
return // nothing to do
|
||||
}
|
||||
|
||||
// TODO(adonovan): consider splitting Builder up into a
|
||||
// package-specific part (needn't be exported) containing
|
||||
// info, nto1Vars which actually traverses the AST, plus the
|
||||
// shared portion (Builder).
|
||||
p.info = info
|
||||
p.nTo1Vars = make(map[*ast.ValueSpec]bool)
|
||||
|
||||
if b.Context.Mode&LogSource != 0 {
|
||||
defer logStack("build package %s", p.Types.Path())()
|
||||
defer logStack("build %s", p)()
|
||||
}
|
||||
init := p.Init
|
||||
init.startBody()
|
||||
@ -2717,7 +2651,7 @@ func (b *Builder) BuildPackage(p *Package) {
|
||||
// transitive closure of dependencies,
|
||||
// e.g. when using GcImporter.
|
||||
seen := make(map[*types.Package]bool)
|
||||
for _, file := range p.Files {
|
||||
for _, file := range info.Files {
|
||||
for _, imp := range file.Imports {
|
||||
path, _ := strconv.Unquote(imp.Path.Value)
|
||||
if path == "unsafe" {
|
||||
@ -2731,7 +2665,7 @@ func (b *Builder) BuildPackage(p *Package) {
|
||||
|
||||
p2 := b.packages[typkg]
|
||||
if p2 == nil {
|
||||
panic("Building " + p.Name() + ": CreatePackage has not been called for package " + path)
|
||||
panic("Building " + p.String() + ": CreatePackage has not been called for package " + path)
|
||||
}
|
||||
|
||||
var v Call
|
||||
@ -2750,17 +2684,14 @@ func (b *Builder) BuildPackage(p *Package) {
|
||||
//
|
||||
// We also ensure all functions and methods are built, even if
|
||||
// they are unreachable.
|
||||
for _, file := range p.Files {
|
||||
for _, file := range info.Files {
|
||||
for _, decl := range file.Decls {
|
||||
b.buildDecl(p, decl)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out the typed ASTs unless otherwise requested.
|
||||
if retain := b.Context.RetainAST; retain == nil || !retain(p) {
|
||||
p.Files = nil
|
||||
p.TypeInfo = nil
|
||||
}
|
||||
// We no longer need ASTs or go/types deductions.
|
||||
p.info = nil
|
||||
p.nTo1Vars = nil
|
||||
|
||||
// Finish up.
|
||||
@ -2770,3 +2701,17 @@ func (b *Builder) BuildPackage(p *Package) {
|
||||
init.emit(new(Ret))
|
||||
init.finishBody()
|
||||
}
|
||||
|
||||
// Only valid during p's build phase!
|
||||
func (p *Package) objectOf(id *ast.Ident) types.Object {
|
||||
if o := p.info.ObjectOf(id); o != nil {
|
||||
return o
|
||||
}
|
||||
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
|
||||
id.Name, p.Prog.Files.Position(id.Pos())))
|
||||
}
|
||||
|
||||
// Only valid during p's build phase!
|
||||
func (p *Package) typeOf(e ast.Expr) types.Type {
|
||||
return p.info.TypeOf(e)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ssa_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
@ -35,24 +36,27 @@ func main() {
|
||||
fmt.Println(message)
|
||||
}
|
||||
`
|
||||
|
||||
// Construct a builder. Imports will be loaded as if by 'go build'.
|
||||
builder := ssa.NewBuilder(&ssa.Context{Loader: ssa.MakeGoBuildLoader(nil)})
|
||||
// Construct an importer. Imports will be loaded as if by 'go build'.
|
||||
imp := importer.New(&importer.Context{Loader: importer.MakeGoBuildLoader(nil)})
|
||||
|
||||
// Parse the input file.
|
||||
file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors)
|
||||
file, err := parser.ParseFile(imp.Fset, "hello.go", hello, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error()) // parse error
|
||||
return
|
||||
}
|
||||
|
||||
// Create a "main" package containing one file.
|
||||
mainPkg, err := builder.CreatePackage("main", []*ast.File{file})
|
||||
info, err := imp.CreateSourcePackage("main", []*ast.File{file})
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error()) // type error
|
||||
return
|
||||
}
|
||||
|
||||
// Construct an SSA builder.
|
||||
builder := ssa.NewBuilder(&ssa.Context{}, imp)
|
||||
mainPkg := builder.PackageFor(info.Pkg)
|
||||
|
||||
// Print out the package.
|
||||
mainPkg.DumpTo(os.Stdout)
|
||||
fmt.Println()
|
||||
@ -70,7 +74,7 @@ func main() {
|
||||
|
||||
// Output:
|
||||
//
|
||||
// Package main:
|
||||
// package main:
|
||||
// var init$guard *bool
|
||||
// func main func()
|
||||
// const message message = "Hello, World!":untyped string
|
||||
|
11
ssa/func.go
11
ssa/func.go
@ -213,21 +213,18 @@ func (f *Function) startBody() {
|
||||
// and named result locals) for all the parameters declared in the
|
||||
// syntax. In addition it populates the f.objects mapping.
|
||||
//
|
||||
// idents must be a mapping from syntactic identifiers to their
|
||||
// canonical type objects.
|
||||
//
|
||||
// Preconditions:
|
||||
// f.syntax != nil, i.e. this is a Go source function.
|
||||
// f.startBody() was called.
|
||||
// Postcondition:
|
||||
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
|
||||
//
|
||||
func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) {
|
||||
func (f *Function) createSyntacticParams() {
|
||||
// Receiver (at most one inner iteration).
|
||||
if f.syntax.recvField != nil {
|
||||
for _, field := range f.syntax.recvField.List {
|
||||
for _, n := range field.Names {
|
||||
f.addSpilledParam(idents[n])
|
||||
f.addSpilledParam(f.Pkg.objectOf(n))
|
||||
}
|
||||
// Anonymous receiver? No need to spill.
|
||||
if field.Names == nil {
|
||||
@ -241,7 +238,7 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) {
|
||||
n := len(f.Params) // 1 if has recv, 0 otherwise
|
||||
for _, field := range f.syntax.paramFields.List {
|
||||
for _, n := range field.Names {
|
||||
f.addSpilledParam(idents[n])
|
||||
f.addSpilledParam(f.Pkg.objectOf(n))
|
||||
}
|
||||
// Anonymous parameter? No need to spill.
|
||||
if field.Names == nil {
|
||||
@ -255,7 +252,7 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) {
|
||||
for _, field := range f.syntax.resultFields.List {
|
||||
// Implicit "var" decl of locals for named results.
|
||||
for _, n := range field.Names {
|
||||
f.namedResults = append(f.namedResults, f.addNamedLocal(idents[n]))
|
||||
f.namedResults = append(f.namedResults, f.addNamedLocal(f.Pkg.objectOf(n)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,7 +530,7 @@ func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) {
|
||||
*g = v
|
||||
return
|
||||
}
|
||||
panic("no global variable: " + pkg.Name() + "." + name)
|
||||
panic("no global variable: " + pkg.Types.Path() + "." + name)
|
||||
}
|
||||
|
||||
// Interpret interprets the Go program whose main package is mainpkg.
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
"code.google.com/p/go.tools/ssa/interp"
|
||||
)
|
||||
@ -141,11 +142,11 @@ func run(t *testing.T, dir, input string) bool {
|
||||
inputs = append(inputs, dir+i)
|
||||
}
|
||||
|
||||
b := ssa.NewBuilder(&ssa.Context{
|
||||
Mode: ssa.SanityCheckFunctions,
|
||||
Loader: ssa.MakeGoBuildLoader(nil),
|
||||
})
|
||||
files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...)
|
||||
impctx := &importer.Context{
|
||||
Loader: importer.MakeGoBuildLoader(nil),
|
||||
}
|
||||
imp := importer.New(impctx)
|
||||
files, err := importer.ParseFiles(imp.Fset, ".", inputs...)
|
||||
if err != nil {
|
||||
t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error())
|
||||
return false
|
||||
@ -163,13 +164,15 @@ func run(t *testing.T, dir, input string) bool {
|
||||
}()
|
||||
|
||||
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go run exp/ssa/ssadump.go -build=CFP %s\n", input)
|
||||
mainpkg, err := b.CreatePackage("main", files)
|
||||
info, err := imp.CreateSourcePackage("main", files)
|
||||
if err != nil {
|
||||
t.Errorf("ssa.Builder.CreatePackage(%s) failed: %s", inputs, err.Error())
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions}, imp)
|
||||
mainpkg := b.PackageFor(info.Pkg)
|
||||
|
||||
b.BuildAllPackages()
|
||||
b = nil // discard Builder
|
||||
|
||||
|
@ -356,11 +356,11 @@ func (s *MapUpdate) String() string {
|
||||
}
|
||||
|
||||
func (p *Package) String() string {
|
||||
return "Package " + p.Types.Path()
|
||||
return "package " + p.Types.Path()
|
||||
}
|
||||
|
||||
func (p *Package) DumpTo(w io.Writer) {
|
||||
fmt.Fprintf(w, "Package %s:\n", p.Types.Path())
|
||||
fmt.Fprintf(w, "%s:\n", p)
|
||||
|
||||
var names []string
|
||||
maxname := 0
|
||||
|
@ -149,7 +149,11 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
||||
if nt, ok := t.(*types.Named); ok {
|
||||
for i, n := 0, nt.NumMethods(); i < n; i++ {
|
||||
m := nt.Method(i)
|
||||
addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, prog.concreteMethods[m], node)
|
||||
concrete := prog.concreteMethods[m]
|
||||
if concrete == nil {
|
||||
panic(fmt.Sprint("no ssa.Function for mset(%s)[%s]", t, m.Name()))
|
||||
}
|
||||
addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, concrete, node)
|
||||
}
|
||||
t = nt.Underlying()
|
||||
}
|
||||
@ -311,6 +315,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
||||
c.Call.Args = append(c.Call.Args, v)
|
||||
} else {
|
||||
c.Call.Recv = v
|
||||
// TODO(adonovan): fix: this looks wrong! Need to
|
||||
// find method index within
|
||||
// v.Type().Underlying().(*types.Interface).Methods()
|
||||
c.Call.Method = 0
|
||||
}
|
||||
for _, arg := range fn.Params[1:] {
|
||||
|
@ -6,6 +6,7 @@ package ssa
|
||||
// since neither depends on SSA internals.
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
@ -23,16 +24,19 @@ func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
|
||||
// program prog. exact is defined as for standalone
|
||||
// PathEnclosingInterval.
|
||||
//
|
||||
// imp provides ASTs for the program's packages.
|
||||
//
|
||||
// The result is (nil, nil, false) if not found.
|
||||
//
|
||||
func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
|
||||
for _, pkg = range prog.Packages {
|
||||
for _, f := range pkg.Files {
|
||||
func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
|
||||
for path, info := range imp.Packages {
|
||||
pkg := prog.Packages[path]
|
||||
for _, f := range info.Files {
|
||||
if !tokenFileContainsPos(prog.Files.File(f.Package), start) {
|
||||
continue
|
||||
}
|
||||
if path, exact = PathEnclosingInterval(f, start, end); path != nil {
|
||||
return
|
||||
if path, exact := PathEnclosingInterval(f, start, end); path != nil {
|
||||
return pkg, path, exact
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ package ssa_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
@ -235,8 +236,8 @@ func TestEnclosingFunction(t *testing.T) {
|
||||
"900", "func@2.27"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
b := ssa.NewBuilder(new(ssa.Context)) // (NB: no Loader)
|
||||
f, start, end := findInterval(t, b.Prog.Files, test.input, test.substr)
|
||||
imp := importer.New(new(importer.Context)) // (NB: no Loader)
|
||||
f, start, end := findInterval(t, imp.Fset, test.input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
@ -245,12 +246,14 @@ func TestEnclosingFunction(t *testing.T) {
|
||||
t.Errorf("EnclosingFunction(%q) not exact", test.substr)
|
||||
continue
|
||||
}
|
||||
pkg, err := b.CreatePackage("main", []*ast.File{f})
|
||||
info, err := imp.CreateSourcePackage("main", []*ast.File{f})
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
b := ssa.NewBuilder(new(ssa.Context), imp)
|
||||
pkg := b.PackageFor(info.Pkg)
|
||||
b.BuildPackage(pkg)
|
||||
|
||||
name := "(none)"
|
||||
|
13
ssa/ssa.go
13
ssa/ssa.go
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/importer"
|
||||
)
|
||||
|
||||
// A Program is a partial or complete Go program converted to SSA form.
|
||||
@ -18,7 +19,7 @@ import (
|
||||
// lifetime.
|
||||
//
|
||||
type Program struct {
|
||||
Files *token.FileSet // position information for the files of this Program
|
||||
Files *token.FileSet // position information for the files of this Program [TODO: rename Fset]
|
||||
Packages map[string]*Package // all loaded Packages, keyed by import path
|
||||
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
|
||||
|
||||
@ -35,18 +36,14 @@ type Program struct {
|
||||
//
|
||||
type Package struct {
|
||||
Prog *Program // the owning program
|
||||
Types *types.Package // the type checker's package object for this package.
|
||||
Types *types.Package // the type checker's package object for this package
|
||||
Members map[string]Member // all exported and unexported members of the package
|
||||
Init *Function // the package's (concatenated) init function
|
||||
|
||||
// These fields are available between package creation and SSA
|
||||
// building, but are then cleared unless Context.RetainAST(pkg).
|
||||
Files []*ast.File // abstract syntax for the package's files
|
||||
*TypeInfo // type-checker intermediate results
|
||||
|
||||
// The following fields are set transiently during building,
|
||||
// then cleared.
|
||||
started int32 // atomically tested and set at start of build phase
|
||||
info *importer.PackageInfo // package ASTs and type information
|
||||
nTo1Vars map[*ast.ValueSpec]bool // set of n:1 ValueSpecs already built
|
||||
}
|
||||
|
||||
@ -1286,8 +1283,6 @@ func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() }
|
||||
func (t *Type) String() string { return t.Name() }
|
||||
func (t *Type) Type() types.Type { return t.NamedType }
|
||||
|
||||
func (p *Package) Name() string { return p.Types.Name() }
|
||||
|
||||
func (c *Constant) Name() string { return c.name }
|
||||
func (c *Constant) Pos() token.Pos { return c.pos }
|
||||
func (c *Constant) String() string { return c.Name() }
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
"code.google.com/p/go.tools/ssa/interp"
|
||||
)
|
||||
@ -50,6 +51,8 @@ func main() {
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
|
||||
impctx := importer.Context{Loader: importer.MakeGoBuildLoader(nil)}
|
||||
|
||||
var mode ssa.BuilderMode
|
||||
for _, c := range *buildFlag {
|
||||
switch c {
|
||||
@ -64,7 +67,7 @@ func main() {
|
||||
case 'N':
|
||||
mode |= ssa.NaiveForm
|
||||
case 'G':
|
||||
mode |= ssa.UseGCImporter
|
||||
impctx.Loader = nil
|
||||
case 'L':
|
||||
mode |= ssa.BuildSerially
|
||||
default:
|
||||
@ -99,19 +102,24 @@ func main() {
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
context := &ssa.Context{
|
||||
Mode: mode,
|
||||
Loader: ssa.MakeGoBuildLoader(nil),
|
||||
}
|
||||
b := ssa.NewBuilder(context)
|
||||
mainpkg, args, err := ssa.CreatePackageFromArgs(b, args)
|
||||
// Load, parse and type-check the program.
|
||||
imp := importer.New(&impctx)
|
||||
info, args, err := importer.CreatePackageFromArgs(imp, args)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// Build SSA-form program representation.
|
||||
context := &ssa.Context{
|
||||
Mode: mode,
|
||||
}
|
||||
b := ssa.NewBuilder(context, imp)
|
||||
mainpkg := b.PackageFor(info.Pkg)
|
||||
b.BuildAllPackages()
|
||||
b = nil // discard Builder
|
||||
|
||||
// Run the interpreter.
|
||||
if *runFlag {
|
||||
interp.Interpret(mainpkg, interpMode, mainpkg.Name(), args)
|
||||
interp.Interpret(mainpkg, interpMode, info.Pkg.Path(), args)
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ func unreachable() {
|
||||
|
||||
//// AST utilities
|
||||
|
||||
// noparens returns e with any enclosing parentheses stripped.
|
||||
func noparens(e ast.Expr) ast.Expr {
|
||||
// unparen returns e with any enclosing parentheses stripped.
|
||||
func unparen(e ast.Expr) ast.Expr {
|
||||
for {
|
||||
p, ok := e.(*ast.ParenExpr)
|
||||
if !ok {
|
||||
|
Loading…
x
Reference in New Issue
Block a user