Initialize module and dependencies

This commit is contained in:
dwrz
2026-02-13 14:59:42 +00:00
commit 0740968bca
390 changed files with 131652 additions and 0 deletions

861
vendor/golang.org/x/tools/go/callgraph/vta/graph.go generated vendored Normal file
View File

@@ -0,0 +1,861 @@
// Copyright 2021 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 vta
import (
"fmt"
"go/token"
"go/types"
"iter"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
)
// node interface for VTA nodes.
type node interface {
Type() types.Type
String() string
}
// constant node for VTA.
type constant struct {
typ types.Type
}
func (c constant) Type() types.Type {
return c.typ
}
func (c constant) String() string {
return fmt.Sprintf("Constant(%v)", c.Type())
}
// pointer node for VTA.
type pointer struct {
typ *types.Pointer
}
func (p pointer) Type() types.Type {
return p.typ
}
func (p pointer) String() string {
return fmt.Sprintf("Pointer(%v)", p.Type())
}
// mapKey node for VTA, modeling reachable map key types.
type mapKey struct {
typ types.Type
}
func (mk mapKey) Type() types.Type {
return mk.typ
}
func (mk mapKey) String() string {
return fmt.Sprintf("MapKey(%v)", mk.Type())
}
// mapValue node for VTA, modeling reachable map value types.
type mapValue struct {
typ types.Type
}
func (mv mapValue) Type() types.Type {
return mv.typ
}
func (mv mapValue) String() string {
return fmt.Sprintf("MapValue(%v)", mv.Type())
}
// sliceElem node for VTA, modeling reachable slice and array element types.
type sliceElem struct {
typ types.Type
}
func (s sliceElem) Type() types.Type {
return s.typ
}
func (s sliceElem) String() string {
return fmt.Sprintf("Slice([]%v)", s.Type())
}
// channelElem node for VTA, modeling reachable channel element types.
type channelElem struct {
typ types.Type
}
func (c channelElem) Type() types.Type {
return c.typ
}
func (c channelElem) String() string {
return fmt.Sprintf("Channel(chan %v)", c.Type())
}
// field node for VTA.
type field struct {
StructType types.Type
index int // index of the field in the struct
}
func (f field) Type() types.Type {
s := typeparams.CoreType(f.StructType).(*types.Struct)
return s.Field(f.index).Type()
}
func (f field) String() string {
s := typeparams.CoreType(f.StructType).(*types.Struct)
return fmt.Sprintf("Field(%v:%s)", f.StructType, s.Field(f.index).Name())
}
// global node for VTA.
type global struct {
val *ssa.Global
}
func (g global) Type() types.Type {
return g.val.Type()
}
func (g global) String() string {
return fmt.Sprintf("Global(%s)", g.val.Name())
}
// local node for VTA modeling local variables
// and function/method parameters.
type local struct {
val ssa.Value
}
func (l local) Type() types.Type {
return l.val.Type()
}
func (l local) String() string {
return fmt.Sprintf("Local(%s)", l.val.Name())
}
// indexedLocal node for VTA node. Models indexed locals
// related to the ssa extract instructions.
type indexedLocal struct {
val ssa.Value
index int
typ types.Type
}
func (i indexedLocal) Type() types.Type {
return i.typ
}
func (i indexedLocal) String() string {
return fmt.Sprintf("Local(%s[%d])", i.val.Name(), i.index)
}
// function node for VTA.
type function struct {
f *ssa.Function
}
func (f function) Type() types.Type {
return f.f.Type()
}
func (f function) String() string {
return fmt.Sprintf("Function(%s)", f.f.Name())
}
// resultVar represents the result
// variable of a function, whether
// named or not.
type resultVar struct {
f *ssa.Function
index int // valid index into result var tuple
}
func (o resultVar) Type() types.Type {
return o.f.Signature.Results().At(o.index).Type()
}
func (o resultVar) String() string {
v := o.f.Signature.Results().At(o.index)
if n := v.Name(); n != "" {
return fmt.Sprintf("Return(%s[%s])", o.f.Name(), n)
}
return fmt.Sprintf("Return(%s[%d])", o.f.Name(), o.index)
}
// nestedPtrInterface node represents all references and dereferences
// of locals and globals that have a nested pointer to interface type.
// We merge such constructs into a single node for simplicity and without
// much precision sacrifice as such variables are rare in practice. Both
// a and b would be represented as the same PtrInterface(I) node in:
//
// type I interface
// var a ***I
// var b **I
type nestedPtrInterface struct {
typ types.Type
}
func (l nestedPtrInterface) Type() types.Type {
return l.typ
}
func (l nestedPtrInterface) String() string {
return fmt.Sprintf("PtrInterface(%v)", l.typ)
}
// nestedPtrFunction node represents all references and dereferences of locals
// and globals that have a nested pointer to function type. We merge such
// constructs into a single node for simplicity and without much precision
// sacrifice as such variables are rare in practice. Both a and b would be
// represented as the same PtrFunction(func()) node in:
//
// var a *func()
// var b **func()
type nestedPtrFunction struct {
typ types.Type
}
func (p nestedPtrFunction) Type() types.Type {
return p.typ
}
func (p nestedPtrFunction) String() string {
return fmt.Sprintf("PtrFunction(%v)", p.typ)
}
// panicArg models types of all arguments passed to panic.
type panicArg struct{}
func (p panicArg) Type() types.Type {
return nil
}
func (p panicArg) String() string {
return "Panic"
}
// recoverReturn models types of all return values of recover().
type recoverReturn struct{}
func (r recoverReturn) Type() types.Type {
return nil
}
func (r recoverReturn) String() string {
return "Recover"
}
type empty = struct{}
// idx is an index representing a unique node in a vtaGraph.
type idx int
// vtaGraph remembers for each VTA node the set of its successors.
// Tailored for VTA, hence does not support singleton (sub)graphs.
type vtaGraph struct {
m []map[idx]empty // m[i] has the successors for the node with index i.
idx map[node]idx // idx[n] is the index for the node n.
node []node // node[i] is the node with index i.
}
func (g *vtaGraph) numNodes() int {
return len(g.idx)
}
func (g *vtaGraph) successors(x idx) iter.Seq[idx] {
return func(yield func(y idx) bool) {
for y := range g.m[x] {
if !yield(y) {
return
}
}
}
}
// addEdge adds an edge x->y to the graph.
func (g *vtaGraph) addEdge(x, y node) {
if g.idx == nil {
g.idx = make(map[node]idx)
}
lookup := func(n node) idx {
i, ok := g.idx[n]
if !ok {
i = idx(len(g.idx))
g.m = append(g.m, nil)
g.idx[n] = i
g.node = append(g.node, n)
}
return i
}
a := lookup(x)
b := lookup(y)
succs := g.m[a]
if succs == nil {
succs = make(map[idx]empty)
g.m[a] = succs
}
succs[b] = empty{}
}
// typePropGraph builds a VTA graph for a set of `funcs` and initial
// `callgraph` needed to establish interprocedural edges. Returns the
// graph and a map for unique type representatives.
func typePropGraph(funcs map[*ssa.Function]bool, callees calleesFunc) (*vtaGraph, *typeutil.Map) {
b := builder{callees: callees}
b.visit(funcs)
b.callees = nil // ensure callees is not pinned by pointers to other fields of b.
return &b.graph, &b.canon
}
// Data structure responsible for linearly traversing the
// code and building a VTA graph.
type builder struct {
graph vtaGraph
callees calleesFunc // initial call graph for creating flows at unresolved call sites.
// Specialized type map for canonicalization of types.Type.
// Semantically equivalent types can have different implementations,
// i.e., they are different pointer values. The map allows us to
// have one unique representative. The keys are fixed and from the
// client perspective they are types. The values in our case are
// types too, in particular type representatives. Each value is a
// pointer so this map is not expected to take much memory.
canon typeutil.Map
}
func (b *builder) visit(funcs map[*ssa.Function]bool) {
// Add the fixed edge Panic -> Recover
b.graph.addEdge(panicArg{}, recoverReturn{})
for f, in := range funcs {
if in {
b.fun(f)
}
}
}
func (b *builder) fun(f *ssa.Function) {
for _, bl := range f.Blocks {
for _, instr := range bl.Instrs {
b.instr(instr)
}
}
}
func (b *builder) instr(instr ssa.Instruction) {
switch i := instr.(type) {
case *ssa.Store:
b.addInFlowAliasEdges(b.nodeFromVal(i.Addr), b.nodeFromVal(i.Val))
case *ssa.MakeInterface:
b.addInFlowEdge(b.nodeFromVal(i.X), b.nodeFromVal(i))
case *ssa.MakeClosure:
b.closure(i)
case *ssa.UnOp:
b.unop(i)
case *ssa.Phi:
b.phi(i)
case *ssa.ChangeInterface:
// Although in change interface a := A(b) command a and b are
// the same object, the only interesting flow happens when A
// is an interface. We create flow b -> a, but omit a -> b.
// The latter flow is not needed: if a gets assigned concrete
// type later on, that cannot be propagated back to b as b
// is a separate variable. The a -> b flow can happen when
// A is a pointer to interface, but then the command is of
// type ChangeType, handled below.
b.addInFlowEdge(b.nodeFromVal(i.X), b.nodeFromVal(i))
case *ssa.ChangeType:
// change type command a := A(b) results in a and b being the
// same value. For concrete type A, there is no interesting flow.
//
// When A is an interface, most interface casts are handled
// by the ChangeInterface instruction. The relevant case here is
// when converting a pointer to an interface type. This can happen
// when the underlying interfaces have the same method set.
//
// type I interface{ foo() }
// type J interface{ foo() }
// var b *I
// a := (*J)(b)
//
// When this happens we add flows between a <--> b.
b.addInFlowAliasEdges(b.nodeFromVal(i), b.nodeFromVal(i.X))
case *ssa.TypeAssert:
b.tassert(i)
case *ssa.Extract:
b.extract(i)
case *ssa.Field:
b.field(i)
case *ssa.FieldAddr:
b.fieldAddr(i)
case *ssa.Send:
b.send(i)
case *ssa.Select:
b.selekt(i)
case *ssa.Index:
b.index(i)
case *ssa.IndexAddr:
b.indexAddr(i)
case *ssa.Lookup:
b.lookup(i)
case *ssa.MapUpdate:
b.mapUpdate(i)
case *ssa.Next:
b.next(i)
case ssa.CallInstruction:
b.call(i)
case *ssa.Panic:
b.panic(i)
case *ssa.Return:
b.rtrn(i)
case *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeSlice, *ssa.BinOp,
*ssa.Alloc, *ssa.DebugRef, *ssa.Convert, *ssa.Jump, *ssa.If,
*ssa.Slice, *ssa.SliceToArrayPointer, *ssa.Range, *ssa.RunDefers:
// No interesting flow here.
// Notes on individual instructions:
// SliceToArrayPointer: t1 = slice to array pointer *[4]T <- []T (t0)
// No interesting flow as sliceArrayElem(t1) == sliceArrayElem(t0).
return
case *ssa.MultiConvert:
b.multiconvert(i)
default:
panic(fmt.Sprintf("unsupported instruction %v\n", instr))
}
}
func (b *builder) unop(u *ssa.UnOp) {
switch u.Op {
case token.MUL:
// Multiplication operator * is used here as a dereference operator.
b.addInFlowAliasEdges(b.nodeFromVal(u), b.nodeFromVal(u.X))
case token.ARROW:
t := typeparams.CoreType(u.X.Type()).(*types.Chan).Elem()
b.addInFlowAliasEdges(b.nodeFromVal(u), channelElem{typ: t})
default:
// There is no interesting type flow otherwise.
}
}
func (b *builder) phi(p *ssa.Phi) {
for _, edge := range p.Edges {
b.addInFlowAliasEdges(b.nodeFromVal(p), b.nodeFromVal(edge))
}
}
func (b *builder) tassert(a *ssa.TypeAssert) {
if !a.CommaOk {
b.addInFlowEdge(b.nodeFromVal(a.X), b.nodeFromVal(a))
return
}
// The case where a is <a.AssertedType, bool> register so there
// is a flow from a.X to a[0]. Here, a[0] is represented as an
// indexedLocal: an entry into local tuple register a at index 0.
tup := a.Type().(*types.Tuple)
t := tup.At(0).Type()
local := indexedLocal{val: a, typ: t, index: 0}
b.addInFlowEdge(b.nodeFromVal(a.X), local)
}
// extract instruction t1 := t2[i] generates flows between t2[i]
// and t1 where the source is indexed local representing a value
// from tuple register t2 at index i and the target is t1.
func (b *builder) extract(e *ssa.Extract) {
tup := e.Tuple.Type().(*types.Tuple)
t := tup.At(e.Index).Type()
local := indexedLocal{val: e.Tuple, typ: t, index: e.Index}
b.addInFlowAliasEdges(b.nodeFromVal(e), local)
}
func (b *builder) field(f *ssa.Field) {
fnode := field{StructType: f.X.Type(), index: f.Field}
b.addInFlowEdge(fnode, b.nodeFromVal(f))
}
func (b *builder) fieldAddr(f *ssa.FieldAddr) {
t := typeparams.CoreType(f.X.Type()).(*types.Pointer).Elem()
// Since we are getting pointer to a field, make a bidirectional edge.
fnode := field{StructType: t, index: f.Field}
b.addInFlowEdge(fnode, b.nodeFromVal(f))
b.addInFlowEdge(b.nodeFromVal(f), fnode)
}
func (b *builder) send(s *ssa.Send) {
t := typeparams.CoreType(s.Chan.Type()).(*types.Chan).Elem()
b.addInFlowAliasEdges(channelElem{typ: t}, b.nodeFromVal(s.X))
}
// selekt generates flows for select statement
//
// a = select blocking/nonblocking [c_1 <- t_1, c_2 <- t_2, ..., <- o_1, <- o_2, ...]
//
// between receiving channel registers c_i and corresponding input register t_i. Further,
// flows are generated between o_i and a[2 + i]. Note that a is a tuple register of type
// <int, bool, r_1, r_2, ...> where the type of r_i is the element type of channel o_i.
func (b *builder) selekt(s *ssa.Select) {
recvIndex := 0
for _, state := range s.States {
t := typeparams.CoreType(state.Chan.Type()).(*types.Chan).Elem()
if state.Dir == types.SendOnly {
b.addInFlowAliasEdges(channelElem{typ: t}, b.nodeFromVal(state.Send))
} else {
// state.Dir == RecvOnly by definition of select instructions.
tupEntry := indexedLocal{val: s, typ: t, index: 2 + recvIndex}
b.addInFlowAliasEdges(tupEntry, channelElem{typ: t})
recvIndex++
}
}
}
// index instruction a := b[c] on slices creates flows between a and
// SliceElem(t) flow where t is an interface type of c. Arrays and
// slice elements are both modeled as SliceElem.
func (b *builder) index(i *ssa.Index) {
et := sliceArrayElem(i.X.Type())
b.addInFlowAliasEdges(b.nodeFromVal(i), sliceElem{typ: et})
}
// indexAddr instruction a := &b[c] fetches address of a index
// into the field so we create bidirectional flow a <-> SliceElem(t)
// where t is an interface type of c. Arrays and slice elements are
// both modeled as SliceElem.
func (b *builder) indexAddr(i *ssa.IndexAddr) {
et := sliceArrayElem(i.X.Type())
b.addInFlowEdge(sliceElem{typ: et}, b.nodeFromVal(i))
b.addInFlowEdge(b.nodeFromVal(i), sliceElem{typ: et})
}
// lookup handles map query commands a := m[b] where m is of type
// map[...]V and V is an interface. It creates flows between `a`
// and MapValue(V).
func (b *builder) lookup(l *ssa.Lookup) {
t, ok := l.X.Type().Underlying().(*types.Map)
if !ok {
// No interesting flows for string lookups.
return
}
if !l.CommaOk {
b.addInFlowAliasEdges(b.nodeFromVal(l), mapValue{typ: t.Elem()})
} else {
i := indexedLocal{val: l, typ: t.Elem(), index: 0}
b.addInFlowAliasEdges(i, mapValue{typ: t.Elem()})
}
}
// mapUpdate handles map update commands m[b] = a where m is of type
// map[K]V and K and V are interfaces. It creates flows between `a`
// and MapValue(V) as well as between MapKey(K) and `b`.
func (b *builder) mapUpdate(u *ssa.MapUpdate) {
t, ok := u.Map.Type().Underlying().(*types.Map)
if !ok {
// No interesting flows for string updates.
return
}
b.addInFlowAliasEdges(mapKey{typ: t.Key()}, b.nodeFromVal(u.Key))
b.addInFlowAliasEdges(mapValue{typ: t.Elem()}, b.nodeFromVal(u.Value))
}
// next instruction <ok, key, value> := next r, where r
// is a range over map or string generates flow between
// key and MapKey as well value and MapValue nodes.
func (b *builder) next(n *ssa.Next) {
if n.IsString {
return
}
tup := n.Type().(*types.Tuple)
kt := tup.At(1).Type()
vt := tup.At(2).Type()
b.addInFlowAliasEdges(indexedLocal{val: n, typ: kt, index: 1}, mapKey{typ: kt})
b.addInFlowAliasEdges(indexedLocal{val: n, typ: vt, index: 2}, mapValue{typ: vt})
}
// addInFlowAliasEdges adds an edge r -> l to b.graph if l is a node that can
// have an inflow, i.e., a node that represents an interface or an unresolved
// function value. Similarly for the edge l -> r with an additional condition
// of that l and r can potentially alias.
func (b *builder) addInFlowAliasEdges(l, r node) {
b.addInFlowEdge(r, l)
if canAlias(l, r) {
b.addInFlowEdge(l, r)
}
}
func (b *builder) closure(c *ssa.MakeClosure) {
f := c.Fn.(*ssa.Function)
b.addInFlowEdge(function{f: f}, b.nodeFromVal(c))
for i, fv := range f.FreeVars {
b.addInFlowAliasEdges(b.nodeFromVal(fv), b.nodeFromVal(c.Bindings[i]))
}
}
// panic creates a flow from arguments to panic instructions to return
// registers of all recover statements in the program. Introduces a
// global panic node Panic and
// 1. for every panic statement p: add p -> Panic
// 2. for every recover statement r: add Panic -> r (handled in call)
//
// TODO(zpavlinovic): improve precision by explicitly modeling how panic
// values flow from callees to callers and into deferred recover instructions.
func (b *builder) panic(p *ssa.Panic) {
// Panics often have, for instance, strings as arguments which do
// not create interesting flows.
if !canHaveMethods(p.X.Type()) {
return
}
b.addInFlowEdge(b.nodeFromVal(p.X), panicArg{})
}
// call adds flows between arguments/parameters and return values/registers
// for both static and dynamic calls, as well as go and defer calls.
func (b *builder) call(c ssa.CallInstruction) {
// When c is r := recover() call register instruction, we add Recover -> r.
if bf, ok := c.Common().Value.(*ssa.Builtin); ok && bf.Name() == "recover" {
if v, ok := c.(ssa.Value); ok {
b.addInFlowEdge(recoverReturn{}, b.nodeFromVal(v))
}
return
}
for f := range siteCallees(c, b.callees) {
addArgumentFlows(b, c, f)
site, ok := c.(ssa.Value)
if !ok {
continue // go or defer
}
results := f.Signature.Results()
if results.Len() == 1 {
// When there is only one return value, the destination register does not
// have a tuple type.
b.addInFlowEdge(resultVar{f: f, index: 0}, b.nodeFromVal(site))
} else {
tup := site.Type().(*types.Tuple)
for i := 0; i < results.Len(); i++ {
local := indexedLocal{val: site, typ: tup.At(i).Type(), index: i}
b.addInFlowEdge(resultVar{f: f, index: i}, local)
}
}
}
}
func addArgumentFlows(b *builder, c ssa.CallInstruction, f *ssa.Function) {
// When f has no parameters (including receiver), there is no type
// flow here. Also, f's body and parameters might be missing, such
// as when vta is used within the golang.org/x/tools/go/analysis
// framework (see github.com/golang/go/issues/50670).
if len(f.Params) == 0 {
return
}
cc := c.Common()
if cc.Method != nil {
// In principle we don't add interprocedural flows for receiver
// objects. At a call site, the receiver object is interface
// while the callee object is concrete. The flow from interface
// to concrete type in general does not make sense. The exception
// is when the concrete type is a named function type (see #57756).
//
// The flow other way around would bake in information from the
// initial call graph.
if isFunction(f.Params[0].Type()) {
b.addInFlowEdge(b.nodeFromVal(cc.Value), b.nodeFromVal(f.Params[0]))
}
}
offset := 0
if cc.Method != nil {
offset = 1
}
for i, v := range cc.Args {
// Parameters of f might not be available, as in the case
// when vta is used within the golang.org/x/tools/go/analysis
// framework (see github.com/golang/go/issues/50670).
//
// TODO: investigate other cases of missing body and parameters
if len(f.Params) <= i+offset {
return
}
b.addInFlowAliasEdges(b.nodeFromVal(f.Params[i+offset]), b.nodeFromVal(v))
}
}
// rtrn creates flow edges from the operands of the return
// statement to the result variables of the enclosing function.
func (b *builder) rtrn(r *ssa.Return) {
for i, rs := range r.Results {
b.addInFlowEdge(b.nodeFromVal(rs), resultVar{f: r.Parent(), index: i})
}
}
func (b *builder) multiconvert(c *ssa.MultiConvert) {
// TODO(zpavlinovic): decide what to do on MultiConvert long term.
// TODO(zpavlinovic): add unit tests.
typeSetOf := func(typ types.Type) []*types.Term {
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
var terms []*types.Term
var err error
switch typ := types.Unalias(typ).(type) {
case *types.TypeParam:
terms, err = typeparams.StructuralTerms(typ)
case *types.Union:
terms, err = typeparams.UnionTermSet(typ)
case *types.Interface:
terms, err = typeparams.InterfaceTermSet(typ)
default:
// Common case.
// Specializing the len=1 case to avoid a slice
// had no measurable space/time benefit.
terms = []*types.Term{types.NewTerm(false, typ)}
}
if err != nil {
return nil
}
return terms
}
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named or alias type.
isValuePreserving := func(ut_src, ut_dst types.Type) bool {
// Identical underlying types?
if types.IdenticalIgnoreTags(ut_dst, ut_src) {
return true
}
switch ut_dst.(type) {
case *types.Chan:
// Conversion between channel types?
_, ok := ut_src.(*types.Chan)
return ok
case *types.Pointer:
// Conversion between pointers with identical base types?
_, ok := ut_src.(*types.Pointer)
return ok
}
return false
}
dst_terms := typeSetOf(c.Type())
src_terms := typeSetOf(c.X.Type())
for _, s := range src_terms {
us := s.Type().Underlying()
for _, d := range dst_terms {
ud := d.Type().Underlying()
if isValuePreserving(us, ud) {
// This is equivalent to a ChangeType.
b.addInFlowAliasEdges(b.nodeFromVal(c), b.nodeFromVal(c.X))
return
}
// This is equivalent to either: SliceToArrayPointer,,
// SliceToArrayPointer+Deref, Size 0 Array constant, or a Convert.
}
}
}
// addInFlowEdge adds s -> d to g if d is node that can have an inflow, i.e., a node
// that represents an interface or an unresolved function value. Otherwise, there
// is no interesting type flow so the edge is omitted.
func (b *builder) addInFlowEdge(s, d node) {
if hasInFlow(d) {
b.graph.addEdge(b.representative(s), b.representative(d))
}
}
// Creates const, pointer, global, func, and local nodes based on register instructions.
func (b *builder) nodeFromVal(val ssa.Value) node {
if p, ok := types.Unalias(val.Type()).(*types.Pointer); ok && !types.IsInterface(p.Elem()) && !isFunction(p.Elem()) {
// Nested pointer to interfaces are modeled as a special
// nestedPtrInterface node.
if i := interfaceUnderPtr(p.Elem()); i != nil {
return nestedPtrInterface{typ: i}
}
// The same goes for nested function types.
if f := functionUnderPtr(p.Elem()); f != nil {
return nestedPtrFunction{typ: f}
}
return pointer{typ: p}
}
switch v := val.(type) {
case *ssa.Const:
return constant{typ: val.Type()}
case *ssa.Global:
return global{val: v}
case *ssa.Function:
return function{f: v}
case *ssa.Parameter, *ssa.FreeVar, ssa.Instruction:
// ssa.Param, ssa.FreeVar, and a specific set of "register" instructions,
// satisfying the ssa.Value interface, can serve as local variables.
return local{val: v}
default:
panic(fmt.Errorf("unsupported value %v in node creation", val))
}
}
// representative returns a unique representative for node `n`. Since
// semantically equivalent types can have different implementations,
// this method guarantees the same implementation is always used.
func (b *builder) representative(n node) node {
if n.Type() == nil {
// panicArg and recoverReturn do not have
// types and are unique by definition.
return n
}
t := canonicalize(n.Type(), &b.canon)
switch i := n.(type) {
case constant:
return constant{typ: t}
case pointer:
return pointer{typ: t.(*types.Pointer)}
case sliceElem:
return sliceElem{typ: t}
case mapKey:
return mapKey{typ: t}
case mapValue:
return mapValue{typ: t}
case channelElem:
return channelElem{typ: t}
case nestedPtrInterface:
return nestedPtrInterface{typ: t}
case nestedPtrFunction:
return nestedPtrFunction{typ: t}
case field:
return field{StructType: canonicalize(i.StructType, &b.canon), index: i.index}
case indexedLocal:
return indexedLocal{typ: t, val: i.val, index: i.index}
case local, global, panicArg, recoverReturn, function, resultVar:
return n
default:
panic(fmt.Errorf("canonicalizing unrecognized node %v", n))
}
}
// canonicalize returns a type representative of `t` unique subject
// to type map `canon`.
func canonicalize(t types.Type, canon *typeutil.Map) types.Type {
rep := canon.At(t)
if rep != nil {
return rep.(types.Type)
}
canon.Set(t, t)
return t
}

37
vendor/golang.org/x/tools/go/callgraph/vta/initial.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
// Copyright 2024 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 vta
import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/internal/chautil"
"golang.org/x/tools/go/ssa"
)
// calleesFunc abstracts call graph in one direction,
// from call sites to callees.
type calleesFunc func(ssa.CallInstruction) []*ssa.Function
// makeCalleesFunc returns an initial call graph for vta as a
// calleesFunc. If c is not nil, returns callees as given by c.
// Otherwise, it returns chautil.LazyCallees over fs.
func makeCalleesFunc(fs map[*ssa.Function]bool, c *callgraph.Graph) calleesFunc {
if c == nil {
return chautil.LazyCallees(fs)
}
return func(call ssa.CallInstruction) []*ssa.Function {
node := c.Nodes[call.Parent()]
if node == nil {
return nil
}
var cs []*ssa.Function
for _, edge := range node.Out {
if edge.Site == call {
cs = append(cs, edge.Callee.Func)
}
}
return cs
}
}

View File

@@ -0,0 +1,127 @@
// Copyright 2021 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 trie
import (
"math/bits"
)
// This file contains bit twiddling functions for Patricia tries.
// Consult this paper for details.
// C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
// Workshop on ML, September 1998, pp. 7786.
// key is a key in a Map.
type key uint64
// bitpos is the position of a bit. A position is represented by having a 1
// bit in that position.
// Examples:
// - 0b0010 is the position of the `1` bit in 2.
// It is the 3rd most specific bit position in big endian encoding
// (0b0 and 0b1 are more specific).
// - 0b0100 is the position of the bit that 1 and 5 disagree on.
// - 0b0 is a special value indicating that all bit agree.
type bitpos uint64
// prefixes represent a set of keys that all agree with the
// prefix up to a bitpos m.
//
// The value for a prefix is determined by the mask(k, m) function.
// (See mask for details on the values.)
// A `p` prefix for position `m` matches a key `k` iff mask(k, m) == p.
// A prefix always mask(p, m) == p.
//
// A key is its own prefix for the bit position 64,
// e.g. seeing a `prefix(key)` is not a problem.
//
// Prefixes should never be turned into keys.
type prefix uint64
// branchingBit returns the position of the first bit in `x` and `y`
// that are not equal.
func branchingBit(x, y prefix) bitpos {
p := x ^ y
if p == 0 {
return 0
}
return bitpos(1) << uint(bits.Len64(uint64(p))-1) // uint conversion needed for go1.12
}
// zeroBit returns true if k has a 0 bit at position `b`.
func zeroBit(k prefix, b bitpos) bool {
return (uint64(k) & uint64(b)) == 0
}
// matchPrefix returns true if a prefix k matches a prefix p up to position `b`.
func matchPrefix(k prefix, p prefix, b bitpos) bool {
return mask(k, b) == p
}
// mask returns a prefix of `k` with all bits after and including `b` zeroed out.
//
// In big endian encoding, this value is the [64-(m-1)] most significant bits of k
// followed by a `0` bit at bitpos m, followed m-1 `1` bits.
// Examples:
//
// prefix(0b1011) for a bitpos 0b0100 represents the keys:
// 0b1000, 0b1001, 0b1010, 0b1011, 0b1100, 0b1101, 0b1110, 0b1111
//
// This mask function has the property that if matchPrefix(k, p, b), then
// k <= p if and only if zeroBit(k, m). This induces binary search tree tries.
// See Okasaki & Gill for more details about this choice of mask function.
//
// mask is idempotent for a given `b`, i.e. mask(mask(p, b), b) == mask(p,b).
func mask(k prefix, b bitpos) prefix {
return prefix((uint64(k) | (uint64(b) - 1)) & (^uint64(b)))
}
// ord returns true if m comes before n in the bit ordering.
func ord(m, n bitpos) bool {
return m > n // big endian encoding
}
// prefixesOverlap returns true if there is some key a prefix `p` for bitpos `m`
// can hold that can also be held by a prefix `q` for some bitpos `n`.
//
// This is equivalent to:
//
// m ==n && p == q,
// higher(m, n) && matchPrefix(q, p, m), or
// higher(n, m) && matchPrefix(p, q, n)
func prefixesOverlap(p prefix, m bitpos, q prefix, n bitpos) bool {
fbb := n
if ord(m, n) {
fbb = m
}
return mask(p, fbb) == mask(q, fbb)
// Lemma:
// mask(p, fbb) == mask(q, fbb)
// iff
// m > n && matchPrefix(q, p, m) or (note: big endian encoding)
// m < n && matchPrefix(p, q, n) or (note: big endian encoding)
// m ==n && p == q
// Quick-n-dirty proof:
// p == mask(p0, m) for some p0 by precondition.
// q == mask(q0, n) for some q0 by precondition.
// So mask(p, m) == p and mask(q, n) == q as mask(*, n') is idempotent.
//
// [=> proof]
// Suppose mask(p, fbb) == mask(q, fbb).
// if m ==n, p == mask(p, m) == mask(p, fbb) == mask(q, fbb) == mask(q, n) == q
// if m > n, fbb = firstBranchBit(m, n) = m (big endian).
// p == mask(p, m) == mask(p, fbb) == mask(q, fbb) == mask(q, m)
// so mask(q, m) == p or matchPrefix(q, p, m)
// if m < n, is symmetric to the above.
//
// [<= proof]
// case m ==n && p == q. Then mask(p, fbb) == mask(q, fbb)
//
// case m > n && matchPrefix(q, p, m).
// fbb == firstBranchBit(m, n) == m (by m>n).
// mask(q, fbb) == mask(q, m) == p == mask(p, m) == mask(p, fbb)
//
// case m < n && matchPrefix(p, q, n) is symmetric.
}

