LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2013 The Go Authors. All rights reserved. | 1 // Copyright 2013 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package oracle | 5 package oracle |
6 | 6 |
7 import ( | 7 import ( |
8 "go/ast" | 8 "go/ast" |
9 "go/token" | 9 "go/token" |
| 10 "sort" |
10 | 11 |
11 "code.google.com/p/go.tools/go/types" | 12 "code.google.com/p/go.tools/go/types" |
| 13 "code.google.com/p/go.tools/oracle/json" |
12 "code.google.com/p/go.tools/pointer" | 14 "code.google.com/p/go.tools/pointer" |
13 "code.google.com/p/go.tools/ssa" | 15 "code.google.com/p/go.tools/ssa" |
14 ) | 16 ) |
15 | 17 |
16 // peers enumerates, for a given channel send (or receive) operation, | 18 // peers enumerates, for a given channel send (or receive) operation, |
17 // the set of possible receives (or sends) that correspond to it. | 19 // the set of possible receives (or sends) that correspond to it. |
18 // | 20 // |
19 // TODO(adonovan): support reflect.{Select,Recv,Send}. | 21 // TODO(adonovan): support reflect.{Select,Recv,Send}. |
20 // TODO(adonovan): permit the user to query based on a MakeChan (not send/recv), | 22 // TODO(adonovan): permit the user to query based on a MakeChan (not send/recv), |
21 // or the implicit receive in "for v := range ch". | 23 // or the implicit receive in "for v := range ch". |
(...skipping 23 matching lines...) Expand all Loading... |
45 } | 47 } |
46 } | 48 } |
47 } | 49 } |
48 } | 50 } |
49 if queryOp.ch == nil { | 51 if queryOp.ch == nil { |
50 return nil, o.errorf(arrowPos, "ssa.Instruction for send/receive
not found") | 52 return nil, o.errorf(arrowPos, "ssa.Instruction for send/receive
not found") |
51 } | 53 } |
52 | 54 |
53 // Discard operations of wrong channel element type. | 55 // Discard operations of wrong channel element type. |
54 // Build set of channel ssa.Values as query to pointer analysis. | 56 // Build set of channel ssa.Values as query to pointer analysis. |
55 » queryElemType := queryOp.ch.Type().Underlying().(*types.Chan).Elem() | 57 » // We compare channels by element types, not channel types, to |
| 58 » // ignore both directionality and type names. |
| 59 » queryType := queryOp.ch.Type() |
| 60 » queryElemType := queryType.Underlying().(*types.Chan).Elem() |
56 channels := map[ssa.Value][]pointer.Pointer{queryOp.ch: nil} | 61 channels := map[ssa.Value][]pointer.Pointer{queryOp.ch: nil} |
57 i := 0 | 62 i := 0 |
58 for _, op := range ops { | 63 for _, op := range ops { |
59 if types.IsIdentical(op.ch.Type().Underlying().(*types.Chan).Ele
m(), queryElemType) { | 64 if types.IsIdentical(op.ch.Type().Underlying().(*types.Chan).Ele
m(), queryElemType) { |
60 channels[op.ch] = nil | 65 channels[op.ch] = nil |
61 ops[i] = op | 66 ops[i] = op |
62 i++ | 67 i++ |
63 } | 68 } |
64 } | 69 } |
65 ops = ops[:i] | 70 ops = ops[:i] |
66 | 71 |
67 // Run the pointer analysis. | 72 // Run the pointer analysis. |
68 o.config.QueryValues = channels | 73 o.config.QueryValues = channels |
69 ptrAnalysis(o) | 74 ptrAnalysis(o) |
70 | 75 |
71 // Combine the PT sets from all contexts. | 76 // Combine the PT sets from all contexts. |
72 queryChanPts := pointer.PointsToCombined(channels[queryOp.ch]) | 77 queryChanPts := pointer.PointsToCombined(channels[queryOp.ch]) |
73 | 78 |
| 79 // Ascertain which make(chan) labels the query's channel can alias. |
| 80 var makes []token.Pos |
| 81 for _, label := range queryChanPts.Labels() { |
| 82 makes = append(makes, label.Pos()) |
| 83 } |
| 84 sort.Sort(byPos(makes)) |
| 85 |
| 86 // Ascertain which send/receive operations can alias the same make(chan)
labels. |
| 87 var sends, receives []token.Pos |
| 88 for _, op := range ops { |
| 89 for _, ptr := range o.config.QueryValues[op.ch] { |
| 90 if ptr != nil && ptr.PointsTo().Intersects(queryChanPts)
{ |
| 91 if op.dir == ast.SEND { |
| 92 sends = append(sends, op.pos) |
| 93 } else { |
| 94 receives = append(receives, op.pos) |
| 95 } |
| 96 } |
| 97 } |
| 98 } |
| 99 sort.Sort(byPos(sends)) |
| 100 sort.Sort(byPos(receives)) |
| 101 |
74 return &peersResult{ | 102 return &peersResult{ |
75 » » queryOp: queryOp, | 103 » » queryPos: arrowPos, |
76 » » ops: ops, | 104 » » queryType: queryType, |
77 » » queryChanPts: queryChanPts, | 105 » » makes: makes, |
| 106 » » sends: sends, |
| 107 » » receives: receives, |
78 }, nil | 108 }, nil |
79 } | 109 } |
80 | 110 |
81 // findArrow returns the position of the enclosing send/receive op | 111 // findArrow returns the position of the enclosing send/receive op |
82 // (<-) for the query position, or token.NoPos if not found. | 112 // (<-) for the query position, or token.NoPos if not found. |
83 // | 113 // |
84 func findArrow(o *oracle) token.Pos { | 114 func findArrow(o *oracle) token.Pos { |
85 for _, n := range o.queryPath { | 115 for _, n := range o.queryPath { |
86 switch n := n.(type) { | 116 switch n := n.(type) { |
87 case *ast.UnaryExpr: | 117 case *ast.UnaryExpr: |
(...skipping 27 matching lines...) Expand all Loading... |
115 ops = append(ops, chanOp{instr.Chan, ast.SEND, instr.Pos()}) | 145 ops = append(ops, chanOp{instr.Chan, ast.SEND, instr.Pos()}) |
116 case *ssa.Select: | 146 case *ssa.Select: |
117 for _, st := range instr.States { | 147 for _, st := range instr.States { |
118 ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos}) | 148 ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos}) |
119 } | 149 } |
120 } | 150 } |
121 return ops | 151 return ops |
122 } | 152 } |
123 | 153 |
124 type peersResult struct { | 154 type peersResult struct { |
125 » queryOp chanOp | 155 » queryPos token.Pos // of queried '<-' token |
126 » ops []chanOp | 156 » queryType types.Type // type of queried channel |
127 » queryChanPts pointer.PointsToSet | 157 » makes, sends, receives []token.Pos // positions of alisaed makechan/send
/receive instrs |
128 } | 158 } |
129 | 159 |
130 func (r *peersResult) display(o *oracle) { | 160 func (r *peersResult) display(printf printfFunc) { |
131 » // Report which make(chan) labels the query's channel can alias. | 161 » if len(r.makes) == 0 { |
132 » labels := r.queryChanPts.Labels() | 162 » » printf(r.queryPos, "This channel can't point to anything.") |
133 » if len(labels) == 0 { | |
134 » » o.printf(r.queryOp.pos, "This channel can't point to anything.") | |
135 return | 163 return |
136 } | 164 } |
137 » o.printf(r.queryOp.pos, "This channel of type %s may be:", r.queryOp.ch.
Type()) | 165 » printf(r.queryPos, "This channel of type %s may be:", r.queryType) |
138 » // TODO(adonovan): sort, to ensure test determinism. | 166 » for _, alloc := range r.makes { |
139 » for _, label := range labels { | 167 » » printf(alloc, "\tallocated here") |
140 » » o.printf(label, "\tallocated here") | 168 » } |
141 » } | 169 » for _, send := range r.sends { |
142 | 170 » » printf(send, "\tsent to, here") |
143 » // Report which send/receive operations can alias the same make(chan) la
bels. | 171 » } |
144 » for _, op := range r.ops { | 172 » for _, receive := range r.receives { |
145 » » // TODO(adonovan): sort, to ensure test determinism. | 173 » » printf(receive, "\treceived from, here") |
146 » » for _, ptr := range o.config.QueryValues[op.ch] { | 174 » } |
147 » » » if ptr != nil && ptr.PointsTo().Intersects(r.queryChanPt
s) { | 175 } |
148 » » » » verb := "received from" | 176 |
149 » » » » if op.dir == ast.SEND { | 177 func (r *peersResult) toJSON(res *json.Result, fset *token.FileSet) { |
150 » » » » » verb = "sent to" | 178 » peers := &json.Peers{ |
151 » » » » } | 179 » » Pos: fset.Position(r.queryPos).String(), |
152 » » » » o.printf(op.pos, "\t%s, here", verb) | 180 » » Type: r.queryType.String(), |
153 » » » } | 181 » } |
154 » » } | 182 » for _, alloc := range r.makes { |
155 » } | 183 » » peers.Allocs = append(peers.Allocs, fset.Position(alloc).String(
)) |
156 } | 184 » } |
| 185 » for _, send := range r.sends { |
| 186 » » peers.Sends = append(peers.Sends, fset.Position(send).String()) |
| 187 » } |
| 188 » for _, receive := range r.receives { |
| 189 » » peers.Receives = append(peers.Receives, fset.Position(receive).S
tring()) |
| 190 » } |
| 191 » res.Peers = peers |
| 192 } |
| 193 |
| 194 // -------- utils -------- |
| 195 |
| 196 type byPos []token.Pos |
| 197 |
| 198 func (p byPos) Len() int { return len(p) } |
| 199 func (p byPos) Less(i, j int) bool { return p[i] < p[j] } |
| 200 func (p byPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] } |
LEFT | RIGHT |