go.tools/oracle: consider calls to close a peer operation.
This makes it possible to find corresponding closes to receives and sends. LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/155260043
This commit is contained in:
parent
cced8d21dd
commit
70f0e2472d
|
|
@ -70,7 +70,7 @@ describe
|
||||||
|
|
||||||
peers
|
peers
|
||||||
|
|
||||||
Permit querying from a makechan, close(), for...range, or reflective op.
|
Permit querying from a makechan, for...range, or reflective op.
|
||||||
|
|
||||||
Report aliasing reflect.{Send,Recv,Close} and close() operations.
|
Report aliasing reflect.{Send,Recv,Close} and close() operations.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,10 @@ import (
|
||||||
// TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
|
// TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
|
||||||
// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
|
// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
|
||||||
// or the implicit receive in "for v := range ch".
|
// or the implicit receive in "for v := range ch".
|
||||||
// TODO(adonovan): support "close" as a channel op.
|
|
||||||
//
|
|
||||||
func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
arrowPos := findArrow(qpos)
|
opPos := findOp(qpos)
|
||||||
if arrowPos == token.NoPos {
|
if opPos == token.NoPos {
|
||||||
return nil, fmt.Errorf("there is no send/receive here")
|
return nil, fmt.Errorf("there is no channel operation here")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildSSA(o)
|
buildSSA(o)
|
||||||
|
|
@ -35,15 +33,15 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
var queryOp chanOp // the originating send or receive operation
|
var queryOp chanOp // the originating send or receive operation
|
||||||
var ops []chanOp // all sends/receives of opposite direction
|
var ops []chanOp // all sends/receives of opposite direction
|
||||||
|
|
||||||
// Look at all send/receive instructions in the whole ssa.Program.
|
// Look at all channel operations in the whole ssa.Program.
|
||||||
// Build a list of those of same type to query.
|
// Build a list of those of same type as the query.
|
||||||
allFuncs := ssautil.AllFunctions(o.prog)
|
allFuncs := ssautil.AllFunctions(o.prog)
|
||||||
for fn := range allFuncs {
|
for fn := range allFuncs {
|
||||||
for _, b := range fn.Blocks {
|
for _, b := range fn.Blocks {
|
||||||
for _, instr := range b.Instrs {
|
for _, instr := range b.Instrs {
|
||||||
for _, op := range chanOps(instr) {
|
for _, op := range chanOps(instr) {
|
||||||
ops = append(ops, op)
|
ops = append(ops, op)
|
||||||
if op.pos == arrowPos {
|
if op.pos == opPos {
|
||||||
queryOp = op // we found the query op
|
queryOp = op // we found the query op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,33 +82,40 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
}
|
}
|
||||||
sort.Sort(byPos(makes))
|
sort.Sort(byPos(makes))
|
||||||
|
|
||||||
// Ascertain which send/receive operations can alias the same make(chan) labels.
|
// Ascertain which channel operations can alias the same make(chan) labels.
|
||||||
var sends, receives []token.Pos
|
var sends, receives, closes []token.Pos
|
||||||
for _, op := range ops {
|
for _, op := range ops {
|
||||||
if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
|
if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
|
||||||
if op.dir == types.SendOnly {
|
switch op.dir {
|
||||||
|
case types.SendOnly:
|
||||||
sends = append(sends, op.pos)
|
sends = append(sends, op.pos)
|
||||||
} else {
|
case types.RecvOnly:
|
||||||
receives = append(receives, op.pos)
|
receives = append(receives, op.pos)
|
||||||
|
case types.SendRecv:
|
||||||
|
closes = append(closes, op.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(byPos(sends))
|
sort.Sort(byPos(sends))
|
||||||
sort.Sort(byPos(receives))
|
sort.Sort(byPos(receives))
|
||||||
|
sort.Sort(byPos(closes))
|
||||||
|
|
||||||
return &peersResult{
|
return &peersResult{
|
||||||
queryPos: arrowPos,
|
queryPos: opPos,
|
||||||
queryType: queryType,
|
queryType: queryType,
|
||||||
makes: makes,
|
makes: makes,
|
||||||
sends: sends,
|
sends: sends,
|
||||||
receives: receives,
|
receives: receives,
|
||||||
|
closes: closes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findArrow returns the position of the enclosing send/receive op
|
// findOp returns the position of the enclosing send/receive/close op.
|
||||||
// (<-) for the query position, or token.NoPos if not found.
|
// For send and receive operations, this is the position of the <- token;
|
||||||
|
// for close operations, it's the Lparen of the function call.
|
||||||
//
|
//
|
||||||
func findArrow(qpos *QueryPos) token.Pos {
|
// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
|
||||||
|
func findOp(qpos *QueryPos) token.Pos {
|
||||||
for _, n := range qpos.path {
|
for _, n := range qpos.path {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *ast.UnaryExpr:
|
case *ast.UnaryExpr:
|
||||||
|
|
@ -119,6 +124,13 @@ func findArrow(qpos *QueryPos) token.Pos {
|
||||||
}
|
}
|
||||||
case *ast.SendStmt:
|
case *ast.SendStmt:
|
||||||
return n.Arrow
|
return n.Arrow
|
||||||
|
case *ast.CallExpr:
|
||||||
|
// close function call can only exist as a direct identifier
|
||||||
|
if close, ok := unparen(n.Fun).(*ast.Ident); ok {
|
||||||
|
if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
|
||||||
|
return n.Lparen
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return token.NoPos
|
return token.NoPos
|
||||||
|
|
@ -127,7 +139,7 @@ func findArrow(qpos *QueryPos) token.Pos {
|
||||||
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
|
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
|
||||||
type chanOp struct {
|
type chanOp struct {
|
||||||
ch ssa.Value
|
ch ssa.Value
|
||||||
dir types.ChanDir // SendOnly or RecvOnly
|
dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,14 +158,19 @@ func chanOps(instr ssa.Instruction) []chanOp {
|
||||||
for _, st := range instr.States {
|
for _, st := range instr.States {
|
||||||
ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
|
ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
|
||||||
}
|
}
|
||||||
|
case ssa.CallInstruction:
|
||||||
|
cc := instr.Common()
|
||||||
|
if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
|
||||||
|
ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ops
|
return ops
|
||||||
}
|
}
|
||||||
|
|
||||||
type peersResult struct {
|
type peersResult struct {
|
||||||
queryPos token.Pos // of queried '<-' token
|
queryPos token.Pos // of queried channel op
|
||||||
queryType types.Type // type of queried channel
|
queryType types.Type // type of queried channel
|
||||||
makes, sends, receives []token.Pos // positions of aliased makechan/send/receive instrs
|
makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *peersResult) display(printf printfFunc) {
|
func (r *peersResult) display(printf printfFunc) {
|
||||||
|
|
@ -171,6 +188,9 @@ func (r *peersResult) display(printf printfFunc) {
|
||||||
for _, receive := range r.receives {
|
for _, receive := range r.receives {
|
||||||
printf(receive, "\treceived from, here")
|
printf(receive, "\treceived from, here")
|
||||||
}
|
}
|
||||||
|
for _, clos := range r.closes {
|
||||||
|
printf(clos, "\tclosed, here")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||||
|
|
@ -187,6 +207,9 @@ func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||||
for _, receive := range r.receives {
|
for _, receive := range r.receives {
|
||||||
peers.Receives = append(peers.Receives, fset.Position(receive).String())
|
peers.Receives = append(peers.Receives, fset.Position(receive).String())
|
||||||
}
|
}
|
||||||
|
for _, clos := range r.closes {
|
||||||
|
peers.Closes = append(peers.Closes, fset.Position(clos).String())
|
||||||
|
}
|
||||||
res.Peers = peers
|
res.Peers = peers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ type Peers struct {
|
||||||
Allocs []string `json:"allocs,omitempty"` // locations of aliased make(chan) ops
|
Allocs []string `json:"allocs,omitempty"` // locations of aliased make(chan) ops
|
||||||
Sends []string `json:"sends,omitempty"` // locations of aliased ch<-x ops
|
Sends []string `json:"sends,omitempty"` // locations of aliased ch<-x ops
|
||||||
Receives []string `json:"receives,omitempty"` // locations of aliased <-ch ops
|
Receives []string `json:"receives,omitempty"` // locations of aliased <-ch ops
|
||||||
|
Closes []string `json:"closes,omitempty"` // locations of aliased close(ch) ops
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Referrers is the result of a 'referrers' query.
|
// A Referrers is the result of a 'referrers' query.
|
||||||
|
|
|
||||||
|
|
@ -37,4 +37,16 @@ func main() {
|
||||||
|
|
||||||
for _ = range chA {
|
for _ = range chA {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close(chA) // @peers peer-close-chA "chA"
|
||||||
|
|
||||||
|
chC := make(chan *int)
|
||||||
|
(close)(chC) // @peers peer-close-chC "chC"
|
||||||
|
|
||||||
|
close := func(ch chan *int) chan *int {
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
close(chC) <- &b // @peers peer-send-chC "chC"
|
||||||
|
<-close(chC) // @peers peer-recv-chC "chC"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ This channel of type chan *int may be:
|
||||||
received from, here
|
received from, here
|
||||||
received from, here
|
received from, here
|
||||||
received from, here
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
-------- @pointsto pointsto-rA --------
|
-------- @pointsto pointsto-rA --------
|
||||||
this *int may point to these objects:
|
this *int may point to these objects:
|
||||||
|
|
@ -50,6 +51,7 @@ This channel of type chan *int may be:
|
||||||
received from, here
|
received from, here
|
||||||
received from, here
|
received from, here
|
||||||
received from, here
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
-------- @peers peer-send-chA' --------
|
-------- @peers peer-send-chA' --------
|
||||||
This channel of type chan *int may be:
|
This channel of type chan *int may be:
|
||||||
|
|
@ -60,4 +62,39 @@ This channel of type chan *int may be:
|
||||||
received from, here
|
received from, here
|
||||||
received from, here
|
received from, here
|
||||||
received from, here
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
|
-------- @peers peer-close-chA --------
|
||||||
|
This channel of type chan *int may be:
|
||||||
|
allocated here
|
||||||
|
allocated here
|
||||||
|
sent to, here
|
||||||
|
sent to, here
|
||||||
|
received from, here
|
||||||
|
received from, here
|
||||||
|
received from, here
|
||||||
|
received from, here
|
||||||
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
|
-------- @peers peer-close-chC --------
|
||||||
|
This channel of type chan *int may be:
|
||||||
|
allocated here
|
||||||
|
sent to, here
|
||||||
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
|
-------- @peers peer-send-chC --------
|
||||||
|
This channel of type chan *int may be:
|
||||||
|
allocated here
|
||||||
|
sent to, here
|
||||||
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
|
-------- @peers peer-recv-chC --------
|
||||||
|
This channel of type chan *int may be:
|
||||||
|
allocated here
|
||||||
|
sent to, here
|
||||||
|
received from, here
|
||||||
|
closed, here
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue