github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/callgraph/vta/utils.go (about)

     1  // Copyright 2021 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 vta
     6  
     7  import (
     8  	"go/types"
     9  
    10  	"github.com/powerman/golang-tools/go/callgraph"
    11  	"github.com/powerman/golang-tools/go/ssa"
    12  )
    13  
    14  func canAlias(n1, n2 node) bool {
    15  	return isReferenceNode(n1) && isReferenceNode(n2)
    16  }
    17  
    18  func isReferenceNode(n node) bool {
    19  	if _, ok := n.(nestedPtrInterface); ok {
    20  		return true
    21  	}
    22  	if _, ok := n.(nestedPtrFunction); ok {
    23  		return true
    24  	}
    25  
    26  	if _, ok := n.Type().(*types.Pointer); ok {
    27  		return true
    28  	}
    29  
    30  	return false
    31  }
    32  
    33  // hasInFlow checks if a concrete type can flow to node `n`.
    34  // Returns yes iff the type of `n` satisfies one the following:
    35  //  1) is an interface
    36  //  2) is a (nested) pointer to interface (needed for, say,
    37  //     slice elements of nested pointers to interface type)
    38  //  3) is a function type (needed for higher-order type flow)
    39  //  4) is a (nested) pointer to function (needed for, say,
    40  //     slice elements of nested pointers to function type)
    41  //  5) is a global Recover or Panic node
    42  func hasInFlow(n node) bool {
    43  	if _, ok := n.(panicArg); ok {
    44  		return true
    45  	}
    46  	if _, ok := n.(recoverReturn); ok {
    47  		return true
    48  	}
    49  
    50  	t := n.Type()
    51  
    52  	if i := interfaceUnderPtr(t); i != nil {
    53  		return true
    54  	}
    55  	if f := functionUnderPtr(t); f != nil {
    56  		return true
    57  	}
    58  
    59  	return isInterface(t) || isFunction(t)
    60  }
    61  
    62  // hasInitialTypes check if a node can have initial types.
    63  // Returns true iff `n` is not a panic or recover node as
    64  // those are artificial.
    65  func hasInitialTypes(n node) bool {
    66  	switch n.(type) {
    67  	case panicArg, recoverReturn:
    68  		return false
    69  	default:
    70  		return true
    71  	}
    72  }
    73  
    74  func isInterface(t types.Type) bool {
    75  	_, ok := t.Underlying().(*types.Interface)
    76  	return ok
    77  }
    78  
    79  func isFunction(t types.Type) bool {
    80  	_, ok := t.Underlying().(*types.Signature)
    81  	return ok
    82  }
    83  
    84  // interfaceUnderPtr checks if type `t` is a potentially nested
    85  // pointer to interface and if yes, returns the interface type.
    86  // Otherwise, returns nil.
    87  func interfaceUnderPtr(t types.Type) types.Type {
    88  	p, ok := t.Underlying().(*types.Pointer)
    89  	if !ok {
    90  		return nil
    91  	}
    92  
    93  	if isInterface(p.Elem()) {
    94  		return p.Elem()
    95  	}
    96  
    97  	return interfaceUnderPtr(p.Elem())
    98  }
    99  
   100  // functionUnderPtr checks if type `t` is a potentially nested
   101  // pointer to function type and if yes, returns the function type.
   102  // Otherwise, returns nil.
   103  func functionUnderPtr(t types.Type) types.Type {
   104  	p, ok := t.Underlying().(*types.Pointer)
   105  	if !ok {
   106  		return nil
   107  	}
   108  
   109  	if isFunction(p.Elem()) {
   110  		return p.Elem()
   111  	}
   112  
   113  	return functionUnderPtr(p.Elem())
   114  }
   115  
   116  // sliceArrayElem returns the element type of type `t` that is
   117  // expected to be a (pointer to) array or slice, consistent with
   118  // the ssa.Index and ssa.IndexAddr instructions. Panics otherwise.
   119  func sliceArrayElem(t types.Type) types.Type {
   120  	u := t.Underlying()
   121  
   122  	if p, ok := u.(*types.Pointer); ok {
   123  		u = p.Elem().Underlying()
   124  	}
   125  
   126  	if a, ok := u.(*types.Array); ok {
   127  		return a.Elem()
   128  	}
   129  	return u.(*types.Slice).Elem()
   130  }
   131  
   132  // siteCallees computes a set of callees for call site `c` given program `callgraph`.
   133  func siteCallees(c ssa.CallInstruction, callgraph *callgraph.Graph) []*ssa.Function {
   134  	var matches []*ssa.Function
   135  
   136  	node := callgraph.Nodes[c.Parent()]
   137  	if node == nil {
   138  		return nil
   139  	}
   140  
   141  	for _, edge := range node.Out {
   142  		if edge.Site == c {
   143  			matches = append(matches, edge.Callee.Func)
   144  		}
   145  	}
   146  	return matches
   147  }
   148  
   149  func canHaveMethods(t types.Type) bool {
   150  	if _, ok := t.(*types.Named); ok {
   151  		return true
   152  	}
   153  
   154  	u := t.Underlying()
   155  	switch u.(type) {
   156  	case *types.Interface, *types.Signature, *types.Struct:
   157  		return true
   158  	default:
   159  		return false
   160  	}
   161  }
   162  
   163  // calls returns the set of call instructions in `f`.
   164  func calls(f *ssa.Function) []ssa.CallInstruction {
   165  	var calls []ssa.CallInstruction
   166  	for _, bl := range f.Blocks {
   167  		for _, instr := range bl.Instrs {
   168  			if c, ok := instr.(ssa.CallInstruction); ok {
   169  				calls = append(calls, c)
   170  			}
   171  		}
   172  	}
   173  	return calls
   174  }
   175  
   176  // intersect produces an intersection of functions in `fs1` and `fs2`.
   177  func intersect(fs1, fs2 []*ssa.Function) []*ssa.Function {
   178  	m := make(map[*ssa.Function]bool)
   179  	for _, f := range fs1 {
   180  		m[f] = true
   181  	}
   182  
   183  	var res []*ssa.Function
   184  	for _, f := range fs2 {
   185  		if m[f] {
   186  			res = append(res, f)
   187  		}
   188  	}
   189  	return res
   190  }