1
// Copyright 2014 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
7
// This file computes the channel "peers" relation over all pairs of
8
// channel operations in the program. The peers are displayed in the
9
// lower pane when a channel operation (make, <-, close) is clicked.
11
// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
12
// then enable reflection in PTA.
18
"code.google.com/p/go.tools/go/pointer"
19
"code.google.com/p/go.tools/go/ssa"
20
"code.google.com/p/go.tools/go/types"
23
func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
24
addSendRecv := func(j *commJSON, op chanOp) {
25
j.Ops = append(j.Ops, commOpJSON{
28
Href: a.posURL(op.pos, op.len),
30
Fn: prettyFunc(nil, op.fn),
34
// Build an undirected bipartite multigraph (binary relation)
35
// of MakeChan ops and send/recv/close ops.
37
// TODO(adonovan): opt: use channel element types to partition
38
// the O(n^2) problem into subproblems.
39
aliasedOps := make(map[*ssa.MakeChan][]chanOp)
40
opToMakes := make(map[chanOp][]*ssa.MakeChan)
41
for _, op := range a.ops {
42
// Combine the PT sets from all contexts.
43
var makes []*ssa.MakeChan // aliased ops
44
ptr, ok := ptsets[op.ch]
46
continue // e.g. channel op in dead code
48
for _, label := range ptr.PointsTo().Labels() {
49
makechan, ok := label.Value().(*ssa.MakeChan)
51
continue // skip intrinsically-created channels for now
53
if makechan.Pos() == token.NoPos {
54
continue // not possible?
56
makes = append(makes, makechan)
57
aliasedOps[makechan] = append(aliasedOps[makechan], op)
62
// Now that complete relation is built, build links for ops.
63
for _, op := range a.ops {
65
Ops: []commOpJSON{}, // (JS wants non-nil)
67
ops := make(map[chanOp]bool)
68
for _, makechan := range opToMakes[op] {
69
v.Ops = append(v.Ops, commOpJSON{
72
Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
75
Fn: makechan.Parent().RelString(op.fn.Package().Object),
77
for _, op := range aliasedOps[makechan] {
85
// Add links for each aliased op.
86
fi, offset := a.fileAndOffset(op.pos)
90
title: "show channel ops",
91
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
94
// Add links for makechan ops themselves.
95
for makechan, ops := range aliasedOps {
97
Ops: []commOpJSON{}, // (JS wants non-nil)
99
for _, op := range ops {
103
fi, offset := a.fileAndOffset(makechan.Pos())
105
start: offset - len("make"),
107
title: "show channel ops",
108
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
113
// -- utilities --------------------------------------------------------
115
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
116
// Derived from oracle/peers.go.
119
mode string // sent|received|closed
125
// chanOps returns a slice of all the channel operations in the instruction.
126
// Derived from oracle/peers.go.
127
func chanOps(instr ssa.Instruction) []chanOp {
130
switch instr := instr.(type) {
132
if instr.Op == token.ARROW {
133
// TODO(adonovan): don't assume <-ch; could be 'range ch'.
134
ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
137
ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
139
for _, st := range instr.States {
141
if st.Dir == types.SendOnly {
144
ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
146
case ssa.CallInstruction:
147
call := instr.Common()
148
if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
149
pos := instr.Common().Pos()
150
ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})