/
peers.go
154 lines (141 loc) · 4.29 KB
/
peers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2014 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.
package analysis
// This file computes the channel "peers" relation over all pairs of
// channel operations in the program. The peers are displayed in the
// lower pane when a channel operation (make, <-, close) is clicked.
// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
// then enable reflection in PTA.
import (
"fmt"
"go/token"
"github.com/polaris1119/go.tools/go/pointer"
"github.com/polaris1119/go.tools/go/ssa"
"github.com/polaris1119/go.tools/go/types"
)
func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
addSendRecv := func(j *commJSON, op chanOp) {
j.Ops = append(j.Ops, commOpJSON{
Op: anchorJSON{
Text: op.mode,
Href: a.posURL(op.pos, op.len),
},
Fn: prettyFunc(nil, op.fn),
})
}
// Build an undirected bipartite multigraph (binary relation)
// of MakeChan ops and send/recv/close ops.
//
// TODO(adonovan): opt: use channel element types to partition
// the O(n^2) problem into subproblems.
aliasedOps := make(map[*ssa.MakeChan][]chanOp)
opToMakes := make(map[chanOp][]*ssa.MakeChan)
for _, op := range a.ops {
// Combine the PT sets from all contexts.
var makes []*ssa.MakeChan // aliased ops
ptr, ok := ptsets[op.ch]
if !ok {
continue // e.g. channel op in dead code
}
for _, label := range ptr.PointsTo().Labels() {
makechan, ok := label.Value().(*ssa.MakeChan)
if !ok {
continue // skip intrinsically-created channels for now
}
if makechan.Pos() == token.NoPos {
continue // not possible?
}
makes = append(makes, makechan)
aliasedOps[makechan] = append(aliasedOps[makechan], op)
}
opToMakes[op] = makes
}
// Now that complete relation is built, build links for ops.
for _, op := range a.ops {
v := commJSON{
Ops: []commOpJSON{}, // (JS wants non-nil)
}
ops := make(map[chanOp]bool)
for _, makechan := range opToMakes[op] {
v.Ops = append(v.Ops, commOpJSON{
Op: anchorJSON{
Text: "made",
Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
len("make")),
},
Fn: makechan.Parent().RelString(op.fn.Package().Object),
})
for _, op := range aliasedOps[makechan] {
ops[op] = true
}
}
for op := range ops {
addSendRecv(&v, op)
}
// Add links for each aliased op.
fi, offset := a.fileAndOffset(op.pos)
fi.addLink(aLink{
start: offset,
end: offset + op.len,
title: "show channel ops",
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
})
}
// Add links for makechan ops themselves.
for makechan, ops := range aliasedOps {
v := commJSON{
Ops: []commOpJSON{}, // (JS wants non-nil)
}
for _, op := range ops {
addSendRecv(&v, op)
}
fi, offset := a.fileAndOffset(makechan.Pos())
fi.addLink(aLink{
start: offset - len("make"),
end: offset,
title: "show channel ops",
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
})
}
}
// -- utilities --------------------------------------------------------
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
// Derived from oracle/peers.go.
type chanOp struct {
ch ssa.Value
mode string // sent|received|closed
pos token.Pos
len int
fn *ssa.Function
}
// chanOps returns a slice of all the channel operations in the instruction.
// Derived from oracle/peers.go.
func chanOps(instr ssa.Instruction) []chanOp {
fn := instr.Parent()
var ops []chanOp
switch instr := instr.(type) {
case *ssa.UnOp:
if instr.Op == token.ARROW {
// TODO(adonovan): don't assume <-ch; could be 'range ch'.
ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
}
case *ssa.Send:
ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
case *ssa.Select:
for _, st := range instr.States {
mode := "received"
if st.Dir == types.SendOnly {
mode = "sent"
}
ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
}
case ssa.CallInstruction:
call := instr.Common()
if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
pos := instr.Common().Pos()
ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
}
}
return ops
}