2013-08-27 16:49:13 -06:00
|
|
|
// Copyright 2013 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.
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
package pointer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// CanPoint reports whether the type T is pointerlike,
|
|
|
|
// for the purposes of this analysis.
|
|
|
|
func CanPoint(T types.Type) bool {
|
|
|
|
switch T := T.(type) {
|
|
|
|
case *types.Named:
|
|
|
|
return CanPoint(T.Underlying())
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice:
|
2013-08-22 10:27:55 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false // array struct tuple builtin basic
|
|
|
|
}
|
|
|
|
|
|
|
|
// mustDeref returns the element type of its argument, which must be a
|
|
|
|
// pointer; panic ensues otherwise.
|
|
|
|
func mustDeref(typ types.Type) types.Type {
|
|
|
|
return typ.Underlying().(*types.Pointer).Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
// A fieldInfo describes one subelement (node) of the flattening-out
|
|
|
|
// of a type T: the subelement's type and its path from the root of T.
|
|
|
|
//
|
|
|
|
// For example, for this type:
|
|
|
|
// type line struct{ points []struct{x, y int} }
|
|
|
|
// flatten() of the inner struct yields the following []fieldInfo:
|
|
|
|
// struct{ x, y int } ""
|
|
|
|
// int ".x"
|
|
|
|
// int ".y"
|
|
|
|
// and flatten(line) yields:
|
|
|
|
// struct{ points []struct{x, y int} } ""
|
|
|
|
// struct{ x, y int } ".points[*]"
|
|
|
|
// int ".points[*].x
|
|
|
|
// int ".points[*].y"
|
|
|
|
//
|
|
|
|
type fieldInfo struct {
|
|
|
|
typ types.Type
|
|
|
|
|
|
|
|
// op and tail describe the path to the element (e.g. ".a#2.b[*].c").
|
|
|
|
op interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil
|
|
|
|
tail *fieldInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// path returns a user-friendly string describing the subelement path.
|
|
|
|
//
|
|
|
|
func (fi *fieldInfo) path() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
for p := fi; p != nil; p = p.tail {
|
|
|
|
switch op := p.op.(type) {
|
|
|
|
case bool:
|
|
|
|
fmt.Fprintf(&buf, "[*]")
|
|
|
|
case int:
|
|
|
|
fmt.Fprintf(&buf, "#%d", op)
|
|
|
|
case *types.Var:
|
|
|
|
fmt.Fprintf(&buf, ".%s", op.Name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// flatten returns a list of directly contained fields in the preorder
|
|
|
|
// traversal of the type tree of t. The resulting elements are all
|
|
|
|
// scalars (basic types or pointerlike types), except for struct/array
|
|
|
|
// "identity" nodes, whose type is that of the aggregate.
|
|
|
|
//
|
|
|
|
// Callers must not mutate the result.
|
|
|
|
//
|
|
|
|
func (a *analysis) flatten(t types.Type) []*fieldInfo {
|
|
|
|
fl, ok := a.flattenMemo[t]
|
|
|
|
if !ok {
|
|
|
|
switch t := t.(type) {
|
|
|
|
case *types.Named:
|
|
|
|
u := t.Underlying()
|
|
|
|
if _, ok := u.(*types.Interface); ok {
|
|
|
|
// Debuggability hack: don't remove
|
|
|
|
// the named type from interfaces as
|
|
|
|
// they're very verbose.
|
|
|
|
fl = append(fl, &fieldInfo{typ: t})
|
|
|
|
} else {
|
|
|
|
fl = a.flatten(u)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *types.Basic,
|
|
|
|
*types.Signature,
|
|
|
|
*types.Chan,
|
|
|
|
*types.Map,
|
|
|
|
*types.Interface,
|
|
|
|
*types.Slice,
|
|
|
|
*types.Pointer:
|
|
|
|
fl = append(fl, &fieldInfo{typ: t})
|
|
|
|
|
|
|
|
case *types.Array:
|
|
|
|
fl = append(fl, &fieldInfo{typ: t}) // identity node
|
|
|
|
for _, fi := range a.flatten(t.Elem()) {
|
|
|
|
fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi})
|
|
|
|
}
|
|
|
|
|
|
|
|
case *types.Struct:
|
|
|
|
fl = append(fl, &fieldInfo{typ: t}) // identity node
|
|
|
|
for i, n := 0, t.NumFields(); i < n; i++ {
|
|
|
|
f := t.Field(i)
|
|
|
|
for _, fi := range a.flatten(f.Type()) {
|
|
|
|
fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case *types.Tuple:
|
|
|
|
// No identity node: tuples are never address-taken.
|
|
|
|
for i, n := 0, t.Len(); i < n; i++ {
|
|
|
|
f := t.At(i)
|
|
|
|
for _, fi := range a.flatten(f.Type()) {
|
|
|
|
fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case *types.Builtin:
|
|
|
|
panic("flatten(*types.Builtin)") // not the type of any value
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.flattenMemo[t] = fl
|
|
|
|
}
|
|
|
|
|
|
|
|
return fl
|
|
|
|
}
|
|
|
|
|
|
|
|
// sizeof returns the number of pointerlike abstractions (nodes) in the type t.
|
|
|
|
func (a *analysis) sizeof(t types.Type) uint32 {
|
|
|
|
return uint32(len(a.flatten(t)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// offsetOf returns the (abstract) offset of field index within struct
|
|
|
|
// or tuple typ.
|
|
|
|
func (a *analysis) offsetOf(typ types.Type, index int) uint32 {
|
|
|
|
var offset uint32
|
|
|
|
switch t := typ.Underlying().(type) {
|
|
|
|
case *types.Tuple:
|
|
|
|
for i := 0; i < index; i++ {
|
|
|
|
offset += a.sizeof(t.At(i).Type())
|
|
|
|
}
|
|
|
|
case *types.Struct:
|
|
|
|
offset++ // the node for the struct itself
|
|
|
|
for i := 0; i < index; i++ {
|
|
|
|
offset += a.sizeof(t.Field(i).Type())
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ))
|
|
|
|
}
|
|
|
|
return offset
|
|
|
|
}
|
|
|
|
|
|
|
|
// sliceToArray returns the type representing the arrays to which
|
|
|
|
// slice type slice points.
|
|
|
|
func sliceToArray(slice types.Type) *types.Array {
|
|
|
|
return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Node set -------------------------------------------------------------------
|
|
|
|
|
|
|
|
type nodeset map[nodeid]struct{}
|
|
|
|
|
|
|
|
// ---- Accessors ----
|
|
|
|
|
|
|
|
func (ns nodeset) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
buf.WriteRune('{')
|
|
|
|
var sep string
|
|
|
|
for n := range ns {
|
|
|
|
fmt.Fprintf(&buf, "%sn%d", sep, n)
|
|
|
|
sep = ", "
|
|
|
|
}
|
|
|
|
buf.WriteRune('}')
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// diff returns the set-difference x - y. nil => empty.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): opt: extremely inefficient. BDDs do this in
|
|
|
|
// constant time. Sparse bitvectors are linear but very fast.
|
|
|
|
func (x nodeset) diff(y nodeset) nodeset {
|
|
|
|
var z nodeset
|
|
|
|
for k := range x {
|
|
|
|
if _, ok := y[k]; !ok {
|
|
|
|
z.add(k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return z
|
|
|
|
}
|
|
|
|
|
|
|
|
// clone() returns an unaliased copy of x.
|
|
|
|
func (x nodeset) clone() nodeset {
|
|
|
|
return x.diff(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---- Mutators ----
|
|
|
|
|
|
|
|
func (ns *nodeset) add(n nodeid) bool {
|
|
|
|
sz := len(*ns)
|
|
|
|
if *ns == nil {
|
|
|
|
*ns = make(nodeset)
|
|
|
|
}
|
|
|
|
(*ns)[n] = struct{}{}
|
|
|
|
return len(*ns) > sz
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *nodeset) addAll(y nodeset) bool {
|
|
|
|
if y == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
sz := len(*x)
|
|
|
|
if *x == nil {
|
|
|
|
*x = make(nodeset)
|
|
|
|
}
|
|
|
|
for n := range y {
|
|
|
|
(*x)[n] = struct{}{}
|
|
|
|
}
|
|
|
|
return len(*x) > sz
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constraint set -------------------------------------------------------------
|
|
|
|
|
|
|
|
type constraintset map[constraint]struct{}
|
|
|
|
|
|
|
|
func (cs *constraintset) add(c constraint) bool {
|
|
|
|
sz := len(*cs)
|
|
|
|
if *cs == nil {
|
|
|
|
*cs = make(constraintset)
|
|
|
|
}
|
|
|
|
(*cs)[c] = struct{}{}
|
|
|
|
return len(*cs) > sz
|
|
|
|
}
|
|
|
|
|
|
|
|
// Worklist -------------------------------------------------------------------
|
|
|
|
|
|
|
|
// TODO(adonovan): interface may not be general enough for certain
|
|
|
|
// implementations, e.g. priority queue
|
|
|
|
//
|
|
|
|
// Uses double-buffering so nodes can be added during iteration.
|
|
|
|
type worklist interface {
|
|
|
|
empty() bool // Reports whether active buffer is empty.
|
|
|
|
swap() bool // Switches to the shadow buffer if empty().
|
|
|
|
add(nodeid) // Adds a node to the shadow buffer.
|
|
|
|
take() nodeid // Takes a node from the active buffer. Precondition: !empty().
|
|
|
|
}
|
|
|
|
|
|
|
|
// Horribly naive (and nondeterministic) worklist
|
|
|
|
// based on two hash-sets.
|
|
|
|
type mapWorklist struct {
|
|
|
|
active, shadow nodeset
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *mapWorklist) empty() bool {
|
|
|
|
return len(w.active) == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *mapWorklist) swap() bool {
|
|
|
|
if w.empty() {
|
|
|
|
w.shadow, w.active = w.active, w.shadow
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *mapWorklist) add(n nodeid) {
|
|
|
|
w.shadow[n] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *mapWorklist) take() nodeid {
|
|
|
|
for k := range w.active {
|
|
|
|
delete(w.active, k)
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
panic("worklist.take(): empty active buffer")
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeMapWorklist() worklist {
|
|
|
|
return &mapWorklist{make(nodeset), make(nodeset)}
|
|
|
|
}
|