~vtuson/scopecreator/twitter-template

« back to all changes in this revision

Viewing changes to src/go/src/code.google.com/p/go.tools/godoc/analysis/peers.go

  • Committer: Victor Palau
  • Date: 2015-03-11 14:24:42 UTC
  • Revision ID: vtuson@gmail.com-20150311142442-f2pxp111c8ynv232
public release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
4
 
 
5
package analysis
 
6
 
 
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.
 
10
 
 
11
// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
 
12
// then enable reflection in PTA.
 
13
 
 
14
import (
 
15
        "fmt"
 
16
        "go/token"
 
17
 
 
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"
 
21
)
 
22
 
 
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{
 
26
                        Op: anchorJSON{
 
27
                                Text: op.mode,
 
28
                                Href: a.posURL(op.pos, op.len),
 
29
                        },
 
30
                        Fn: prettyFunc(nil, op.fn),
 
31
                })
 
32
        }
 
33
 
 
34
        // Build an undirected bipartite multigraph (binary relation)
 
35
        // of MakeChan ops and send/recv/close ops.
 
36
        //
 
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]
 
45
                if !ok {
 
46
                        continue // e.g. channel op in dead code
 
47
                }
 
48
                for _, label := range ptr.PointsTo().Labels() {
 
49
                        makechan, ok := label.Value().(*ssa.MakeChan)
 
50
                        if !ok {
 
51
                                continue // skip intrinsically-created channels for now
 
52
                        }
 
53
                        if makechan.Pos() == token.NoPos {
 
54
                                continue // not possible?
 
55
                        }
 
56
                        makes = append(makes, makechan)
 
57
                        aliasedOps[makechan] = append(aliasedOps[makechan], op)
 
58
                }
 
59
                opToMakes[op] = makes
 
60
        }
 
61
 
 
62
        // Now that complete relation is built, build links for ops.
 
63
        for _, op := range a.ops {
 
64
                v := commJSON{
 
65
                        Ops: []commOpJSON{}, // (JS wants non-nil)
 
66
                }
 
67
                ops := make(map[chanOp]bool)
 
68
                for _, makechan := range opToMakes[op] {
 
69
                        v.Ops = append(v.Ops, commOpJSON{
 
70
                                Op: anchorJSON{
 
71
                                        Text: "made",
 
72
                                        Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
 
73
                                                len("make")),
 
74
                                },
 
75
                                Fn: makechan.Parent().RelString(op.fn.Package().Object),
 
76
                        })
 
77
                        for _, op := range aliasedOps[makechan] {
 
78
                                ops[op] = true
 
79
                        }
 
80
                }
 
81
                for op := range ops {
 
82
                        addSendRecv(&v, op)
 
83
                }
 
84
 
 
85
                // Add links for each aliased op.
 
86
                fi, offset := a.fileAndOffset(op.pos)
 
87
                fi.addLink(aLink{
 
88
                        start:   offset,
 
89
                        end:     offset + op.len,
 
90
                        title:   "show channel ops",
 
91
                        onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
 
92
                })
 
93
        }
 
94
        // Add links for makechan ops themselves.
 
95
        for makechan, ops := range aliasedOps {
 
96
                v := commJSON{
 
97
                        Ops: []commOpJSON{}, // (JS wants non-nil)
 
98
                }
 
99
                for _, op := range ops {
 
100
                        addSendRecv(&v, op)
 
101
                }
 
102
 
 
103
                fi, offset := a.fileAndOffset(makechan.Pos())
 
104
                fi.addLink(aLink{
 
105
                        start:   offset - len("make"),
 
106
                        end:     offset,
 
107
                        title:   "show channel ops",
 
108
                        onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
 
109
                })
 
110
        }
 
111
}
 
112
 
 
113
// -- utilities --------------------------------------------------------
 
114
 
 
115
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
 
116
// Derived from oracle/peers.go.
 
117
type chanOp struct {
 
118
        ch   ssa.Value
 
119
        mode string // sent|received|closed
 
120
        pos  token.Pos
 
121
        len  int
 
122
        fn   *ssa.Function
 
123
}
 
124
 
 
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 {
 
128
        fn := instr.Parent()
 
129
        var ops []chanOp
 
130
        switch instr := instr.(type) {
 
131
        case *ssa.UnOp:
 
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})
 
135
                }
 
136
        case *ssa.Send:
 
137
                ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
 
138
        case *ssa.Select:
 
139
                for _, st := range instr.States {
 
140
                        mode := "received"
 
141
                        if st.Dir == types.SendOnly {
 
142
                                mode = "sent"
 
143
                        }
 
144
                        ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
 
145
                }
 
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})
 
151
                }
 
152
        }
 
153
        return ops
 
154
}