src

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

visit.go (5489B)


      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 ssautil // import "golang.org/x/tools/go/ssa/ssautil"
      6 
      7 import (
      8 	"go/ast"
      9 	"go/types"
     10 
     11 	"golang.org/x/tools/go/ssa"
     12 
     13 	_ "unsafe" // for linkname hack
     14 )
     15 
     16 // This file defines utilities for visiting the SSA representation of
     17 // a Program.
     18 //
     19 // TODO(adonovan): test coverage.
     20 
     21 // AllFunctions finds and returns the set of functions potentially
     22 // needed by program prog, as determined by a simple linker-style
     23 // reachability algorithm starting from the members and method-sets of
     24 // each package.  The result may include anonymous functions and
     25 // synthetic wrappers.
     26 //
     27 // Precondition: all packages are built.
     28 //
     29 // TODO(adonovan): this function is underspecified. It doesn't
     30 // actually work like a linker, which computes reachability from main
     31 // using something like go/callgraph/cha (without materializing the
     32 // call graph). In fact, it treats all public functions and all
     33 // methods of public non-parameterized types as roots, even though
     34 // they may be unreachable--but only in packages created from syntax.
     35 //
     36 // I think we should deprecate AllFunctions function in favor of two
     37 // clearly defined ones:
     38 //
     39 //  1. The first would efficiently compute CHA reachability from a set
     40 //     of main packages, making it suitable for a whole-program
     41 //     analysis context with InstantiateGenerics, in conjunction with
     42 //     Program.Build.
     43 //
     44 //  2. The second would return only the set of functions corresponding
     45 //     to source Func{Decl,Lit} syntax, like SrcFunctions in
     46 //     go/analysis/passes/buildssa; this is suitable for
     47 //     package-at-a-time (or handful of packages) context.
     48 //     ssa.Package could easily expose it as a field.
     49 //
     50 // We could add them unexported for now and use them via the linkname hack.
     51 func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool {
     52 	seen := make(map[*ssa.Function]bool)
     53 
     54 	var function func(fn *ssa.Function)
     55 	function = func(fn *ssa.Function) {
     56 		if !seen[fn] {
     57 			seen[fn] = true
     58 			var buf [10]*ssa.Value // avoid alloc in common case
     59 			for _, b := range fn.Blocks {
     60 				for _, instr := range b.Instrs {
     61 					for _, op := range instr.Operands(buf[:0]) {
     62 						if fn, ok := (*op).(*ssa.Function); ok {
     63 							function(fn)
     64 						}
     65 					}
     66 				}
     67 			}
     68 		}
     69 	}
     70 
     71 	// TODO(adonovan): opt: provide a way to share a builder
     72 	// across a sequence of MethodValue calls.
     73 
     74 	methodsOf := func(T types.Type) {
     75 		if !types.IsInterface(T) {
     76 			mset := prog.MethodSets.MethodSet(T)
     77 			for sel := range mset.Methods() {
     78 				// Skip generic methods.
     79 				if sel.Obj().(*types.Func).Signature().TypeParams() == nil {
     80 					function(prog.MethodValue(sel))
     81 				}
     82 			}
     83 		}
     84 	}
     85 
     86 	// Historically, Program.RuntimeTypes used to include the type
     87 	// of any exported member of a package loaded from syntax that
     88 	// has a non-parameterized type, plus all types
     89 	// reachable from that type using reflection, even though
     90 	// these runtime types may not be required for them.
     91 	//
     92 	// Rather than break existing programs that rely on
     93 	// AllFunctions visiting extra methods that are unreferenced
     94 	// by IR and unreachable via reflection, we moved the logic
     95 	// here, unprincipled though it is.
     96 	// (See doc comment for better ideas.)
     97 	//
     98 	// Nonetheless, after the move, we no longer visit every
     99 	// method of any type recursively reachable from T, only the
    100 	// methods of T and *T themselves, and we only apply this to
    101 	// named types T, and not to the type of every exported
    102 	// package member.
    103 	exportedTypeHack := func(t *ssa.Type) {
    104 		if isSyntactic(t.Package()) &&
    105 			ast.IsExported(t.Name()) &&
    106 			!types.IsInterface(t.Type()) {
    107 			// Consider only named types.
    108 			// (Ignore aliases and unsafe.Pointer.)
    109 			if named, ok := t.Type().(*types.Named); ok {
    110 				// Skip generic types.
    111 				if named.TypeParams() == nil {
    112 					methodsOf(named)                   //  T
    113 					methodsOf(types.NewPointer(named)) // *T
    114 				}
    115 			}
    116 		}
    117 	}
    118 
    119 	for _, pkg := range prog.AllPackages() {
    120 		for _, mem := range pkg.Members {
    121 			switch mem := mem.(type) {
    122 			case *ssa.Function:
    123 				// Visit all package-level declared functions.
    124 				//
    125 				// (This may include generic functions, which is
    126 				// inconsistent with the treatment of methods:
    127 				// we skip both generic methods,
    128 				// and methods of generic types.)
    129 				function(mem)
    130 
    131 			case *ssa.Type:
    132 				exportedTypeHack(mem)
    133 			}
    134 		}
    135 	}
    136 
    137 	// Visit all methods of types for which runtime types were
    138 	// materialized, as they are reachable through reflection.
    139 	for _, T := range prog.RuntimeTypes() {
    140 		methodsOf(T)
    141 	}
    142 
    143 	return seen
    144 }
    145 
    146 // MainPackages returns the subset of the specified packages
    147 // named "main" that define a main function.
    148 // The result may include synthetic "testmain" packages.
    149 func MainPackages(pkgs []*ssa.Package) []*ssa.Package {
    150 	var mains []*ssa.Package
    151 	for _, pkg := range pkgs {
    152 		if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
    153 			mains = append(mains, pkg)
    154 		}
    155 	}
    156 	return mains
    157 }
    158 
    159 // TODO(adonovan): propose a principled API for this. One possibility
    160 // is a new field, Package.SrcFunctions []*Function, which would
    161 // contain the list of SrcFunctions described in point 2 of the
    162 // AllFunctions doc comment, or nil if the package is not from syntax.
    163 // But perhaps overloading nil vs empty slice is too subtle.
    164 //
    165 //go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic
    166 func isSyntactic(pkg *ssa.Package) bool