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.
19 "golang.org/x/tools/go/pointer"
20 "golang.org/x/tools/go/ssa"
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().Pkg),
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 cmd/guru/peers.go.
119 mode string // sent|received|closed
125 // chanOps returns a slice of all the channel operations in the instruction.
126 // Derived from cmd/guru/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})