src

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

utils.go (4360B)


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