// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ssautil // import "golang.org/x/tools/go/ssa/ssautil" import ( "go/ast" "go/types" "golang.org/x/tools/go/ssa" _ "unsafe" // for linkname hack ) // This file defines utilities for visiting the SSA representation of // a Program. // // TODO(adonovan): test coverage. // AllFunctions finds and returns the set of functions potentially // needed by program prog, as determined by a simple linker-style // reachability algorithm starting from the members and method-sets of // each package. The result may include anonymous functions and // synthetic wrappers. // // Precondition: all packages are built. // // TODO(adonovan): this function is underspecified. It doesn't // actually work like a linker, which computes reachability from main // using something like go/callgraph/cha (without materializing the // call graph). In fact, it treats all public functions and all // methods of public non-parameterized types as roots, even though // they may be unreachable--but only in packages created from syntax. // // I think we should deprecate AllFunctions function in favor of two // clearly defined ones: // // 1. The first would efficiently compute CHA reachability from a set // of main packages, making it suitable for a whole-program // analysis context with InstantiateGenerics, in conjunction with // Program.Build. // // 2. The second would return only the set of functions corresponding // to source Func{Decl,Lit} syntax, like SrcFunctions in // go/analysis/passes/buildssa; this is suitable for // package-at-a-time (or handful of packages) context. // ssa.Package could easily expose it as a field. // // We could add them unexported for now and use them via the linkname hack. func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool { seen := make(map[*ssa.Function]bool) var function func(fn *ssa.Function) function = func(fn *ssa.Function) { if !seen[fn] { seen[fn] = true var buf [10]*ssa.Value // avoid alloc in common case for _, b := range fn.Blocks { for _, instr := range b.Instrs { for _, op := range instr.Operands(buf[:0]) { if fn, ok := (*op).(*ssa.Function); ok { function(fn) } } } } } } // TODO(adonovan): opt: provide a way to share a builder // across a sequence of MethodValue calls. methodsOf := func(T types.Type) { if !types.IsInterface(T) { mset := prog.MethodSets.MethodSet(T) for i := 0; i < mset.Len(); i++ { function(prog.MethodValue(mset.At(i))) } } } // Historically, Program.RuntimeTypes used to include the type // of any exported member of a package loaded from syntax that // has a non-parameterized type, plus all types // reachable from that type using reflection, even though // these runtime types may not be required for them. // // Rather than break existing programs that rely on // AllFunctions visiting extra methods that are unreferenced // by IR and unreachable via reflection, we moved the logic // here, unprincipled though it is. // (See doc comment for better ideas.) // // Nonetheless, after the move, we no longer visit every // method of any type recursively reachable from T, only the // methods of T and *T themselves, and we only apply this to // named types T, and not to the type of every exported // package member. exportedTypeHack := func(t *ssa.Type) { if isSyntactic(t.Package()) && ast.IsExported(t.Name()) && !types.IsInterface(t.Type()) { // Consider only named types. // (Ignore aliases and unsafe.Pointer.) if named, ok := t.Type().(*types.Named); ok { if named.TypeParams() == nil { methodsOf(named) // T methodsOf(types.NewPointer(named)) // *T } } } } for _, pkg := range prog.AllPackages() { for _, mem := range pkg.Members { switch mem := mem.(type) { case *ssa.Function: // Visit all package-level declared functions. function(mem) case *ssa.Type: exportedTypeHack(mem) } } } // Visit all methods of types for which runtime types were // materialized, as they are reachable through reflection. for _, T := range prog.RuntimeTypes() { methodsOf(T) } return seen } // MainPackages returns the subset of the specified packages // named "main" that define a main function. // The result may include synthetic "testmain" packages. func MainPackages(pkgs []*ssa.Package) []*ssa.Package { var mains []*ssa.Package for _, pkg := range pkgs { if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil { mains = append(mains, pkg) } } return mains } // TODO(adonovan): propose a principled API for this. One possibility // is a new field, Package.SrcFunctions []*Function, which would // contain the list of SrcFunctions described in point 2 of the // AllFunctions doc comment, or nil if the package is not from syntax. // But perhaps overloading nil vs empty slice is too subtle. // //go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic func isSyntactic(pkg *ssa.Package) bool