blob: 6ca765b0624f89915f0810140bbabf68d71b4561 [file] [log] [blame]
// 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/types"
"golang.org/x/tools/go/ssa"
)
// 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 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 := f.StructType.Underlying().(*types.Struct)
return s.Field(f.index).Type()
}
func (f field) String() string {
s := f.StructType.Underlying().(*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())
}
// 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)
}
// 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"
}
// vtaGraph remembers for each VTA node the set of its successors.
// Tailored for VTA, hence does not support singleton (sub)graphs.
type vtaGraph map[node]map[node]bool
// addEdge adds an edge x->y to the graph.
func (g vtaGraph) addEdge(x, y node) {
succs, ok := g[x]
if !ok {
succs = make(map[node]bool)
g[x] = succs
}
succs[y] = true
}
// successors returns all of n's immediate successors in the graph.
// The order of successor nodes is arbitrary.
func (g vtaGraph) successors(n node) []node {
var succs []node
for succ := range g[n] {
succs = append(succs, succ)
}
return succs
}