View File

@@ -0,0 +1,516 @@
// Copyright 2021 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 trie
// Collision functions combine a left and right hand side (lhs and rhs) values
// the two values are associated with the same key and produces the value that
// will be stored for the key.
//
// Collision functions must be idempotent:
//
// collision(x, x) == x for all x.
//
// Collisions functions may be applied whenever a value is inserted
// or two maps are merged, or intersected.
type Collision func(lhs any, rhs any) any
// TakeLhs always returns the left value in a collision.
func TakeLhs(lhs, rhs any) any { return lhs }
// TakeRhs always returns the right hand side in a collision.
func TakeRhs(lhs, rhs any) any { return rhs }
// Builder creates new Map. Each Builder has a unique Scope.
//
// IMPORTANT: Nodes are hash-consed internally to reduce memory consumption. To
// support hash-consing Builders keep an internal Map of all of the Maps that they
// have created. To GC any of the Maps created by the Builder, all references to
// the Builder must be dropped. This includes MutMaps.
type Builder struct {
scope Scope
// hash-consing maps for each node type.
empty *empty
leaves map[leaf]*leaf
branches map[branch]*branch
// It may be possible to support more types of patricia tries
// (e.g. non-hash-consed) by making Builder an interface and abstracting
// the mkLeaf and mkBranch functions.
}
// NewBuilder creates a new Builder with a unique Scope.
func NewBuilder() *Builder {
s := newScope()
return &Builder{
scope: s,
empty: &empty{s},
leaves: make(map[leaf]*leaf),
branches: make(map[branch]*branch),
}
}
func (b *Builder) Scope() Scope { return b.scope }
// Rescope changes the builder's scope to a new unique Scope.
//
// Any Maps created using the previous scope need to be Cloned
// before any operation.
//
// This makes the old internals of the Builder eligible to be GC'ed.
func (b *Builder) Rescope() {
s := newScope()
b.scope = s
b.empty = &empty{s}
b.leaves = make(map[leaf]*leaf)
b.branches = make(map[branch]*branch)
}
// Empty is the empty map.
func (b *Builder) Empty() Map { return Map{b.Scope(), b.empty} }
// InsertWith inserts a new association from k to v into the Map m to create a new map
// in the current scope and handle collisions using the collision function c.
//
// This is roughly corresponds to updating a map[uint64]interface{} by:
//
// if _, ok := m[k]; ok { m[k] = c(m[k], v} else { m[k] = v}
//
// An insertion or update happened whenever Insert(m, ...) != m .
func (b *Builder) InsertWith(c Collision, m Map, k uint64, v any) Map {
m = b.Clone(m)
return Map{b.Scope(), b.insert(c, m.n, b.mkLeaf(key(k), v), false)}
}
// Inserts a new association from key to value into the Map m to create
// a new map in the current scope.
//
// If there was a previous value mapped by key, keep the previously mapped value.
// This is roughly corresponds to updating a map[uint64]interface{} by:
//
// if _, ok := m[k]; ok { m[k] = val }
//
// This is equivalent to b.Merge(m, b.Create({k: v})).
func (b *Builder) Insert(m Map, k uint64, v any) Map {
return b.InsertWith(TakeLhs, m, k, v)
}
// Updates a (key, value) in the map. This is roughly corresponds to
// updating a map[uint64]interface{} by:
//
// m[key] = val
func (b *Builder) Update(m Map, key uint64, val any) Map {
return b.InsertWith(TakeRhs, m, key, val)
}
// Merge two maps lhs and rhs to create a new map in the current scope.
//
// Whenever there is a key in both maps (a collision), the resulting value mapped by
// the key will be `c(lhs[key], rhs[key])`.
func (b *Builder) MergeWith(c Collision, lhs, rhs Map) Map {
lhs, rhs = b.Clone(lhs), b.Clone(rhs)
return Map{b.Scope(), b.merge(c, lhs.n, rhs.n)}
}
// Merge two maps lhs and rhs to create a new map in the current scope.
//
// Whenever there is a key in both maps (a collision), the resulting value mapped by
// the key will be the value in lhs `b.Collision(lhs[key], rhs[key])`.
func (b *Builder) Merge(lhs, rhs Map) Map {
return b.MergeWith(TakeLhs, lhs, rhs)
}
// Clone returns a Map that contains the same (key, value) elements
// within b.Scope(), i.e. return m if m.Scope() == b.Scope() or return
// a deep copy of m within b.Scope() otherwise.
func (b *Builder) Clone(m Map) Map {
if m.Scope() == b.Scope() {
return m
} else if m.n == nil {
return Map{b.Scope(), b.empty}
}
return Map{b.Scope(), b.clone(m.n)}
}
func (b *Builder) clone(n node) node {
switch n := n.(type) {
case *empty:
return b.empty
case *leaf:
return b.mkLeaf(n.k, n.v)
case *branch:
return b.mkBranch(n.prefix, n.branching, b.clone(n.left), b.clone(n.right))
default:
panic("unreachable")
}
}
// Remove a key from a Map m and return the resulting Map.
func (b *Builder) Remove(m Map, k uint64) Map {
m = b.Clone(m)
return Map{b.Scope(), b.remove(m.n, key(k))}
}
// Intersect Maps lhs and rhs and returns a map with all of the keys in
// both lhs and rhs and the value comes from lhs, i.e.
//
// {(k, lhs[k]) | k in lhs, k in rhs}.
func (b *Builder) Intersect(lhs, rhs Map) Map {
return b.IntersectWith(TakeLhs, lhs, rhs)
}
// IntersectWith take lhs and rhs and returns the intersection
// with the value coming from the collision function, i.e.
//
// {(k, c(lhs[k], rhs[k]) ) | k in lhs, k in rhs}.
//
// The elements of the resulting map are always { <k, c(lhs[k], rhs[k]) > }
// for each key k that a key in both lhs and rhs.
func (b *Builder) IntersectWith(c Collision, lhs, rhs Map) Map {
l, r := b.Clone(lhs), b.Clone(rhs)
return Map{b.Scope(), b.intersect(c, l.n, r.n)}
}
// MutMap is a convenient wrapper for a Map and a *Builder that will be used to create
// new Maps from it.
type MutMap struct {
B *Builder
M Map
}
// MutEmpty is an empty MutMap for a builder.
func (b *Builder) MutEmpty() MutMap {
return MutMap{b, b.Empty()}
}
// Insert an element into the map using the collision function for the builder.
// Returns true if the element was inserted.
func (mm *MutMap) Insert(k uint64, v any) bool {
old := mm.M
mm.M = mm.B.Insert(old, k, v)
return old != mm.M
}
// Updates an element in the map. Returns true if the map was updated.
func (mm *MutMap) Update(k uint64, v any) bool {
old := mm.M
mm.M = mm.B.Update(old, k, v)
return old != mm.M
}
// Removes a key from the map. Returns true if the element was removed.
func (mm *MutMap) Remove(k uint64) bool {
old := mm.M
mm.M = mm.B.Remove(old, k)
return old != mm.M
}
// Merge another map into the current one using the collision function
// for the builder. Returns true if the map changed.
func (mm *MutMap) Merge(other Map) bool {
old := mm.M
mm.M = mm.B.Merge(old, other)
return old != mm.M
}
// Intersect another map into the current one using the collision function
// for the builder. Returns true if the map changed.
func (mm *MutMap) Intersect(other Map) bool {
old := mm.M
mm.M = mm.B.Intersect(old, other)
return old != mm.M
}
func (b *Builder) Create(m map[uint64]any) Map {
var leaves []*leaf
for k, v := range m {
leaves = append(leaves, b.mkLeaf(key(k), v))
}
return Map{b.Scope(), b.create(leaves)}
}
// Merge another map into the current one using the collision function
// for the builder. Returns true if the map changed.
func (mm *MutMap) MergeWith(c Collision, other Map) bool {
old := mm.M
mm.M = mm.B.MergeWith(c, old, other)
return old != mm.M
}
// creates a map for a collection of leaf nodes.
func (b *Builder) create(leaves []*leaf) node {
n := len(leaves)
if n == 0 {
return b.empty
} else if n == 1 {
return leaves[0]
}
// Note: we can do a more sophisticated algorithm by:
// - sorting the leaves ahead of time,
// - taking the prefix and branching bit of the min and max key,
// - binary searching for the branching bit,
// - splitting exactly where the branch will be, and
// - making the branch node for this prefix + branching bit.
// Skipping until this is a performance bottleneck.
m := n / 2 // (n >= 2) ==> 1 <= m < n
l, r := leaves[:m], leaves[m:]
return b.merge(nil, b.create(l), b.create(r))
}
// mkLeaf returns the hash-consed representative of (k, v) in the current scope.
func (b *Builder) mkLeaf(k key, v any) *leaf {
rep, ok := b.leaves[leaf{k, v}]
if !ok {
rep = &leaf{k, v} // heap-allocated copy
b.leaves[leaf{k, v}] = rep
}
return rep
}
// mkBranch returns the hash-consed representative of the tuple
//
// (prefix, branch, left, right)
//
// in the current scope.
func (b *Builder) mkBranch(p prefix, bp bitpos, left node, right node) *branch {
br := branch{
sz: left.size() + right.size(),
prefix: p,
branching: bp,
left: left,
right: right,
}
rep, ok := b.branches[br]
if !ok {
rep = new(branch) // heap-allocated copy
*rep = br
b.branches[br] = rep
}
return rep
}
// join two maps with prefixes p0 and p1 that are *known* to disagree.
func (b *Builder) join(p0 prefix, t0 node, p1 prefix, t1 node) *branch {
m := branchingBit(p0, p1)
var left, right node
if zeroBit(p0, m) {
left, right = t0, t1
} else {
left, right = t1, t0
}
prefix := mask(p0, m)
return b.mkBranch(prefix, m, left, right)
}
// collide two leaves with the same key to create a leaf
// with the collided value.
func (b *Builder) collide(c Collision, left, right *leaf) *leaf {
if left == right {
return left // c is idempotent: c(x, x) == x
}
val := left.v // keep the left value by default if c is nil
if c != nil {
val = c(left.v, right.v)
}
switch val {
case left.v:
return left
case right.v:
return right
default:
return b.mkLeaf(left.k, val)
}
}
// inserts a leaf l into a map m and returns the resulting map.
// When lhs is true, l is the left hand side in a collision.
// Both l and m are in the current scope.
func (b *Builder) insert(c Collision, m node, l *leaf, lhs bool) node {
switch m := m.(type) {
case *empty:
return l
case *leaf:
if m.k == l.k {
left, right := l, m
if !lhs {
left, right = right, left
}
return b.collide(c, left, right)
}
return b.join(prefix(l.k), l, prefix(m.k), m)
case *branch:
// fallthrough
}
// m is a branch
br := m.(*branch)
if !matchPrefix(prefix(l.k), br.prefix, br.branching) {
return b.join(prefix(l.k), l, br.prefix, br)
}
var left, right node
if zeroBit(prefix(l.k), br.branching) {
left, right = b.insert(c, br.left, l, lhs), br.right
} else {
left, right = br.left, b.insert(c, br.right, l, lhs)
}
if left == br.left && right == br.right {
return m
}
return b.mkBranch(br.prefix, br.branching, left, right)
}
// merge two maps in the current scope.
func (b *Builder) merge(c Collision, lhs, rhs node) node {
if lhs == rhs {
return lhs
}
switch lhs := lhs.(type) {
case *empty:
return rhs
case *leaf:
return b.insert(c, rhs, lhs, true)
case *branch:
switch rhs := rhs.(type) {
case *empty:
return lhs
case *leaf:
return b.insert(c, lhs, rhs, false)
case *branch:
// fallthrough
}
}
// Last remaining case is branch merging.
// For brevity, we adopt the Okasaki and Gill naming conventions
// for branching and prefixes.
s, t := lhs.(*branch), rhs.(*branch)
p, m := s.prefix, s.branching
q, n := t.prefix, t.branching
if m == n && p == q { // prefixes are identical.
left, right := b.merge(c, s.left, t.left), b.merge(c, s.right, t.right)
return b.mkBranch(p, m, left, right)
}
if !prefixesOverlap(p, m, q, n) {
return b.join(p, s, q, t) // prefixes are disjoint.
}
// prefixesOverlap(p, m, q, n) && !(m ==n && p == q)
// By prefixesOverlap(...), either:
// higher(m, n) && matchPrefix(q, p, m), or
// higher(n, m) && matchPrefix(p, q, n)
// So either s or t may can be merged with one branch or the other.
switch {
case ord(m, n) && zeroBit(q, m):
return b.mkBranch(p, m, b.merge(c, s.left, t), s.right)
case ord(m, n) && !zeroBit(q, m):
return b.mkBranch(p, m, s.left, b.merge(c, s.right, t))
case ord(n, m) && zeroBit(p, n):
return b.mkBranch(q, n, b.merge(c, s, t.left), t.right)
default:
return b.mkBranch(q, n, t.left, b.merge(c, s, t.right))
}
}
func (b *Builder) remove(m node, k key) node {
switch m := m.(type) {
case *empty:
return m
case *leaf:
if m.k == k {
return b.empty
}
return m
case *branch:
// fallthrough
}
br := m.(*branch)
kp := prefix(k)
if !matchPrefix(kp, br.prefix, br.branching) {
// The prefix does not match. kp is not in br.
return br
}
// the prefix matches. try to remove from the left or right branch.
left, right := br.left, br.right
if zeroBit(kp, br.branching) {
left = b.remove(left, k) // k may be in the left branch.
} else {
right = b.remove(right, k) // k may be in the right branch.
}
if left == br.left && right == br.right {
return br // no update
} else if _, ok := left.(*empty); ok {
return right // left updated and is empty.
} else if _, ok := right.(*empty); ok {
return left // right updated and is empty.
}
// Either left or right updated. Both left and right are not empty.
// The left and right branches still share the same prefix and disagree
// on the same branching bit. It is safe to directly create the branch.
return b.mkBranch(br.prefix, br.branching, left, right)
}
func (b *Builder) intersect(c Collision, l, r node) node {
if l == r {
return l
}
switch l := l.(type) {
case *empty:
return b.empty
case *leaf:
if rleaf := r.find(l.k); rleaf != nil {
return b.collide(c, l, rleaf)
}
return b.empty
case *branch:
switch r := r.(type) {
case *empty:
return b.empty
case *leaf:
if lleaf := l.find(r.k); lleaf != nil {
return b.collide(c, lleaf, r)
}
return b.empty
case *branch:
// fallthrough
}
}
// Last remaining case is branch intersection.
s, t := l.(*branch), r.(*branch)
p, m := s.prefix, s.branching
q, n := t.prefix, t.branching
if m == n && p == q {
// prefixes are identical.
left, right := b.intersect(c, s.left, t.left), b.intersect(c, s.right, t.right)
if _, ok := left.(*empty); ok {
return right
} else if _, ok := right.(*empty); ok {
return left
}
// The left and right branches are both non-empty.
// They still share the same prefix and disagree on the same branching bit.
// It is safe to directly create the branch.
return b.mkBranch(p, m, left, right)
}
if !prefixesOverlap(p, m, q, n) {
return b.empty // The prefixes share no keys.
}
// prefixesOverlap(p, m, q, n) && !(m ==n && p == q)
// By prefixesOverlap(...), either:
// ord(m, n) && matchPrefix(q, p, m), or
// ord(n, m) && matchPrefix(p, q, n)
// So either s or t may be a strict subtree of the other.
var lhs, rhs node
switch {
case ord(m, n) && zeroBit(q, m):
lhs, rhs = s.left, t
case ord(m, n) && !zeroBit(q, m):
lhs, rhs = s.right, t
case ord(n, m) && zeroBit(p, n):
lhs, rhs = s, t.left
default:
lhs, rhs = s, t.right
}
return b.intersect(c, lhs, rhs)
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 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 trie
import (
"strconv"
"sync/atomic"
)
// Scope represents a distinct collection of maps.
// Maps with the same Scope can be equal. Maps in different scopes are distinct.
// Each Builder creates maps within a unique Scope.
type Scope struct {
id int32
}
var nextScopeId int32
func newScope() Scope {
id := atomic.AddInt32(&nextScopeId, 1)
return Scope{id: id}
}
func (s Scope) String() string {
return strconv.Itoa(int(s.id))
}

View File

@@ -0,0 +1,229 @@
// Copyright 2021 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.
// trie implements persistent Patricia trie maps.
//
// Each Map is effectively a map from uint64 to interface{}. Patricia tries are
// a form of radix tree that are particularly appropriate when many maps will be
// created, merged together and large amounts of sharing are expected (e.g.
// environment abstract domains in program analysis).
//
// This implementation closely follows the paper:
//
// C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
// Workshop on ML, September 1998, pp. 7786.
//
// Each Map is immutable and can be read from concurrently. The map does not
// guarantee that the value pointed to by the interface{} value is not updated
// concurrently.
//
// These Maps are optimized for situations where there will be many maps created at
// with a high degree of sharing and combining of maps together. If you do not expect,
// significant amount of sharing, the builtin map[T]U is much better choice!
//
// Each Map is created by a Builder. Each Builder has a unique Scope and each node is
// created within this scope. Maps x and y are == if they contains the same
// (key,value) mappings and have equal scopes.
//
// Internally these are big endian Patricia trie nodes, and the keys are sorted.
package trie
import (
"fmt"
"strings"
)
// Map is effectively a finite mapping from uint64 keys to interface{} values.
// Maps are immutable and can be read from concurrently.
//
// Notes on concurrency:
// - A Map value itself is an interface and assignments to a Map value can race.
// - Map does not guarantee that the value pointed to by the interface{} value
// is not updated concurrently.
type Map struct {
s Scope
n node
}
func (m Map) Scope() Scope {
return m.s
}
func (m Map) Size() int {
if m.n == nil {
return 0
}
return m.n.size()
}
func (m Map) Lookup(k uint64) (any, bool) {
if m.n != nil {
if leaf := m.n.find(key(k)); leaf != nil {
return leaf.v, true
}
}
return nil, false
}
// Converts the map into a {<key>: <value>[, ...]} string. This uses the default
// %s string conversion for <value>.
func (m Map) String() string {
var kvs []string
m.Range(func(u uint64, i any) bool {
kvs = append(kvs, fmt.Sprintf("%d: %s", u, i))
return true
})
return fmt.Sprintf("{%s}", strings.Join(kvs, ", "))
}
// Range over the leaf (key, value) pairs in the map in order and
// applies cb(key, value) to each. Stops early if cb returns false.
// Returns true if all elements were visited without stopping early.
func (m Map) Range(cb func(uint64, any) bool) bool {
if m.n != nil {
return m.n.visit(cb)
}
return true
}
// DeepEqual returns true if m and other contain the same (k, v) mappings
// [regardless of Scope].
//
// Equivalently m.DeepEqual(other) <=> reflect.DeepEqual(Elems(m), Elems(other))
func (m Map) DeepEqual(other Map) bool {
if m.Scope() == other.Scope() {
return m.n == other.n
}
if (m.n == nil) || (other.n == nil) {
return m.Size() == 0 && other.Size() == 0
}
return m.n.deepEqual(other.n)
}
// Elems are the (k,v) elements in the Map as a map[uint64]interface{}
func Elems(m Map) map[uint64]any {
dest := make(map[uint64]any, m.Size())
m.Range(func(k uint64, v any) bool {
dest[k] = v
return true
})
return dest
}
// node is an internal node within a trie map.
// A node is either empty, a leaf or a branch.
type node interface {
size() int
// visit the leaves (key, value) pairs in the map in order and
// applies cb(key, value) to each. Stops early if cb returns false.
// Returns true if all elements were visited without stopping early.
visit(cb func(uint64, any) bool) bool
// Two nodes contain the same elements regardless of scope.
deepEqual(node) bool
// find the leaf for the given key value or nil if it is not present.
find(k key) *leaf
// implementations must implement this.
nodeImpl()
}
// empty represents the empty map within a scope.
//
// The current builder ensure
type empty struct {
s Scope
}
// leaf represents a single <key, value> pair.
type leaf struct {
k key
v any
}
// branch represents a tree node within the Patricia trie.
//
// All keys within the branch match a `prefix` of the key
// up to a `branching` bit, and the left and right nodes
// contain keys that disagree on the bit at the `branching` bit.
type branch struct {
sz int // size. cached for O(1) lookup
prefix prefix // == mask(p0, branching) for some p0
branching bitpos
// Invariants:
// - neither is nil.
// - neither is *empty.
// - all keys in left are <= p.
// - all keys in right are > p.
left, right node
}
// all of these types are Maps.
var _ node = &empty{}
var _ node = &leaf{}
var _ node = &branch{}
func (*empty) nodeImpl() {}
func (*leaf) nodeImpl() {}
func (*branch) nodeImpl() {}
func (*empty) find(k key) *leaf { return nil }
func (l *leaf) find(k key) *leaf {
if k == l.k {
return l
}
return nil
}
func (br *branch) find(k key) *leaf {
kp := prefix(k)
if !matchPrefix(kp, br.prefix, br.branching) {
return nil
}
if zeroBit(kp, br.branching) {
return br.left.find(k)
}
return br.right.find(k)
}
func (*empty) size() int { return 0 }
func (*leaf) size() int { return 1 }
func (br *branch) size() int { return br.sz }
func (*empty) deepEqual(m node) bool {
_, ok := m.(*empty)
return ok
}
func (l *leaf) deepEqual(m node) bool {
if m, ok := m.(*leaf); ok {
return m == l || (l.k == m.k && l.v == m.v)
}
return false
}
func (br *branch) deepEqual(m node) bool {
if m, ok := m.(*branch); ok {
if br == m {
return true
}
return br.sz == m.sz && br.branching == m.branching && br.prefix == m.prefix &&
br.left.deepEqual(m.left) && br.right.deepEqual(m.right)
}
// if m is not a branch, m contains 0 or 1 elem.
// br contains at least 2 keys that disagree on a prefix.
return false
}
func (*empty) visit(cb func(uint64, any) bool) bool {
return true
}
func (l *leaf) visit(cb func(uint64, any) bool) bool {
return cb(uint64(l.k), l.v)
}
func (br *branch) visit(cb func(uint64, any) bool) bool {
if !br.left.visit(cb) {
return false
}
return br.right.visit(cb)
}

View File

@@ -0,0 +1,201 @@
// Copyright 2021 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 vta
import (
"go/types"
"iter"
"slices"
"golang.org/x/tools/go/callgraph/vta/internal/trie"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
)
// scc computes strongly connected components (SCCs) of `g` using the
// classical Tarjan's algorithm for SCCs. The result is two slices:
// - sccs: the SCCs, each represented as a slice of node indices
// - idxToSccID: the inverse map, from node index to SCC number.
//
// The SCCs are sorted in reverse topological order: for SCCs
// with ids X and Y s.t. X < Y, Y comes before X in the topological order.
func scc(g *vtaGraph) (sccs [][]idx, idxToSccID []int) {
// standard data structures used by Tarjan's algorithm.
type state struct {
pre int // preorder of the node (0 if unvisited)
lowLink int
onStack bool
}
states := make([]state, g.numNodes())
var stack []idx
idxToSccID = make([]int, g.numNodes())
nextPre := 0
var doSCC func(idx)
doSCC = func(n idx) {
nextPre++
ns := &states[n]
*ns = state{pre: nextPre, lowLink: nextPre, onStack: true}
stack = append(stack, n)
for s := range g.successors(n) {
if ss := &states[s]; ss.pre == 0 {
// Analyze successor s that has not been visited yet.
doSCC(s)
ns.lowLink = min(ns.lowLink, ss.lowLink)
} else if ss.onStack {
// The successor is on the stack, meaning it has to be
// in the current SCC.
ns.lowLink = min(ns.lowLink, ss.pre)
}
}
// if n is a root node, pop the stack and generate a new SCC.
if ns.lowLink == ns.pre {
sccStart := slicesLastIndex(stack, n)
scc := slices.Clone(stack[sccStart:])
stack = stack[:sccStart]
sccID := len(sccs)
sccs = append(sccs, scc)
for _, w := range scc {
states[w].onStack = false
idxToSccID[w] = sccID
}
}
}
for n, nn := 0, g.numNodes(); n < nn; n++ {
if states[n].pre == 0 {
doSCC(idx(n))
}
}
return sccs, idxToSccID
}
// slicesLastIndex returns the index of the last occurrence of v in s, or -1 if v is
// not present in s.
//
// slicesLastIndex iterates backwards through the elements of s, stopping when the ==
// operator determines an element is equal to v.
func slicesLastIndex[S ~[]E, E comparable](s S, v E) int {
// TODO: move to / dedup with slices.LastIndex
for i := len(s) - 1; i >= 0; i-- {
if s[i] == v {
return i
}
}
return -1
}
// propType represents type information being propagated
// over the vta graph. f != nil only for function nodes
// and nodes reachable from function nodes. There, we also
// remember the actual *ssa.Function in order to more
// precisely model higher-order flow.
type propType struct {
typ types.Type
f *ssa.Function
}
// propTypeMap is an auxiliary structure that serves
// the role of a map from nodes to a set of propTypes.
type propTypeMap map[node]*trie.MutMap
// propTypes returns an iterator for the propTypes associated with
// node `n` in map `ptm`.
func (ptm propTypeMap) propTypes(n node) iter.Seq[propType] {
return func(yield func(propType) bool) {
if types := ptm[n]; types != nil {
types.M.Range(func(_ uint64, elem any) bool {
return yield(elem.(propType))
})
}
}
}
// propagate reduces the `graph` based on its SCCs and
// then propagates type information through the reduced
// graph. The result is a map from nodes to a set of types
// and functions, stemming from higher-order data flow,
// reaching the node. `canon` is used for type uniqueness.
func propagate(graph *vtaGraph, canon *typeutil.Map) propTypeMap {
sccs, idxToSccID := scc(graph)
// propTypeIds are used to create unique ids for
// propType, to be used for trie-based type sets.
propTypeIds := make(map[propType]uint64)
// Id creation is based on == equality, which works
// as types are canonicalized (see getPropType).
propTypeId := func(p propType) uint64 {
if id, ok := propTypeIds[p]; ok {
return id
}
id := uint64(len(propTypeIds))
propTypeIds[p] = id
return id
}
builder := trie.NewBuilder()
// Initialize sccToTypes to avoid repeated check
// for initialization later.
sccToTypes := make([]*trie.MutMap, len(sccs))
for sccID, scc := range sccs {
typeSet := builder.MutEmpty()
for _, idx := range scc {
if n := graph.node[idx]; hasInitialTypes(n) {
// add the propType for idx to typeSet.
pt := getPropType(n, canon)
typeSet.Update(propTypeId(pt), pt)
}
}
sccToTypes[sccID] = &typeSet
}
for i := len(sccs) - 1; i >= 0; i-- {
nextSccs := make(map[int]empty)
for _, n := range sccs[i] {
for succ := range graph.successors(n) {
nextSccs[idxToSccID[succ]] = empty{}
}
}
// Propagate types to all successor SCCs.
for nextScc := range nextSccs {
sccToTypes[nextScc].Merge(sccToTypes[i].M)
}
}
nodeToTypes := make(propTypeMap, graph.numNodes())
for sccID, scc := range sccs {
types := sccToTypes[sccID]
for _, idx := range scc {
nodeToTypes[graph.node[idx]] = types
}
}
return nodeToTypes
}
// hasInitialTypes check if a node can have initial types.
// Returns true iff `n` is not a panic, recover, nestedPtr*
// node, nor a node whose type is an interface.
func hasInitialTypes(n node) bool {
switch n.(type) {
case panicArg, recoverReturn, nestedPtrFunction, nestedPtrInterface:
return false
default:
return !types.IsInterface(n.Type())
}
}
// getPropType creates a propType for `node` based on its type.
// propType.typ is always node.Type(). If node is function, then
// propType.val is the underlying function; nil otherwise.
func getPropType(node node, canon *typeutil.Map) propType {
t := canonicalize(node.Type(), canon)
if fn, ok := node.(function); ok {
return propType{f: fn.f, typ: t}
}
return propType{f: nil, typ: t}
}

188
vendor/golang.org/x/tools/go/callgraph/vta/utils.go generated vendored Normal file
View File

@@ -0,0 +1,188 @@
// Copyright 2021 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 vta
import (
"go/types"
"iter"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/internal/typeparams"
)
func canAlias(n1, n2 node) bool {
return isReferenceNode(n1) && isReferenceNode(n2)
}
func isReferenceNode(n node) bool {
if _, ok := n.(nestedPtrInterface); ok {
return true
}
if _, ok := n.(nestedPtrFunction); ok {
return true
}
if _, ok := types.Unalias(n.Type()).(*types.Pointer); ok {
return true
}
return false
}
// hasInFlow checks if a concrete type can flow to node `n`.
// Returns yes iff the type of `n` satisfies one the following:
// 1. is an interface
// 2. is a (nested) pointer to interface (needed for, say,
// slice elements of nested pointers to interface type)
// 3. is a function type (needed for higher-order type flow)
// 4. is a (nested) pointer to function (needed for, say,
// slice elements of nested pointers to function type)
// 5. is a global Recover or Panic node
func hasInFlow(n node) bool {
if _, ok := n.(panicArg); ok {
return true
}
if _, ok := n.(recoverReturn); ok {
return true
}
t := n.Type()
if i := interfaceUnderPtr(t); i != nil {
return true
}
if f := functionUnderPtr(t); f != nil {
return true
}
return types.IsInterface(t) || isFunction(t)
}
func isFunction(t types.Type) bool {
_, ok := t.Underlying().(*types.Signature)
return ok
}
// interfaceUnderPtr checks if type `t` is a potentially nested
// pointer to interface and if yes, returns the interface type.
// Otherwise, returns nil.
func interfaceUnderPtr(t types.Type) types.Type {
seen := make(map[types.Type]bool)
var visit func(types.Type) types.Type
visit = func(t types.Type) types.Type {
if seen[t] {
return nil
}
seen[t] = true
p, ok := t.Underlying().(*types.Pointer)
if !ok {
return nil
}
if types.IsInterface(p.Elem()) {
return p.Elem()
}
return visit(p.Elem())
}
return visit(t)
}
// functionUnderPtr checks if type `t` is a potentially nested
// pointer to function type and if yes, returns the function type.
// Otherwise, returns nil.
func functionUnderPtr(t types.Type) types.Type {
seen := make(map[types.Type]bool)
var visit func(types.Type) types.Type
visit = func(t types.Type) types.Type {
if seen[t] {
return nil
}
seen[t] = true
p, ok := t.Underlying().(*types.Pointer)
if !ok {
return nil
}
if isFunction(p.Elem()) {
return p.Elem()
}
return visit(p.Elem())
}
return visit(t)
}
// sliceArrayElem returns the element type of type `t` that is
// expected to be a (pointer to) array, slice or string, consistent with
// the ssa.Index and ssa.IndexAddr instructions. Panics otherwise.
func sliceArrayElem(t types.Type) types.Type {
switch u := t.Underlying().(type) {
case *types.Pointer:
switch e := u.Elem().Underlying().(type) {
case *types.Array:
return e.Elem()
case *types.Interface:
return sliceArrayElem(e) // e is a type param with matching element types.
default:
panic(t)
}
case *types.Array:
return u.Elem()
case *types.Slice:
return u.Elem()
case *types.Basic:
return types.Typ[types.Byte]
case *types.Interface: // type param.
terms, err := typeparams.InterfaceTermSet(u)
if err != nil || len(terms) == 0 {
panic(t)
}
return sliceArrayElem(terms[0].Type()) // Element types must match.
default:
panic(t)
}
}
// siteCallees returns an iterator for the callees for call site `c`.
func siteCallees(c ssa.CallInstruction, callees calleesFunc) iter.Seq[*ssa.Function] {
return func(yield func(*ssa.Function) bool) {
for _, callee := range callees(c) {
if !yield(callee) {
return
}
}
}
}
func canHaveMethods(t types.Type) bool {
t = types.Unalias(t)
if _, ok := t.(*types.Named); ok {
return true
}
u := t.Underlying()
switch u.(type) {
case *types.Interface, *types.Signature, *types.Struct:
return true
default:
return false
}
}
// calls returns the set of call instructions in `f`.
func calls(f *ssa.Function) []ssa.CallInstruction {
var calls []ssa.CallInstruction
for _, bl := range f.Blocks {
for _, instr := range bl.Instrs {
if c, ok := instr.(ssa.CallInstruction); ok {
calls = append(calls, c)
}
}
}
return calls
}

190
vendor/golang.org/x/tools/go/callgraph/vta/vta.go generated vendored Normal file
View File

@@ -0,0 +1,190 @@
// Copyright 2021 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 vta computes the call graph of a Go program using the Variable
// Type Analysis (VTA) algorithm originally described in "Practical Virtual
// Method Call Resolution for Java," Vijay Sundaresan, Laurie Hendren,
// Chrislain Razafimahefa, Raja Vallée-Rai, Patrick Lam, Etienne Gagnon, and
// Charles Godin.
//
// Note: this package is in experimental phase and its interface is
// subject to change.
// TODO(zpavlinovic): reiterate on documentation.
//
// The VTA algorithm overapproximates the set of types (and function literals)
// a variable can take during runtime by building a global type propagation
// graph and propagating types (and function literals) through the graph.
//
// A type propagation is a directed, labeled graph. A node can represent
// one of the following:
// - A field of a struct type.
// - A local (SSA) variable of a method/function.
// - All pointers to a non-interface type.
// - The return value of a method.
// - All elements in an array.
// - All elements in a slice.
// - All elements in a map.
// - All elements in a channel.
// - A global variable.
//
// In addition, the implementation used in this package introduces
// a few Go specific kinds of nodes:
// - (De)references of nested pointers to interfaces are modeled
// as a unique nestedPtrInterface node in the type propagation graph.
// - Each function literal is represented as a function node whose
// internal value is the (SSA) representation of the function. This
// is done to precisely infer flow of higher-order functions.
//
// Edges in the graph represent flow of types (and function literals) through
// the program. That is, the model 1) typing constraints that are induced by
// assignment statements or function and method calls and 2) higher-order flow
// of functions in the program.
//
// The labeling function maps each node to a set of types and functions that
// can intuitively reach the program construct the node represents. Initially,
// every node is assigned a type corresponding to the program construct it
// represents. Function nodes are also assigned the function they represent.
// The labeling function then propagates types and function through the graph.
//
// The result of VTA is a type propagation graph in which each node is labeled
// with a conservative overapproximation of the set of types (and functions)
// it may have. This information is then used to construct the call graph.
// For each unresolved call site, vta uses the set of types and functions
// reaching the node representing the call site to create a set of callees.
package vta
// TODO(zpavlinovic): update VTA for how it handles generic function bodies and instantiation wrappers.
import (
"go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
)
// CallGraph uses the VTA algorithm to compute call graph for all functions
// f:true in funcs. VTA refines the results of initial call graph and uses it
// to establish interprocedural type flow. If initial is nil, VTA uses a more
// efficient approach to construct a CHA call graph.
//
// The resulting graph does not have a root node.
//
// CallGraph does not make any assumptions on initial types global variables
// and function/method inputs can have. CallGraph is then sound, modulo use of
// reflection and unsafe, if the initial call graph is sound.
func CallGraph(funcs map[*ssa.Function]bool, initial *callgraph.Graph) *callgraph.Graph {
callees := makeCalleesFunc(funcs, initial)
vtaG, canon := typePropGraph(funcs, callees)
types := propagate(vtaG, canon)
c := &constructor{types: types, callees: callees, cache: make(methodCache)}
return c.construct(funcs)
}
// constructor type linearly traverses the input program
// and constructs a callgraph based on the results of the
// VTA type propagation phase.
type constructor struct {
types propTypeMap
cache methodCache
callees calleesFunc
}
func (c *constructor) construct(funcs map[*ssa.Function]bool) *callgraph.Graph {
cg := &callgraph.Graph{Nodes: make(map[*ssa.Function]*callgraph.Node)}
for f, in := range funcs {
if in {
c.constrct(cg, f)
}
}
return cg
}
func (c *constructor) constrct(g *callgraph.Graph, f *ssa.Function) {
caller := g.CreateNode(f)
for _, call := range calls(f) {
for _, c := range c.resolves(call) {
callgraph.AddEdge(caller, call, g.CreateNode(c))
}
}
}
// resolves computes the set of functions to which VTA resolves `c`. The resolved
// functions are intersected with functions to which `c.initial` resolves `c`.
func (c *constructor) resolves(call ssa.CallInstruction) []*ssa.Function {
cc := call.Common()
if cc.StaticCallee() != nil {
return []*ssa.Function{cc.StaticCallee()}
}
// Skip builtins as they are not *ssa.Function.
if _, ok := cc.Value.(*ssa.Builtin); ok {
return nil
}
// Cover the case of dynamic higher-order and interface calls.
var res []*ssa.Function
resolved := resolve(call, c.types, c.cache)
for f := range siteCallees(call, c.callees) {
if _, ok := resolved[f]; ok {
res = append(res, f)
}
}
return res
}
// resolve returns a set of functions `c` resolves to based on the
// type propagation results in `types`.
func resolve(c ssa.CallInstruction, types propTypeMap, cache methodCache) map[*ssa.Function]empty {
fns := make(map[*ssa.Function]empty)
n := local{val: c.Common().Value}
for p := range types.propTypes(n) {
for _, f := range propFunc(p, c, cache) {
fns[f] = empty{}
}
}
return fns
}
// propFunc returns the functions modeled with the propagation type `p`
// assigned to call site `c`. If no such function exists, nil is returned.
func propFunc(p propType, c ssa.CallInstruction, cache methodCache) []*ssa.Function {
if p.f != nil {
return []*ssa.Function{p.f}
}
if c.Common().Method == nil {
return nil
}
return cache.methods(p.typ, c.Common().Method.Name(), c.Parent().Prog)
}
// methodCache serves as a type -> method name -> methods
// cache when computing methods of a type using the
// ssa.Program.MethodSets and ssa.Program.MethodValue
// APIs. The cache is used to speed up querying of
// methods of a type as the mentioned APIs are expensive.
type methodCache map[types.Type]map[string][]*ssa.Function
// methods returns methods of a type `t` named `name`. First consults
// `mc` and otherwise queries `prog` for the method. If no such method
// exists, nil is returned.
func (mc methodCache) methods(t types.Type, name string, prog *ssa.Program) []*ssa.Function {
if ms, ok := mc[t]; ok {
return ms[name]
}
ms := make(map[string][]*ssa.Function)
mset := prog.MethodSets.MethodSet(t)
for i, n := 0, mset.Len(); i < n; i++ {
// f can be nil when t is an interface or some
// other type without any runtime methods.
if f := prog.MethodValue(mset.At(i)); f != nil {
ms[f.Name()] = append(ms[f.Name()], f)
}
}
mc[t] = ms
return ms[name]
}