src

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

load.go (6785B)


      1 // Copyright 2015 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 ssautil
      6 
      7 // This file defines utility functions for constructing programs in SSA form.
      8 
      9 import (
     10 	"go/ast"
     11 	"go/token"
     12 	"go/types"
     13 
     14 	"golang.org/x/tools/go/packages"
     15 	"golang.org/x/tools/go/ssa"
     16 )
     17 
     18 // Packages creates an SSA program for a set of packages.
     19 //
     20 // The packages must have been loaded from source syntax using the
     21 // [packages.Load] function in [packages.LoadSyntax] or
     22 // [packages.LoadAllSyntax] mode.
     23 //
     24 // Packages creates an SSA package for each well-typed package in the
     25 // initial list, plus all their dependencies. The resulting list of
     26 // packages corresponds to the list of initial packages, and may contain
     27 // a nil if SSA code could not be constructed for the corresponding initial
     28 // package due to type errors.
     29 //
     30 // Code for bodies of functions is not built until [Program.Build] is
     31 // called on the resulting Program. SSA code is constructed only for
     32 // the initial packages with well-typed syntax trees.
     33 //
     34 // The mode parameter controls diagnostics and checking during SSA construction.
     35 func Packages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
     36 	// TODO(adonovan): opt: this calls CreatePackage far more than
     37 	// necessary: for all dependencies, not just the (non-initial)
     38 	// direct dependencies of the initial packages.
     39 	//
     40 	// But can it reasonably be changed without breaking the
     41 	// spirit and/or letter of the law above? Clients may notice
     42 	// if we call CreatePackage less, as methods like
     43 	// Program.FuncValue will return nil. Or must we provide a new
     44 	// function (and perhaps deprecate this one)? Is it worth it?
     45 	//
     46 	// Tim King makes the interesting point that it would be
     47 	// possible to entirely alleviate the client from the burden
     48 	// of calling CreatePackage for non-syntax packages, if we
     49 	// were to treat vars and funcs lazily in the same way we now
     50 	// treat methods. (In essence, try to move away from the
     51 	// notion of ssa.Packages, and make the Program answer
     52 	// all reasonable questions about any types.Object.)
     53 
     54 	return doPackages(initial, mode, false)
     55 }
     56 
     57 // AllPackages creates an SSA program for a set of packages plus all
     58 // their dependencies.
     59 //
     60 // The packages must have been loaded from source syntax using the
     61 // [packages.Load] function in [packages.LoadAllSyntax] mode.
     62 //
     63 // AllPackages creates an SSA package for each well-typed package in the
     64 // initial list, plus all their dependencies. The resulting list of
     65 // packages corresponds to the list of initial packages, and may contain
     66 // a nil if SSA code could not be constructed for the corresponding
     67 // initial package due to type errors.
     68 //
     69 // Code for bodies of functions is not built until Build is called on
     70 // the resulting Program. SSA code is constructed for all packages with
     71 // well-typed syntax trees.
     72 //
     73 // The mode parameter controls diagnostics and checking during SSA construction.
     74 func AllPackages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
     75 	return doPackages(initial, mode, true)
     76 }
     77 
     78 func doPackages(initial []*packages.Package, mode ssa.BuilderMode, deps bool) (*ssa.Program, []*ssa.Package) {
     79 
     80 	var fset *token.FileSet
     81 	if len(initial) > 0 {
     82 		fset = initial[0].Fset
     83 	}
     84 
     85 	prog := ssa.NewProgram(fset, mode)
     86 
     87 	isInitial := make(map[*packages.Package]bool, len(initial))
     88 	for _, p := range initial {
     89 		isInitial[p] = true
     90 	}
     91 
     92 	ssamap := make(map[*packages.Package]*ssa.Package)
     93 	packages.Visit(initial, nil, func(p *packages.Package) {
     94 		if p.Types != nil && !p.IllTyped {
     95 			var files []*ast.File
     96 			var info *types.Info
     97 			if deps || isInitial[p] {
     98 				files = p.Syntax
     99 				info = p.TypesInfo
    100 			}
    101 			ssamap[p] = prog.CreatePackage(p.Types, files, info, true)
    102 		}
    103 	})
    104 
    105 	var ssapkgs []*ssa.Package
    106 	for _, p := range initial {
    107 		ssapkgs = append(ssapkgs, ssamap[p]) // may be nil
    108 	}
    109 	return prog, ssapkgs
    110 }
    111 
    112 // BuildPackage builds an SSA program with SSA intermediate
    113 // representation (IR) for all functions of a single package.
    114 //
    115 // It populates pkg by type-checking the specified file syntax trees.  All
    116 // dependencies are loaded using the importer specified by tc, which
    117 // typically loads compiler export data; SSA code cannot be built for
    118 // those packages.  BuildPackage then constructs an [ssa.Program] with all
    119 // dependency packages created, and builds and returns the SSA package
    120 // corresponding to pkg.
    121 //
    122 // The caller must have set pkg.Path to the import path.
    123 //
    124 // The operation fails if there were any type-checking or import errors.
    125 //
    126 // See ../example_test.go for an example.
    127 func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
    128 	if fset == nil {
    129 		panic("no token.FileSet")
    130 	}
    131 	if pkg.Path() == "" {
    132 		panic("package has no import path")
    133 	}
    134 
    135 	info := &types.Info{
    136 		Types:        make(map[ast.Expr]types.TypeAndValue),
    137 		Defs:         make(map[*ast.Ident]types.Object),
    138 		Uses:         make(map[*ast.Ident]types.Object),
    139 		Implicits:    make(map[ast.Node]types.Object),
    140 		Instances:    make(map[*ast.Ident]types.Instance),
    141 		Scopes:       make(map[ast.Node]*types.Scope),
    142 		Selections:   make(map[*ast.SelectorExpr]*types.Selection),
    143 		FileVersions: make(map[*ast.File]string),
    144 	}
    145 	if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
    146 		return nil, nil, err
    147 	}
    148 
    149 	prog := ssa.NewProgram(fset, mode)
    150 
    151 	// Create SSA packages for all imports.
    152 	// Order is not significant.
    153 	created := make(map[*types.Package]bool)
    154 	var createAll func(pkgs []*types.Package)
    155 	createAll = func(pkgs []*types.Package) {
    156 		for _, p := range pkgs {
    157 			if !created[p] {
    158 				created[p] = true
    159 				prog.CreatePackage(p, nil, nil, true)
    160 				createAll(p.Imports())
    161 			}
    162 		}
    163 	}
    164 	createAll(pkg.Imports())
    165 
    166 	// TODO(adonovan): we could replace createAll with just:
    167 	//
    168 	// // Create SSA packages for all imports.
    169 	// for _, p := range pkg.Imports() {
    170 	// 	prog.CreatePackage(p, nil, nil, true)
    171 	// }
    172 	//
    173 	// (with minor changes to changes to ../builder_test.go as
    174 	// shown in CL 511715 PS 10.) But this would strictly violate
    175 	// the letter of the doc comment above, which says "all
    176 	// dependencies created".
    177 	//
    178 	// Tim makes the good point with some extra work we could
    179 	// remove the need for any CreatePackage calls except the
    180 	// ones with syntax (i.e. primary packages). Of course
    181 	// You wouldn't have ssa.Packages and Members for as
    182 	// many things but no-one really uses that anyway.
    183 	// I wish I had done this from the outset.
    184 
    185 	// Create and build the primary package.
    186 	ssapkg := prog.CreatePackage(pkg, files, info, false)
    187 	ssapkg.Build()
    188 	return ssapkg, info, nil
    189 }