src

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

lazy.go (3412B)


      1 // Copyright 2024 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 chautil provides helper functions related to
      6 // class hierarchy analysis (CHA) for use in x/tools.
      7 package chautil
      8 
      9 import (
     10 	"go/types"
     11 
     12 	"golang.org/x/tools/go/ssa"
     13 	"golang.org/x/tools/go/types/typeutil"
     14 )
     15 
     16 // LazyCallees returns a function that maps a call site (in a function in fns)
     17 // to its callees within fns. The set of callees is computed using the CHA algorithm,
     18 // i.e., on the entire implements relation between interfaces and concrete types
     19 // in fns. Please see golang.org/x/tools/go/callgraph/cha for more information.
     20 //
     21 // The resulting function is not concurrency safe.
     22 func LazyCallees(fns map[*ssa.Function]bool) func(site ssa.CallInstruction) []*ssa.Function {
     23 	// funcsBySig contains all functions, keyed by signature.  It is
     24 	// the effective set of address-taken functions used to resolve
     25 	// a dynamic call of a particular signature.
     26 	var funcsBySig typeutil.Map // value is []*ssa.Function
     27 
     28 	// methodsByID contains all methods, grouped by ID for efficient
     29 	// lookup.
     30 	//
     31 	// We must key by ID, not name, for correct resolution of interface
     32 	// calls to a type with two (unexported) methods spelled the same but
     33 	// from different packages. The fact that the concrete type implements
     34 	// the interface does not mean the call dispatches to both methods.
     35 	methodsByID := make(map[string][]*ssa.Function)
     36 
     37 	// An imethod represents an interface method I.m.
     38 	// (There's no go/types object for it;
     39 	// a *types.Func may be shared by many interfaces due to interface embedding.)
     40 	type imethod struct {
     41 		I  *types.Interface
     42 		id string
     43 	}
     44 	// methodsMemo records, for every abstract method call I.m on
     45 	// interface type I, the set of concrete methods C.m of all
     46 	// types C that satisfy interface I.
     47 	//
     48 	// Abstract methods may be shared by several interfaces,
     49 	// hence we must pass I explicitly, not guess from m.
     50 	//
     51 	// methodsMemo is just a cache, so it needn't be a typeutil.Map.
     52 	methodsMemo := make(map[imethod][]*ssa.Function)
     53 	lookupMethods := func(I *types.Interface, m *types.Func) []*ssa.Function {
     54 		id := m.Id()
     55 		methods, ok := methodsMemo[imethod{I, id}]
     56 		if !ok {
     57 			for _, f := range methodsByID[id] {
     58 				C := f.Signature.Recv().Type() // named or *named
     59 				if types.Implements(C, I) {
     60 					methods = append(methods, f)
     61 				}
     62 			}
     63 			methodsMemo[imethod{I, id}] = methods
     64 		}
     65 		return methods
     66 	}
     67 
     68 	for f := range fns {
     69 		if f.Signature.Recv() == nil {
     70 			// Package initializers can never be address-taken.
     71 			if f.Name() == "init" && f.Synthetic == "package initializer" {
     72 				continue
     73 			}
     74 			funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function)
     75 			funcs = append(funcs, f)
     76 			funcsBySig.Set(f.Signature, funcs)
     77 		} else if obj := f.Object(); obj != nil {
     78 			id := obj.(*types.Func).Id()
     79 			methodsByID[id] = append(methodsByID[id], f)
     80 		}
     81 	}
     82 
     83 	return func(site ssa.CallInstruction) []*ssa.Function {
     84 		call := site.Common()
     85 		if call.IsInvoke() {
     86 			tiface := call.Value.Type().Underlying().(*types.Interface)
     87 			return lookupMethods(tiface, call.Method)
     88 		} else if g := call.StaticCallee(); g != nil {
     89 			return []*ssa.Function{g}
     90 		} else if _, ok := call.Value.(*ssa.Builtin); !ok {
     91 			fns, _ := funcsBySig.At(call.Signature()).([]*ssa.Function)
     92 			return fns
     93 		}
     94 		return nil
     95 	}
     96 }