Skip to content

Commit

Permalink
vta: adds definition for VTA nodes and graph.
Browse files Browse the repository at this point in the history
This is the first CL for the Variable Type Analysis (VTA) callgraph. It
provides the definition for VTA nodes and graph used to infer how types
flow through the Go program. This CL is first in the series of CLs that
build VTA type propagation graph.

The VTA algorithm is described in the paper called ``Practical Virtual Method Call Resolution for Java."

Change-Id: I2e4eafa139c6fdfba4bd709f041762ddc6174188
Reviewed-on: https://go-review.googlesource.com/c/tools/+/313749
Reviewed-by: Zvonimir Pavlinovic <[email protected]>
Reviewed-by: Tim King <[email protected]>
Reviewed-by: Roland Shoemaker <[email protected]>
Run-TryBot: Zvonimir Pavlinovic <[email protected]>
Run-TryBot: Tim King <[email protected]>
Trust: Tim King <[email protected]>
gopls-CI: kokoro <[email protected]>
TryBot-Result: Go Bot <[email protected]>
  • Loading branch information
zpavlinovic committed May 25, 2021
1 parent c3e30ff commit acaf218
Show file tree
Hide file tree
Showing 4 changed files with 492 additions and 0 deletions.
234 changes: 234 additions & 0 deletions go/callgraph/vta/graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// 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
}
Loading

0 comments on commit acaf218

Please sign in to comment.