go.tools/pointer: replace Pointer, PointsToSet interfaces with their sole implementations.
(Elminate premature abstraction.) The test probes used Pointer!=nil for the "is pointerlike" predicate. Now that Pointer is a struct, they check the type of the expression, which is more accurate. Two probes on non-pointerlike values have beem removed. R=crawshaw CC=golang-dev https://golang.org/cl/38420043
This commit is contained in:
parent
2d324f247c
commit
6b75c15eec
|
|
@ -88,7 +88,7 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
var sends, receives []token.Pos
|
var sends, receives []token.Pos
|
||||||
for _, op := range ops {
|
for _, op := range ops {
|
||||||
for _, ptr := range ptares.Queries[op.ch] {
|
for _, ptr := range ptares.Queries[op.ch] {
|
||||||
if ptr != nil && ptr.PointsTo().Intersects(queryChanPts) {
|
if ptr.PointsTo().Intersects(queryChanPts) {
|
||||||
if op.dir == ast.SEND {
|
if op.dir == ast.SEND {
|
||||||
sends = append(sends, op.pos)
|
sends = append(sends, op.pos)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
118
pointer/api.go
118
pointer/api.go
|
|
@ -40,7 +40,10 @@ type Config struct {
|
||||||
// Pointer p may be saved until the analysis is complete, at
|
// Pointer p may be saved until the analysis is complete, at
|
||||||
// which point its methods provide access to the analysis
|
// which point its methods provide access to the analysis
|
||||||
// (The result of callings its methods within the Print
|
// (The result of callings its methods within the Print
|
||||||
// callback is undefined.) p is nil if x is non-pointerlike.
|
// callback is undefined.)
|
||||||
|
//
|
||||||
|
// CanPoint(site.Args[0].Type()) reports whether p is
|
||||||
|
// pointerlike.
|
||||||
//
|
//
|
||||||
Print func(site *ssa.CallCommon, p Pointer)
|
Print func(site *ssa.CallCommon, p Pointer)
|
||||||
|
|
||||||
|
|
@ -117,53 +120,26 @@ type Result struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Pointer is an equivalence class of pointerlike values.
|
// A Pointer is an equivalence class of pointerlike values.
|
||||||
type Pointer interface {
|
//
|
||||||
// PointsTo returns the points-to set of this pointer.
|
// A pointer doesn't have a unique type because pointers of distinct
|
||||||
PointsTo() PointsToSet
|
// types may alias the same object.
|
||||||
|
//
|
||||||
// MayAlias reports whether the receiver pointer may alias
|
type Pointer struct {
|
||||||
// the argument pointer.
|
a *analysis
|
||||||
MayAlias(Pointer) bool
|
cgn *cgnode
|
||||||
|
n nodeid // non-zero
|
||||||
// Context returns the context of this pointer,
|
|
||||||
// if it corresponds to a local variable.
|
|
||||||
Context() call.GraphNode
|
|
||||||
|
|
||||||
String() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A PointsToSet is a set of labels (locations or allocations).
|
// A PointsToSet is a set of labels (locations or allocations).
|
||||||
//
|
type PointsToSet struct {
|
||||||
type PointsToSet interface {
|
a *analysis // may be nil if pts is nil
|
||||||
// PointsTo returns the set of labels that this points-to set
|
pts nodeset
|
||||||
// contains.
|
|
||||||
Labels() []*Label
|
|
||||||
|
|
||||||
// Intersects reports whether this points-to set and the
|
|
||||||
// argument points-to set contain common members.
|
|
||||||
Intersects(PointsToSet) bool
|
|
||||||
|
|
||||||
// If this PointsToSet came from a Pointer of interface kind
|
|
||||||
// or a reflect.Value, DynamicTypes returns the set of dynamic
|
|
||||||
// types that it may contain. (For an interface, they will
|
|
||||||
// always be concrete types.)
|
|
||||||
//
|
|
||||||
// The result is a mapping whose keys are the dynamic types to
|
|
||||||
// which it may point. For each pointer-like key type, the
|
|
||||||
// corresponding map value is a set of pointer abstractions of
|
|
||||||
// that dynamic type, represented as a []Pointer slice. Use
|
|
||||||
// PointsToCombined to merge them.
|
|
||||||
//
|
|
||||||
// The result is empty unless CanHaveDynamicTypes(T).
|
|
||||||
//
|
|
||||||
DynamicTypes() *typemap.M
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union returns the set containing all the elements of each set in sets.
|
// Union returns the set containing all the elements of each set in sets.
|
||||||
func Union(sets ...PointsToSet) PointsToSet {
|
func Union(sets ...PointsToSet) PointsToSet {
|
||||||
var union ptset
|
var union PointsToSet
|
||||||
for _, set := range sets {
|
for _, set := range sets {
|
||||||
set := set.(ptset)
|
|
||||||
union.a = set.a
|
union.a = set.a
|
||||||
union.pts.addAll(set.pts)
|
union.pts.addAll(set.pts)
|
||||||
}
|
}
|
||||||
|
|
@ -180,14 +156,7 @@ func PointsToCombined(ptrs []Pointer) PointsToSet {
|
||||||
return Union(ptsets...)
|
return Union(ptsets...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- PointsToSet public interface
|
func (s PointsToSet) String() string {
|
||||||
|
|
||||||
type ptset struct {
|
|
||||||
a *analysis // may be nil if pts is nil
|
|
||||||
pts nodeset
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ptset) String() string {
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
fmt.Fprintf(&buf, "[")
|
fmt.Fprintf(&buf, "[")
|
||||||
sep := ""
|
sep := ""
|
||||||
|
|
@ -199,7 +168,9 @@ func (s ptset) String() string {
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s ptset) Labels() []*Label {
|
// PointsTo returns the set of labels that this points-to set
|
||||||
|
// contains.
|
||||||
|
func (s PointsToSet) Labels() []*Label {
|
||||||
var labels []*Label
|
var labels []*Label
|
||||||
for l := range s.pts {
|
for l := range s.pts {
|
||||||
labels = append(labels, s.a.labelFor(l))
|
labels = append(labels, s.a.labelFor(l))
|
||||||
|
|
@ -207,7 +178,20 @@ func (s ptset) Labels() []*Label {
|
||||||
return labels
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s ptset) DynamicTypes() *typemap.M {
|
// If this PointsToSet came from a Pointer of interface kind
|
||||||
|
// or a reflect.Value, DynamicTypes returns the set of dynamic
|
||||||
|
// types that it may contain. (For an interface, they will
|
||||||
|
// always be concrete types.)
|
||||||
|
//
|
||||||
|
// The result is a mapping whose keys are the dynamic types to
|
||||||
|
// which it may point. For each pointer-like key type, the
|
||||||
|
// corresponding map value is a set of pointer abstractions of
|
||||||
|
// that dynamic type, represented as a []Pointer slice. Use
|
||||||
|
// PointsToCombined to merge them.
|
||||||
|
//
|
||||||
|
// The result is empty unless CanHaveDynamicTypes(T).
|
||||||
|
//
|
||||||
|
func (s PointsToSet) DynamicTypes() *typemap.M {
|
||||||
var tmap typemap.M
|
var tmap typemap.M
|
||||||
tmap.SetHasher(s.a.hasher)
|
tmap.SetHasher(s.a.hasher)
|
||||||
for ifaceObjId := range s.pts {
|
for ifaceObjId := range s.pts {
|
||||||
|
|
@ -219,13 +203,14 @@ func (s ptset) DynamicTypes() *typemap.M {
|
||||||
panic("indirect tagged object") // implement later
|
panic("indirect tagged object") // implement later
|
||||||
}
|
}
|
||||||
prev, _ := tmap.At(tDyn).([]Pointer)
|
prev, _ := tmap.At(tDyn).([]Pointer)
|
||||||
tmap.Set(tDyn, append(prev, ptr{s.a, nil, v}))
|
tmap.Set(tDyn, append(prev, Pointer{s.a, nil, v}))
|
||||||
}
|
}
|
||||||
return &tmap
|
return &tmap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x ptset) Intersects(y_ PointsToSet) bool {
|
// Intersects reports whether this points-to set and the
|
||||||
y := y_.(ptset)
|
// argument points-to set contain common members.
|
||||||
|
func (x PointsToSet) Intersects(y PointsToSet) bool {
|
||||||
for l := range x.pts {
|
for l := range x.pts {
|
||||||
if _, ok := y.pts[l]; ok {
|
if _, ok := y.pts[l]; ok {
|
||||||
return true
|
return true
|
||||||
|
|
@ -234,31 +219,28 @@ func (x ptset) Intersects(y_ PointsToSet) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Pointer public interface
|
func (p Pointer) String() string {
|
||||||
|
|
||||||
// ptr adapts a node to the Pointer interface.
|
|
||||||
type ptr struct {
|
|
||||||
a *analysis
|
|
||||||
cgn *cgnode
|
|
||||||
n nodeid // non-zero
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p ptr) String() string {
|
|
||||||
return fmt.Sprintf("n%d", p.n)
|
return fmt.Sprintf("n%d", p.n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ptr) Context() call.GraphNode {
|
// Context returns the context of this pointer,
|
||||||
|
// if it corresponds to a local variable.
|
||||||
|
func (p Pointer) Context() call.GraphNode {
|
||||||
return p.cgn
|
return p.cgn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ptr) PointsTo() PointsToSet {
|
// PointsTo returns the points-to set of this pointer.
|
||||||
return ptset{p.a, p.a.nodes[p.n].pts}
|
func (p Pointer) PointsTo() PointsToSet {
|
||||||
|
return PointsToSet{p.a, p.a.nodes[p.n].pts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ptr) MayAlias(q Pointer) bool {
|
// MayAlias reports whether the receiver pointer may alias
|
||||||
|
// the argument pointer.
|
||||||
|
func (p Pointer) MayAlias(q Pointer) bool {
|
||||||
return p.PointsTo().Intersects(q.PointsTo())
|
return p.PointsTo().Intersects(q.PointsTo())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ptr) DynamicTypes() *typemap.M {
|
// DynamicTypes returns p.PointsTo().DynamicTypes().
|
||||||
|
func (p Pointer) DynamicTypes() *typemap.M {
|
||||||
return p.PointsTo().DynamicTypes()
|
return p.PointsTo().DynamicTypes()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,14 +82,14 @@ func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) {
|
||||||
|
|
||||||
// Record the (v, id) relation if the client has queried pts(v).
|
// Record the (v, id) relation if the client has queried pts(v).
|
||||||
if _, ok := a.config.Queries[v]; ok {
|
if _, ok := a.config.Queries[v]; ok {
|
||||||
a.result.Queries[v] = append(a.result.Queries[v], ptr{a, cgn, id})
|
a.result.Queries[v] = append(a.result.Queries[v], Pointer{a, cgn, id})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the (*v, id) relation if the client has queried pts(*v).
|
// Record the (*v, id) relation if the client has queried pts(*v).
|
||||||
if _, ok := a.config.IndirectQueries[v]; ok {
|
if _, ok := a.config.IndirectQueries[v]; ok {
|
||||||
indirect := a.addNodes(v.Type(), "query.indirect")
|
indirect := a.addNodes(v.Type(), "query.indirect")
|
||||||
a.genLoad(cgn, indirect, v, 0, a.sizeof(v.Type()))
|
a.genLoad(cgn, indirect, v, 0, a.sizeof(v.Type()))
|
||||||
a.result.IndirectQueries[v] = append(a.result.IndirectQueries[v], ptr{a, cgn, indirect})
|
a.result.IndirectQueries[v] = append(a.result.IndirectQueries[v], Pointer{a, cgn, indirect})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,7 +539,7 @@ func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) {
|
||||||
if probe == 0 {
|
if probe == 0 {
|
||||||
probe = a.addNodes(t, "print")
|
probe = a.addNodes(t, "print")
|
||||||
a.probes[call] = probe
|
a.probes[call] = probe
|
||||||
Print(call, ptr{a, nil, probe}) // notify client
|
Print(call, Pointer{a, nil, probe}) // notify client
|
||||||
}
|
}
|
||||||
|
|
||||||
a.copy(probe, a.valueNode(call.Args[0]), a.sizeof(t))
|
a.copy(probe, a.valueNode(call.Args[0]), a.sizeof(t))
|
||||||
|
|
|
||||||
|
|
@ -309,9 +309,9 @@ func doOneInput(input, filename string) bool {
|
||||||
e.errorf("unreachable print() statement has expectation %s", e)
|
e.errorf("unreachable print() statement has expectation %s", e)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pr.arg0 == nil {
|
if tArg := pr.instr.Args[0].Type(); !pointer.CanPoint(tArg) {
|
||||||
ok = false
|
ok = false
|
||||||
e.errorf("expectation on non-pointerlike operand: %s", pr.instr.Args[0].Type())
|
e.errorf("expectation on non-pointerlike operand: %s", tArg)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,4 @@ func main() {
|
||||||
// labels, even though it may contain pointers that do.
|
// labels, even though it may contain pointers that do.
|
||||||
print(i) // @pointsto makeinterface:func(x int) int | makeinterface:func(x int, y int) | makeinterface:func(int, int) | makeinterface:int | makeinterface:main.S
|
print(i) // @pointsto makeinterface:func(x int) int | makeinterface:func(x int, y int) | makeinterface:func(int, int) | makeinterface:int | makeinterface:main.S
|
||||||
print(i.(func(int) int)) // @pointsto main.incr
|
print(i.(func(int) int)) // @pointsto main.incr
|
||||||
print(i.(S)) // @pointsto
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,6 @@ func structs2() {
|
||||||
|
|
||||||
var s3 S // @line s2s3
|
var s3 S // @line s2s3
|
||||||
s3.c[2] = new(T) // @line s2s3c
|
s3.c[2] = new(T) // @line s2s3c
|
||||||
print(s3.c) // @pointsto
|
|
||||||
print(&s3.c) // @pointsto s3.c@s2s3:6
|
print(&s3.c) // @pointsto s3.c@s2s3:6
|
||||||
print(s3.c[1]) // @pointsto new@s2s3c:15
|
print(s3.c[1]) // @pointsto new@s2s3c:15
|
||||||
print(&s3.c[1]) // @pointsto s3.c[*]@s2s3:6
|
print(&s3.c[1]) // @pointsto s3.c[*]@s2s3:6
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue