-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
vta: adds definition for VTA nodes and graph.
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
1 parent
c3e30ff
commit acaf218
Showing
4 changed files
with
492 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.