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