LEFT | RIGHT |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 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 // Theory: | 5 // The base version before this file existed, active with debug['s'] |
6 // Note: this is an attempt at something more general than I actually do below. | 6 // == 0, assumes any node that has a reference to it created at some |
| 7 // point, may flow to the global scope except |
| 8 // - if its address is dereferenced immediately with only CONVNOPs in |
| 9 // between the * and the & |
| 10 // - if it is for a closure variable and the closure executed at the |
| 11 // place it's defined |
7 // | 12 // |
8 // A subset of all AST nodes holds values. These are the 'expression' | 13 // Flag -s disables the old codepaths and switches on the code here: |
9 // nodes. Nodes have a scope: the function they are defined in or the | |
10 // global scope. Nodes are visible in their scope and all scopes | |
11 // contained in it (closures). The scope of a function parameter is | |
12 // that function. | |
13 // | 14 // |
14 // A node ref can be a reference to another node n. References | 15 // First escfunc, escstmt and escexpr recurse over the ast of each |
15 // to nodes are created with the & operator (OADDR). Go slices, maps, | 16 // function to dig out flow(dst,src) edges between any |
16 // and channels are references to arrays, runtime.Hashmap<K,V> and runtime.Chan
<T> | 17 // pointer-containing nodes and store them in dst->escflowsrc. For |
17 // resp. The last two are hidden from the programmer but relevant here. | 18 // variables assigned to a variable in an outer scope or used as a |
18 // A final important case form the 'closure' variables: any variable from | 19 // return value, they store a flow(theSink, src) edge to a fake node |
19 // an outer scope has it's reference implicitly taken. | 20 // 'the Sink'.» For variables referenced in closures, an edge |
| 21 // flow(closure, &var) is recorded and the flow of a closure itself to |
| 22 // an outer scope is tracked the same way as other variables. |
20 // | 23 // |
21 // References can be 'dereferenced' explicitly or implictly: | 24 // Then escflood walks the graph starting at theSink and tags all |
22 // - the * operator (OIND) explicitly dereferences· | 25 // variables of it can reach an & node as escaping and all function |
23 // - the slice index implicitly dereferences the slice (and accesses an eleme
nt of | 26 // parameters it can reach as leaking. |
24 // the underlying array) | |
25 // - the map index operator implicitly dereferences the map and selects an el
ement of it | |
26 // - a channel send or receive operation implicitly dereferences the channel
and selects | |
27 // an element of it (the head or the tail of the queue). | |
28 // - access to a 'closure variable' of an outer scope from the inner scope· | |
29 // happens by implictly dereferencing it. | |
30 // | 27 // |
31 // The re-slice operator creates a new reference to the array the | 28 // Watch the variables moved to the heap and parameters tagged as |
32 // slice is a reference to, the index operator on an array does not | 29 // unsafe with -m, more detailed analysis output with -mm |
33 // dereference the array, as an array is not a reference. It just | |
34 // selects a part, like the ODOT does on a struct. | |
35 // | 30 // |
36 // An expression node src, irrespective of whether it's a reference | 31 |
37 // to another node or not, 'flows' to another node dst, flows(dst, src) | |
38 // if at any point during the execution of the program | |
39 // - src is assigned to dst | |
40 // - src is a struct and one of its fields is assigned to dst | |
41 // - dst is a struct and src is assigned to one of its fields | |
42 // - src is an array and one of its elements is assigned to dst | |
43 // - dst is an array, and src is assigned to one if its elements | |
44 // - dst is a runtime.Hashmap<K,V> and src is assigned to the key or value pa
rt of one of its elements | |
45 // - src is a runtime.Hashmap<K,V> and the key or value part of one of its el
ements is assigned to dst | |
46 // - dst is a runtime.Chan<T> and src is sent to it (inserted as the head ele
ment) | |
47 // - src is a runtime.Chan<T> and dst is assigned the result of a receive ope
ration (the tail element) | |
48 // - dst is a closure and src is a variable declared in its scope | |
49 // - dst is a closure (function literal), and src is the implicit reference t
aken | |
50 // to a variable (not that variable itself!) in an enclosing scope. (this i
s | |
51 // really a special case of the previous one, combined with an assignment | |
52 // | |
53 // This way, the 'flow' of values forms a directed graph between nodes. | |
54 // We can extend the above pseudo definition of flow with it's transitive | |
55 // closure:· | |
56 // - there exists a node n such that flows(dst, n) and flows(n, src) | |
57 // | |
58 // If we imagine the flow edges as horizontal, we can imagine referencing | |
59 // as taking a step down, and dereferencing taking a step up, and we | |
60 // can draw nice pictures to figure out what's going on. | |
61 // | |
62 // A struct may contain references at different levels in this picture, | |
63 // so be careful before drawing your whiteboard full of nonsense. | |
64 // | |
65 // Let's call the set of all nodes dst' such that dst'==src or flows(dst, src)
the | |
66 // downstream of src. and vice versa the upstream, and the union of these | |
67 // the stream of a node. | |
68 // | |
69 // Since we don't consider the time ordering of flows, the | |
70 // node *could* be accessed trough the dereference of any node in the | |
71 // stream of any of it's references, or through the double | |
72 // dereference of any node in the stream of any reference | |
73 // to a node in the alias closure of any reference to it etc. | |
74 // | |
75 // It helps if you draw it. | |
76 // | |
77 // Let's use this to define:· | |
78 // A node x aliases a node y, if· | |
79 // there is a reference xref of x | |
80 // and y is a dereference of a node yref and | |
81 // xref is in the upstream of yref, or | |
82 // there is a node xref' in the downstream of xref and a node yref' in t
he upstream of yref | |
83 // and xref' aliases yref' | |
84 // | |
85 // For most values this is of passing interest, but any node src | |
86 // referenced by a node ref that is aliased to outside of src's | |
87 // scope, must be moved to the heap. | |
88 // | |
89 // This can happen in 4 ways: | |
90 // - ref is assigned to a global variable | |
91 // - ref is returned from a function (or assigned to one of it's output paramet
ers) | |
92 // - ref is passed as a value to a function f as a parameter p (including the '
this' parameter) , and p leaks from f | |
93 // - ref is passed as a value to a function called in a go (OPROC) statement | |
94 // | |
95 // Since the implicit references of closure variables, by the | |
96 // definition above, flows to the closure node this covers the cases | |
97 // where the closure gets executed only in its scope or gets returned | |
98 // or exectuted in a go (OPROC) statement. | |
99 // | |
100 // The base version of gc, before this file existed, assumes any node that has a
reference | |
101 // to it created at some point, may flow to the global scope except | |
102 // - if it is for a closure variable and the closure executed at the place it'
s defined | |
103 // - if it's address is dereferenced immediately with only CONVNOPs in between | |
104 // | |
105 // This file implements 2 levels, or will, when i'm done. | |
106 // Level 1: the flow of the reference. As soon as the reference is referenced,
the bets are off. | |
107 // Level 2: references are checked for aliasing. | |
108 // | |
109 // First we recurse over the ast of each function to dig out the | |
110 // flow edges and store them in the node's asrc and adst fields. | |
111 // | |
112 //· | |
113 // | |
114 #include "go.h" | 32 #include "go.h" |
115 | 33 |
116 static void flows(Node* dst, Node* src); | 34 static void escfunc(Node *func); |
117 static void eastmtlist(Node* dst, NodeList *l); | 35 static void escstmtlist(NodeList *stmts); |
118 static void eastmt(Node* dst, Node *stmt); | 36 static void escstmt(Node *stmt); |
119 static void eaexpr(Node *dst, Node *expr); | 37 static void escexpr(Node *dst, Node *expr); |
120 static void eawalk(Node *sink); | 38 static void escexprcall(Node *dst, Node *callexpr); |
121 | 39 static void escflows(Node* dst, Node* src); |
122 // Fake node that all return values, global variables and | 40 static void escflood(Node *dst); |
123 // goroutine-used variables leak to. | 41 static void escwalk(int level, Node *dst, Node *src); |
124 static Node theSink; | 42 static void esctag(Node *func); |
| 43 |
| 44 // Fake node that all |
| 45 // - return values and output variables |
| 46 // - parameters on imported functions not marked 'safe' |
| 47 // - assignments to global variables |
| 48 // flow to. |
| 49 static Node» theSink; |
| 50 |
| 51 static NodeList* dsts;» » // all dst nodes |
| 52 static int» loopdepth;» // for detecting nested loop scopes |
| 53 static int» pdepth;»» // for debug printing in recursions. |
| 54 static int» floodgen;» // loop prevention in flood/walk |
| 55 static Strlit*» safetag;» // gets slapped on safe parameters' field types
for export |
| 56 static int» dstcount, edgecount;» // diagnostic |
125 | 57 |
126 void | 58 void |
127 escanal() | 59 escapes(void) |
128 { | 60 { |
129 » NodeList* l; | 61 » NodeList *l; |
130 | 62 |
131 » for(l=xtop; l; l=l->next) { | 63 » theSink.op = ONAME; |
132 » » switch(l->n->op) { | 64 » theSink.class = PEXTERN; |
133 » » case ODCL: | 65 » theSink.sym = lookup(".sink"); |
134 » » case OAS: | 66 » theSink.escloopdepth = -1; |
135 » » » eastmt(&theSink, l->n); | 67 |
| 68 » safetag = strlit("noescape"); |
| 69 |
| 70 » // flow-analyze top level functions |
| 71 » for(l=xtop; l; l=l->next) |
| 72 » » if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) |
| 73 » » » escfunc(l->n); |
| 74 |
| 75 » // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); |
| 76 |
| 77 » // visit the updstream of each dst, mark address nodes with |
| 78 » // addrescapes, mark parameters unsafe |
| 79 » for (l = dsts; l; l=l->next) |
| 80 » » escflood(l->n); |
| 81 |
| 82 » // for all top level functions, tag the typenodes corresponding to the p
aram nodes |
| 83 » for(l=xtop; l; l=l->next) |
| 84 » » if(l->n->op == ODCLFUNC) |
| 85 » » » esctag(l->n); |
| 86 } |
| 87 |
| 88 static void |
| 89 escfunc(Node *func) |
| 90 { |
| 91 » Node *savefn, *n; |
| 92 » NodeList *ll; |
| 93 » int saveld; |
| 94 |
| 95 » saveld = loopdepth; |
| 96 » loopdepth = 1; |
| 97 » savefn = curfn; |
| 98 » curfn = func; |
| 99 |
| 100 » for(ll=curfn->dcl; ll; ll=ll->next) { |
| 101 » » if(ll->n->op != ONAME) |
| 102 » » » continue; |
| 103 » » switch (ll->n->class) { |
| 104 » » case PPARAMOUT: |
| 105 » » » // output parameters flow to the sink |
| 106 » » » escflows(&theSink, ll->n); |
| 107 » » » ll->n->escloopdepth = loopdepth; |
136 break; | 108 break; |
137 » » case ODCLFUNC: | 109 » » case PPARAM: |
138 » » » curfn = l->n; | 110 » » » ll->n->esc = EscNone;» // prime for escflood later |
139 » » » eastmtlist(N, l->n->nbody); | 111 » » » ll->n->escloopdepth = loopdepth; |
140 » » » curfn = nil; | |
141 break; | 112 break; |
142 } | 113 } |
143 } | 114 } |
144 | 115 |
145 » eawalk(&theSink); | 116 » // walk will take the address of cvar->closure later and assign it to cv
ar. |
146 } | 117 » // handle that here by linking a fake oaddr node directly to the closure
. |
147 | 118 » for (ll=curfn->cvars; ll; ll=ll->next) { |
148 static void | 119 » » if(ll->n->op == OXXX) // see dcl.c:398 |
149 eastmtlist(Node* dst, NodeList* l) | 120 » » » continue; |
150 { | 121 |
151 » for (; l; l = l->next) | 122 » » n = nod(OADDR, ll->n->closure, N); |
152 » » eastmt(dst, l->n); | 123 » » n->lineno = ll->n->lineno; |
153 } | 124 » » typecheck(&n, Erv); |
154 | 125 » » escexpr(curfn, n); |
155 static void | 126 » } |
156 eastmt(Node* fun, Node *stmt) | 127 |
157 { | 128 » escstmtlist(curfn->nbody); |
158 » int cl, cr; | 129 » curfn = savefn; |
| 130 » loopdepth = saveld; |
| 131 } |
| 132 |
| 133 static void |
| 134 escstmtlist(NodeList* stmts) |
| 135 { |
| 136 » for(; stmts; stmts=stmts->next) |
| 137 » » escstmt(stmts->n); |
| 138 } |
| 139 |
| 140 static void |
| 141 escstmt(Node *stmt) |
| 142 { |
| 143 » int cl, cr, lno; |
159 NodeList *ll, *lr; | 144 NodeList *ll, *lr; |
| 145 Node *dst; |
160 | 146 |
161 if(stmt == N) | 147 if(stmt == N) |
162 return; | 148 return; |
163 | 149 |
164 » if (stmt->trecur) | 150 » lno = setlineno(stmt); |
165 » » return; | 151 |
166 » stmt->trecur = 1; | 152 » if(stmt->typecheck == 0 && stmt->op != ODCL) {» // TODO something with
OAS2 |
167 | 153 » » dump("escstmt missing typecheck", stmt); |
168 » setlineno(stmt); | 154 » » fatal("missing typecheck."); |
169 | 155 » } |
170 » if (stmt->typecheck == 0 && stmt->op != ODCL) { // todo, something with
OAS2 | 156 |
171 » » dump("eastmt missing typecheck", stmt); | 157 » // Common to almost all statements, and nil if n/a. |
172 » » fatal("Missing typecheck."); | 158 » escstmtlist(stmt->ninit); |
173 » } | 159 |
174 | 160 » if(debug['m'] > 1) |
175 » if (fun && fun != &theSink && fun->op != OCLOSURE) | 161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth, |
176 » » fatal("eastmt fun is not a closure: %N", fun); | 162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt); |
177 | |
178 » eastmtlist(fun, stmt->ninit); | |
179 | 163 |
180 switch(stmt->op) { | 164 switch(stmt->op) { |
181 default: | |
182 dump("eastmt", stmt); | |
183 fatal("eastmt %O", stmt->op); | |
184 break; | |
185 | |
186 case ODCL: | 165 case ODCL: |
187 case ODCLFIELD: | 166 case ODCLFIELD: |
188 » » // a declaration ties the node to the current function | 167 » » // a declaration ties the node to the current |
189 » » eaexpr(fun, stmt->left); | 168 » » // function, but we already have that edge in |
190 » » break; | 169 » » // curfn->dcl and will follow it explicitly in |
191 | 170 » » // escflood to avoid storing redundant information |
192 » case ODCLCONST: | 171 » » // What does have to happen here is note if the name |
193 » case ODCLTYPE: | 172 » » // is declared inside a looping scope. |
194 » case OBREAK: | 173 » » stmt->left->escloopdepth = loopdepth; |
195 » case OCONTINUE: | 174 » » break; |
196 » case OXFALL: | 175 |
197 » case OGOTO: | 176 » case OLABEL: // TODO: new loop/scope only if there are backjumps to it. |
198 » case OLABEL: | 177 » » loopdepth++; |
199 » case OEMPTY: | 178 » » break; |
200 » case ORECOVER: | 179 |
201 » » // all harmless | 180 » case OBLOCK: |
202 » » break; | 181 » » escstmtlist(stmt->list); |
203 | |
204 » case OBLOCK: | |
205 » » eastmtlist(fun, stmt->list); | |
206 break; | 182 break; |
207 | 183 |
208 case OFOR: | 184 case OFOR: |
209 if(stmt->ntest != N) { | 185 if(stmt->ntest != N) { |
210 » » » eastmtlist(fun, stmt->ntest->ninit); | 186 » » » escstmtlist(stmt->ntest->ninit); |
211 » » » eaexpr(N, stmt->ntest); | 187 » » » escexpr(N, stmt->ntest); |
212 » » } | 188 » » } |
213 » » eastmt(N, stmt->nincr); | 189 » » escstmt(stmt->nincr); |
214 » » eastmtlist(fun, stmt->nbody); | 190 » » loopdepth++; |
215 » » break; | 191 » » escstmtlist(stmt->nbody); |
216 | 192 » » loopdepth--; |
217 » case ORANGE:» » // for <list> = range <right> { <nbody> } | 193 » » break; |
| 194 |
| 195 » case ORANGE:» » // for» <list> = range <right> { <nbody> } |
218 switch(stmt->type->etype) { | 196 switch(stmt->type->etype) { |
219 » » case TARRAY:» // i, v = range sliceorarrayorstring | 197 » » case TSTRING:» // never flows |
220 » » case TSTRING: | 198 » » » escexpr(stmt->list->n, N); |
221 if(stmt->list->next) | 199 if(stmt->list->next) |
222 » » » » eaexpr(stmt->list->next->n, stmt->right); | 200 » » » » escexpr(stmt->list->next->n, N); |
223 » » » break;» » » | 201 » » » escexpr(N, stmt->right); |
224 » » case TMAP:» // k, v = range map | 202 » » » break; |
225 » » » eaexpr(stmt->list->n, stmt->right); | 203 » » case TARRAY:» // i, v = range sliceorarray |
| 204 » » » escexpr(stmt->list->n, N); |
226 if(stmt->list->next) | 205 if(stmt->list->next) |
227 » » » » eaexpr(stmt->list->next->n, stmt->right); | 206 » » » » escexpr(stmt->list->next->n, stmt->right); |
| 207 » » » break; |
| 208 » » case TMAP:» // k [, v] = range map |
| 209 » » » escexpr(stmt->list->n, stmt->right); |
| 210 » » » if(stmt->list->next) |
| 211 » » » » escexpr(stmt->list->next->n, stmt->right); |
228 break; | 212 break; |
229 case TCHAN: // v = range chan | 213 case TCHAN: // v = range chan |
230 » » » eaexpr(stmt->list->n, stmt->right); | 214 » » » escexpr(stmt->list->n, stmt->right); |
231 break; | 215 break; |
232 } | 216 } |
233 » » eastmtlist(fun, stmt->nbody); | 217 » » loopdepth++; |
| 218 » » escstmtlist(stmt->nbody); |
| 219 » » loopdepth--; |
234 break; | 220 break; |
235 | 221 |
236 case OIF: | 222 case OIF: |
237 » » eaexpr(N, stmt->ntest); | 223 » » escexpr(N, stmt->ntest); |
238 » » eastmtlist(fun, stmt->nbody); | 224 » » escstmtlist(stmt->nbody); |
239 » » eastmtlist(fun, stmt->nelse); | 225 » » escstmtlist(stmt->nelse); |
240 break; | 226 break; |
241 | 227 |
242 case OSELECT: | 228 case OSELECT: |
243 for(ll=stmt->list; ll; ll=ll->next) { // cases | 229 for(ll=stmt->list; ll; ll=ll->next) { // cases |
244 » » » eastmt(fun, ll->n->left); | 230 » » » escstmt(ll->n->left); |
245 » » » eastmtlist(fun, ll->n->nbody); | 231 » » » escstmtlist(ll->n->nbody); |
246 » » } | 232 » » } |
247 » » break; | 233 » » break; |
248 | 234 |
249 » case OSELRECV2: | 235 » case OSELRECV2:» // v, ok := <-ch ntest:ok |
250 » » eaexpr(N, stmt->ntest); | 236 » » escexpr(N, stmt->ntest); |
251 // fallthrough | 237 // fallthrough |
252 » case OSELRECV: | 238 » case OSELRECV:» // v := <-ch» left: v right->op = ORECV |
253 » » eaexpr(stmt->left, stmt->right); | 239 » » escexpr(N, stmt->left); |
| 240 » » escexpr(stmt->left, stmt->right); |
254 break; | 241 break; |
255 | 242 |
256 case OSWITCH: | 243 case OSWITCH: |
257 if(stmt->ntest && stmt->ntest->op == OTYPESW) { | 244 if(stmt->ntest && stmt->ntest->op == OTYPESW) { |
258 » » » eaexpr(stmt->ntest->left, stmt->ntest->right); | 245 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases |
259 » » » for(ll=stmt->list; ll; ll=ll->next) | 246 » » » » // ntest->right is the argument of the .(type), |
260 » » » » eastmtlist(fun, ll->n->nbody); | 247 » » » » // ll->n->nname is the variable per case |
| 248 » » » » escexpr(ll->n->nname, stmt->ntest->right); |
| 249 » » » » escstmtlist(ll->n->nbody); |
| 250 » » » } |
261 } else { | 251 } else { |
262 » » » eaexpr(fun, stmt->ntest); | 252 » » » escexpr(N, stmt->ntest); |
263 for(ll=stmt->list; ll; ll=ll->next) { // cases | 253 for(ll=stmt->list; ll; ll=ll->next) { // cases |
264 » » » » for (lr=ll->n->list; lr; lr=lr->next) { | 254 » » » » for(lr=ll->n->list; lr; lr=lr->next) |
265 » » » » » eaexpr(fun, lr->n); | 255 » » » » » escexpr(N, lr->n); |
266 » » » » } | 256 » » » » escstmtlist(ll->n->nbody); |
267 » » » » eastmtlist(fun, ll->n->nbody); | |
268 } | 257 } |
269 } | 258 } |
270 break; | 259 break; |
271 | 260 |
272 » case OAS: | 261 » case OAS: |
273 case OASOP: | 262 case OASOP: |
274 » » eaexpr(stmt->left, stmt->right); | 263 » » escexpr(stmt->left, stmt->right); |
275 » » break; | 264 » » break; |
276 | 265 |
277 » // escape analysis happens after typecheck, so the | 266 » » // escape analysis happens after typecheck, so the |
278 » // OAS2xxx have already been substituted. | 267 » » // OAS2xxx have already been substituted. |
279 case OAS2: // x,y = a,b | 268 case OAS2: // x,y = a,b |
280 case OAS2FUNC: // x,y,z = f() | |
281 cl = count(stmt->list); | 269 cl = count(stmt->list); |
282 cr = count(stmt->rlist); | 270 cr = count(stmt->rlist); |
283 » » if (cl > 1 && cr == 1) { | 271 » » if(cl > 1 && cr == 1) { |
284 for(ll=stmt->list; ll; ll=ll->next) | 272 for(ll=stmt->list; ll; ll=ll->next) |
285 » » » » eaexpr(ll->n, stmt->rlist->n); | 273 » » » » escexpr(ll->n, stmt->rlist->n); |
286 » » } else { | 274 » » } else { |
287 if(cl != cr) | 275 if(cl != cr) |
288 » » » » fatal("eastmt: bad OAS2: %N", stmt); | 276 » » » » fatal("escstmt: bad OAS2: %N", stmt); |
289 for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l
r->next) | 277 for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l
r->next) |
290 » » » » eaexpr(ll->n, lr->n); | 278 » » » » escexpr(ll->n, lr->n); |
291 » » } | 279 » » } |
292 » » break; | 280 » » break; |
293 » » | 281 |
294 case OAS2RECV: // v, ok = <-ch | 282 case OAS2RECV: // v, ok = <-ch |
295 case OAS2MAPR: // v, ok = m[k] | 283 case OAS2MAPR: // v, ok = m[k] |
296 case OAS2DOTTYPE: // v, ok = x.(type) | 284 case OAS2DOTTYPE: // v, ok = x.(type) |
297 » case OAS2MAPW: » // m[k] = x, ok | 285 » » escexpr(stmt->list->n, stmt->rlist->n); |
298 » » eaexpr(stmt->list->n, stmt->rlist->n); | 286 » » escexpr(stmt->list->next->n, N); |
| 287 » » break; |
| 288 |
| 289 » case OAS2MAPW:» » // m[k] = x, ok.. stmt->list->n is the INDEXMAP,
k is handled in escexpr(dst...) |
| 290 » » escexpr(stmt->list->n, stmt->rlist->n); |
| 291 » » escexpr(N, stmt->rlist->next->n); |
299 break; | 292 break; |
300 | 293 |
301 case ORECV: // unary <-ch as statement | 294 case ORECV: // unary <-ch as statement |
302 » » eaexpr(N, stmt->left); | 295 » » escexpr(N, stmt->left); |
303 break; | 296 break; |
304 | 297 |
305 case OSEND: // ch <- x | 298 case OSEND: // ch <- x |
306 » » eaexpr(stmt->left, stmt->right); | 299 » » escexpr(&theSink, stmt->right);» // for now. TODO escexpr(stmt->
left, stmt->right); |
307 » » break; | 300 » » break; |
308 | 301 |
309 » case OCOPY:» // second arguments leaks to the first | 302 » case OCOPY:» // todo: treat as *dst=*src instead of as dst=src |
310 » » eaexpr(stmt->left, stmt->right); | 303 » » escexpr(stmt->left, stmt->right); |
311 » » break; | 304 » » break; |
312 | 305 |
313 » case OCALL:» »······· | 306 » case OAS2FUNC:» // x,y,z = f() |
314 » » fatal("eastmt naked OCALL:%N\n", stmt); | 307 » » for(ll = stmt->list; ll; ll=ll->next) |
| 308 » » » escexpr(ll->n, N); |
| 309 » » escexpr(N, stmt->rlist->n); |
| 310 » » break; |
| 311 |
315 case OCALLINTER: | 312 case OCALLINTER: |
316 case OCALLFUNC: | 313 case OCALLFUNC: |
317 case OCALLMETH: | 314 case OCALLMETH: |
318 » » stmt->trecur = 0; | 315 » » escexpr(N, stmt); |
319 » » eaexpr(N, stmt); | 316 » » break; |
320 » » break; | 317 |
321 | 318 » case OPROC: |
322 case ODEFER: | 319 case ODEFER: |
323 » » eastmt(fun, stmt->left); | 320 » » // stmt->left is a (pseud)ocall, stmt->left->left is |
| 321 » » // the function being called. if this defer is at |
| 322 » » // loopdepth >1, everything leaks. TODO this is |
| 323 » » // overly conservative, it's enough if it leaks to a |
| 324 » » // fake node at the function's top level |
| 325 » » dst = &theSink; |
| 326 » » if (stmt->op == ODEFER && loopdepth <= 1) |
| 327 » » » dst = nil; |
| 328 » » escexpr(dst, stmt->left->left); |
| 329 » » for(ll=stmt->left->list; ll; ll=ll->next) |
| 330 » » » escexpr(dst, ll->n); |
324 break; | 331 break; |
325 | 332 |
326 case ORETURN: | 333 case ORETURN: |
327 » » for (ll=stmt->list; ll; ll=ll->next) | 334 » » for(ll=stmt->list; ll; ll=ll->next) |
328 » » » eaexpr(&theSink, ll->n); | 335 » » » escexpr(&theSink, ll->n); |
329 » » break; | |
330 | |
331 » case OPROC: | |
332 » » // stmt->left is a (pseud)ocall, consider all arguments leaking | |
333 » » // stmt->left->left is the function being called. if it's a | |
334 » » // method call x.foo, x leaks TODO but other things not.. | |
335 » » eaexpr(&theSink, stmt->left->left); | |
336 » » for (ll=stmt->left->list; ll; ll=ll->next) | |
337 » » » eaexpr(&theSink, ll->n); | |
338 break; | 336 break; |
339 | 337 |
340 case OCLOSE: | 338 case OCLOSE: |
341 case OPRINT: | 339 case OPRINT: |
342 case OPRINTN: | 340 case OPRINTN: |
343 » » eaexpr(N, stmt->left); | 341 » » escexpr(N, stmt->left); |
| 342 » » for(ll=stmt->list; ll; ll=ll->next) |
| 343 » » » escexpr(N, ll->n); |
344 break; | 344 break; |
345 | 345 |
346 case OPANIC: | 346 case OPANIC: |
347 // Argument could leak through recover. | 347 // Argument could leak through recover. |
348 » » eaexpr(&theSink, stmt->left); | 348 » » escexpr(&theSink, stmt->left); |
349 » » break; | 349 » » break; |
350 » } | 350 » } |
351 | 351 |
352 » stmt->trecur = 0; | 352 » lineno = lno; |
353 } | 353 } |
354 | 354 |
355 static void | 355 // Assert that expr somehow gets assigned to dst, if non nil. for |
356 flows(Node* dst, Node* src) | 356 // dst==nil, any name node expr still must be marked as being |
357 { | 357 // evaluated in curfn.» For expr==nil, dst must still be examined for |
358 » if (dst == nil || src == nil) | 358 // evaluations inside it (e.g *f(x) = y) |
359 » » return; | 359 static void |
360 » dst->asrc = list(dst->asrc, src); | 360 escexpr(Node *dst, Node *expr) |
361 //» src->adst = list(src->adst, dst); | 361 { |
362 | 362 » int lno; |
363 » if (debug['s']>1) { | |
364 » if (dst != &theSink) | |
365 » » print("%L %S::%N <- %N", lineno, curfn ? curfn->nname->sym : S,
dst, src); | |
366 » else | |
367 » » print("%L %S:: leaks %N", lineno, curfn ? curfn->nname->sym :S,
src); | |
368 | |
369 » if (src->op == OADDR) print(" <= %#N", src->left); | |
370 » print("\n"); | |
371 » } | |
372 } | |
373 | |
374 static void | |
375 eaexpr(Node *dst, Node *expr) | |
376 { | |
377 NodeList *ll; | 363 NodeList *ll; |
378 | 364 |
379 » if(expr == N || expr == dst) | 365 » if(isblank(dst)) dst = N; |
380 » » return; | 366 |
381 | 367 » // the lhs of an assignment needs recursive analysis too |
382 » setlineno(expr); | 368 » // these are the only interesting cases |
383 | 369 » // todo:check channel case |
384 » if (expr->trecur) | 370 » if(dst) { |
385 » » return; | 371 » » setlineno(dst); |
386 » expr->trecur = 1; | 372 |
387 | |
388 » if (expr->typecheck == 0 && expr->op != ONONAME) { // TODO where do
these come from | |
389 » » dump("eaexpr missing typecheck", expr); | |
390 » » fatal("Missing typecheck."); | |
391 » } | |
392 | |
393 » if (debug['s'] > 2) | |
394 » » print("%L %S:: ... examining %N <= %N\n", lineno, curfn ? curfn-
>nname->sym : S, dst, expr); | |
395 | |
396 | |
397 » // here: turn dests x[k] into x, etc. see part-of in top comment | |
398 | |
399 //» if (dst && dst != &theSink && !dst->type) dump("typeless dst", dst); | |
400 »······· | |
401 » if (dst) { | |
402 switch(dst->op) { | 373 switch(dst->op) { |
403 » » case ONAME: | 374 » » case OINDEX: |
404 » » » if (dst->class == PEXTERN) | 375 » » case OSLICE: |
405 » » » » dst = &theSink; | 376 » » » escexpr(N, dst->right); |
406 » » » break; | 377 |
407 | 378 » » » // slice: "dst[x] = src" is like *(underlying array)[x
] = src |
408 » » // aliasing happens here | 379 » » » // TODO maybe this never occurs b/c of OSLICEARR and it'
s inserted OADDR |
| 380 » » » if(!isfixedarray(dst->left->type)) |
| 381 » » » » goto doref; |
| 382 |
| 383 » » » // fallthrough;» treat "dst[x] = src" as "dst = src" |
| 384 » » case ODOT:» // treat "dst.x = src" as "dst = src" |
| 385 » » » escexpr(dst->left, expr); |
| 386 » » » return; |
| 387 |
| 388 » » case OINDEXMAP: |
| 389 » » » escexpr(&theSink, dst->right);» // map key is put in map |
| 390 » » » // fallthrough |
409 case OIND: | 391 case OIND: |
410 case ODOTPTR: | 392 case ODOTPTR: |
411 » » » dst = &theSink; | 393 » » case OSLICEARR:» // ->left is the OADDR of the array |
412 » » » break; | 394 » » doref: |
413 | 395 » » » escexpr(N, dst->left); |
414 » » case OINDEXMAP: | 396 » » » // assignment to dereferences: for now we lose track |
415 » » » dst = dst->left; | 397 » » » escexpr(&theSink, expr); |
416 » » » break; | 398 » » » return; |
417 | 399 » » } |
418 » » case OINDEX: | 400 |
419 » » » if(isfixedarray(dst->left->type)) | 401 » } |
420 » » » » dst = dst->left; | 402 |
421 » » » else | 403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX) |
422 » » » » dst = &theSink; | 404 » » return; |
423 » » } | 405 |
424 | 406 » if(expr->typecheck == 0 && expr->op != OKEY) { |
425 » » // i am an idiot. channels and maps are reference types, meanin
g | 407 » » dump("escexpr missing typecheck", expr); |
426 » » // they can alias all over the place, treat like OIND | 408 » » fatal("Missing typecheck."); |
427 » » if (dst->type) { | 409 » } |
428 » » » switch(dst->type->etype) { | 410 |
429 » » » case TCHAN: | 411 » lno = setlineno(expr); |
430 » » » case TMAP: | 412 » pdepth++; |
431 » » » » dst = &theSink; | 413 |
432 » » » » break; | 414 » if(debug['m'] > 1) |
433 » » » case TARRAY: | 415 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth, |
434 » » » » if(!isfixedarray(dst->type)) | 416 » » (curfn && curfn->nname) ? curfn->nname->sym : S, dst, |
435 » » » » » dst = &theSink; | 417 » » 2*pdepth, ".\t.\t.\t.\t.\t", expr); |
436 » » » » break; | 418 |
437 » » » } | |
438 » » } | |
439 » } | |
440 | 419 |
441 switch(expr->op) { | 420 switch(expr->op) { |
442 » default: | 421 » case OADDR:» // dst = &x |
443 » » dump("eaexpr", expr); | 422 » case OIND:» // dst = *x |
444 » » fatal("eaexpr %O", expr->op); | 423 » case ODOTPTR:» // dst = (*x).f |
445 » » break; | 424 » » // restart the recursion at x to figure out where it came from |
446 | 425 » » escexpr(expr->left, expr->left); |
447 » case OADDR: | 426 » » // fallthrough |
448 » » flows(dst, expr); // dst = &x | |
449 » » // to the right of & there can be only an array-,map- or struct-
literal, a name (not of a func),· | |
450 » » // an index or member selection operation &x[y] (OINDEX), &x.y
(ODOT).· | |
451 » » // or an indirection: &*x (OIND), &(*x).y (ODOTPTR, written as &
x.y , but x of pointer type | |
452 » » // in the last two cases, the flow is actually not interrupted, | |
453 » » // which in turn is only interesting if it is a pointer value. | |
454 » » if (expr->left->op == OIND || expr->left->op == ODOTPTR) | |
455 » » » eaexpr(dst, expr->left);···· | |
456 » » else | |
457 » » » eaexpr(N, expr->left);···· | |
458 » » break; | |
459 | |
460 » case OIND:» » » // dst = *y -> who cares, | |
461 » » // even if dst = * .... &x, that doesnt mean &x flows to dst, ju
st x | |
462 » » // if this OIND is to the right of an OADDR, things are differen
t, but that's handled above. | |
463 » » eaexpr(N, expr->left); | |
464 » » break; | |
465 | |
466 case ONAME: | 427 case ONAME: |
467 case ONONAME: // not typechecked yet? | |
468 case OPARAM: | 428 case OPARAM: |
469 » » flows(dst, expr); | 429 » » // loopdepth was set in the defining statement or function heade
r |
| 430 » » escflows(dst, expr); |
470 break; | 431 break; |
471 | 432 |
472 case OARRAYLIT: | 433 case OARRAYLIT: |
473 case OSTRUCTLIT: | 434 case OSTRUCTLIT: |
474 » » flows(dst, expr); | 435 » case OMAPLIT: |
475 » » // ll->n is key, left is fieldname or constant index | 436 » » expr->escloopdepth = loopdepth; |
476 » » for (ll=expr->list; ll; ll=ll->next) | 437 » » escflows(dst, expr); |
477 » » » eaexpr(expr, ll->n->right); | 438 » » for(ll=expr->list; ll; ll=ll->next) { |
478 » » break; | 439 » » » escexpr(expr, ll->n->left); |
479 » case OMAPLIT:» » »······· | 440 » » » escexpr(expr, ll->n->right); |
480 » » flows(dst, expr); | 441 » » } |
481 » » for (ll=expr->list; ll; ll=ll->next) { // ll->n is key,· | 442 » » break; |
482 » » » eaexpr(expr, ll->n->left); | 443 |
483 » » » eaexpr(expr, ll->n->right); | 444 » case OMAKECHAN: |
484 » » } | 445 » case OMAKEMAP: |
485 » » break; | 446 » case OMAKESLICE: |
486 | 447 » case ONEW: |
487 » case OCOMPLIT: | 448 » » expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. |
488 » case OLITERAL: | 449 » » expr->escloopdepth = loopdepth; |
489 » case ORECOVER: | 450 » » escflows(dst, expr); |
490 » » break; | 451 » » // first arg is type, all others need checking |
491 | 452 » » for(ll=expr->list->next; ll; ll=ll->next) |
492 » // harmless unary | 453 » » » escexpr(N, ll->n); |
493 » case ONOT:» case OCOM: | 454 » » break; |
494 » case OCAP:» case OLEN: | 455 |
495 » case OREAL:» case OIMAG: | 456 » case OCLOSURE: |
496 » case OARRAYBYTESTR: | 457 » » expr->curfn = curfn; // should have been done in parse, but pat
ch it up here. |
497 » case OARRAYRUNESTR: | 458 » » expr->escloopdepth = loopdepth; |
498 » case OSTRARRAYBYTE: | 459 » » escflows(dst, expr); |
499 » case OSTRARRAYRUNE: | 460 » » escfunc(expr); |
500 » case ORUNESTR: | 461 » » break; |
501 » » eaexpr(N, expr->left); | 462 |
502 » » break; | 463 » // end of the leaf cases. no calls to escflows() in the cases below. |
503 | 464 |
504 » // harmless binary | 465 |
505 » case OADD:» case OAND:» case OANDAND: | 466 » case OCONV:» // unaries that pass the value through |
506 » case OANDNOT:» case ODIV:» case OEQ: | |
507 » case OGE:» case OGT:» case OLE: | |
508 » case OLT:» case OLSH:» case ORSH: | |
509 » case OMOD:» case OMUL:» case ONE: | |
510 » case OOR:» case OOROR:» case OSUB: | |
511 » case OXOR:» case OADDSTR:» case OASOP: | |
512 » case OCMPIFACE:»case OCMPSTR: » case OCOMPLEX: | |
513 » case OPLUS: case OMINUS: | |
514 » » eaexpr(N, expr->left); | |
515 » » eaexpr(N, expr->right); | |
516 » » break; | |
517 | |
518 » // unaries that pass the value through | |
519 » case OCONV: | |
520 case OCONVIFACE: | 467 case OCONVIFACE: |
521 case OCONVNOP: | 468 case OCONVNOP: |
522 case ODOTTYPE: | 469 case ODOTTYPE: |
523 case ODOTTYPE2: | 470 case ODOTTYPE2: |
524 » case ORECV: // leaks the whole channel | 471 » case ORECV:» // leaks the whole channel |
525 » » eaexpr(dst, expr->left); | 472 » case ODOTMETH:» // expr->right is just the field or method name |
| 473 » case ODOTINTER: |
| 474 » case ODOT: |
| 475 » » escexpr(dst, expr->left); |
526 break; | 476 break; |
527 | 477 |
528 case OCOPY: | 478 case OCOPY: |
529 // left leaks to right, but the return value is harmless | 479 // left leaks to right, but the return value is harmless |
530 » » eaexpr(expr->left, expr->right); | 480 » » // TODO: treat as *dst = *src, rather than as dst = src |
| 481 » » escexpr(expr->left, expr->right); |
531 break; | 482 break; |
532 | 483 |
533 case OAPPEND: | 484 case OAPPEND: |
534 » » eaexpr(dst, expr->list->n); | 485 » » // See TODO for OCOPY |
535 » » for (ll=expr->list->next; ll; ll=ll->next) | 486 » » escexpr(dst, expr->list->n); |
536 » » » eaexpr(expr->list->n, ll->n); | 487 » » for(ll=expr->list->next; ll; ll=ll->next) |
537 » » break; | 488 » » » escexpr(expr->list->n, ll->n); |
538 | 489 » » break; |
539 » // all arguments leak to the parameter nodes. | 490 |
540 » // TODO ...when we figure out how to get our hands at them. | |
541 case OCALLMETH: | 491 case OCALLMETH: |
| 492 case OCALLFUNC: |
542 case OCALLINTER: | 493 case OCALLINTER: |
543 //» » eaexpr(&theSink, expr->left->left); // DOTMETH->this or DOTINTER
->this | 494 » » // Moved to separate function to isolate the hair. |
544 » » // fallthrough | 495 » » escexprcall(dst, expr); |
| 496 » » break; |
| 497 |
| 498 » case OSLICEARR:» // like an implicit OIND to the underlying buffer, but
typecheck has inserted an OADDR |
| 499 » case OSLICESTR: |
| 500 » case OSLICE: |
| 501 » case OINDEX: |
| 502 » case OINDEXMAP: |
| 503 » » // the big thing flows, the keys just need checking |
| 504 » » escexpr(dst, expr->left); |
| 505 » » escexpr(N, expr->right); // expr->right is the OKEY |
| 506 » » break; |
| 507 |
| 508 » default: // all other harmless leaf, unary or binary cases end up here |
| 509 » » escexpr(N, expr->left); |
| 510 » » escexpr(N, expr->right); |
| 511 » » break; |
| 512 » } |
| 513 |
| 514 » pdepth--; |
| 515 » lineno = lno; |
| 516 } |
| 517 |
| 518 |
| 519 // This is a bit messier than fortunate, pulled out of escexpr's big |
| 520 // switch for clarity.» We either have the paramnodes, which may be |
| 521 // connected to other things throug flows or we have the parameter type |
| 522 // nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly |
| 523 // different for methods vs plain functions and for imported vs |
| 524 // this-package |
| 525 static void |
| 526 escexprcall(Node *dst, Node *expr) |
| 527 { |
| 528 » NodeList *ll, *lr; |
| 529 » Node *fn; |
| 530 » Type *t, *fntype, *thisarg, *inargs; |
| 531 |
| 532 » fn = nil; |
| 533 » fntype = nil; |
| 534 |
| 535 » switch(expr->op) { |
545 case OCALLFUNC: | 536 case OCALLFUNC: |
546 » » eaexpr(dst, expr->left); | 537 » » fn = expr->left; |
547 » » for (ll=expr->list; ll; ll=ll->next) | 538 » » escexpr(N, fn); |
548 » » » eaexpr(&theSink , ll->n); | 539 » » fntype = fn->type; |
549 » » break; | 540 » » break; |
550 | 541 |
551 » case ODOTMETH: | 542 » case OCALLMETH: |
552 » case ODOTINTER: | 543 » » fn = expr->left->right;» // ODOTxx name |
553 » case ODOT: | 544 » » fn = fn->sym->def;» // resolve to definition if we have it |
| 545 » » if(fn) |
| 546 » » » fntype = fn->type; |
| 547 » » else |
| 548 » » » fntype = expr->left->type; |
| 549 » » break; |
| 550 |
| 551 » case OCALLINTER: |
| 552 » » break; |
| 553 |
| 554 » default: |
| 555 » » fatal("escexprcall called with non-call expression"); |
| 556 » } |
| 557 |
| 558 » if(fn && fn->ntype) { |
| 559 » » if(debug['m'] > 2) |
| 560 » » » print("escexprcall: have param nodes: %N\n", fn->ntype); |
| 561 |
| 562 » » if(expr->op == OCALLMETH) { |
| 563 » » » if(debug['m'] > 2) |
| 564 » » » » print("escexprcall: this: %N\n",fn->ntype->left-
>left); |
| 565 » » » escexpr(fn->ntype->left->left, expr->left->left); |
| 566 » » } |
| 567 |
| 568 » » // lr->n is the dclfield, ->left is the ONAME param node |
| 569 » » for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) { |
| 570 » » » if(debug['m'] > 2) |
| 571 » » » » print("escexprcall: field param: %N\n", lr->n->l
eft); |
| 572 » » » if (lr->n->left) |
| 573 » » » » escexpr(lr->n->left, ll->n); |
| 574 » » » else |
| 575 » » » » escexpr(&theSink, ll->n); |
| 576 » » » if(lr->n->left && !lr->n->left->isddd) |
| 577 » » » » lr=lr->next; |
| 578 » » } |
| 579 » » return; |
| 580 » } |
| 581 |
| 582 » if(fntype) { |
| 583 » » if(debug['m'] > 2) |
| 584 » » » print("escexprcall: have param types: %T\n", fntype); |
| 585 |
| 586 » » if(expr->op == OCALLMETH) { |
| 587 » » » thisarg = getthisx(fntype); |
| 588 » » » t = thisarg->type; |
| 589 » » » if(debug['m'] > 2) |
| 590 » » » » print("escexprcall: this: %T\n", t); |
| 591 » » » if(!t->note || strcmp(t->note->s, safetag->s) != 0) |
| 592 » » » » escexpr(&theSink, expr->left->left); |
| 593 » » » else |
| 594 » » » » escexpr(N, expr->left->left); |
| 595 » » } |
| 596 |
| 597 » » inargs = getinargx(fntype); |
| 598 » » for(ll=expr->list, t=inargs->type; ll; ll=ll->next) { |
| 599 » » » if(debug['m'] > 2) |
| 600 » » » » print("escexprcall: field type: %T\n", t); |
| 601 » » » if(!t->note || strcmp(t->note->s, safetag->s)) |
| 602 » » » » escexpr(&theSink, ll->n); |
| 603 » » » else |
| 604 » » » » escexpr(N, ll->n); |
| 605 » » » if(t->down) |
| 606 » » » » t=t->down; |
| 607 » » } |
| 608 |
| 609 » » return; |
| 610 » } |
| 611 |
| 612 » // fallthrough if we don't have enough information: |
| 613 » // can only assume all parameters are unsafe |
| 614 » // OCALLINTER always ends up here |
| 615 |
| 616 » if(debug['m']>1 && expr->op != OCALLINTER) { |
| 617 » » // dump("escexprcall", expr); |
| 618 » » print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn)
; |
| 619 » } |
| 620 |
| 621 » escexpr(&theSink, expr->left->left); // the this argument |
| 622 » for(ll=expr->list; ll; ll=ll->next) |
| 623 » » escexpr(&theSink, ll->n); |
| 624 } |
| 625 |
| 626 // Store the link src->dst in dst, throwing out some quick wins. |
| 627 static void |
| 628 escflows(Node* dst, Node* src) |
| 629 { |
| 630 » if(dst == nil || src == nil || dst == src) |
| 631 » » return; |
| 632 |
| 633 » // Don't bother building a graph for scalars. |
| 634 » if (src->type && !haspointers(src->type)) |
| 635 » » return; |
| 636 |
| 637 » if(debug['m']>2) |
| 638 » » print("%L::flows:: %hN <- %hN\n", lineno, dst, src); |
| 639 |
| 640 » // Assignments to global variables get lumped into theSink. |
| 641 » if (dst->op == ONAME && dst->class == PEXTERN) |
| 642 » » dst = &theSink; |
| 643 |
| 644 » if (dst->escflowsrc == nil) { |
| 645 » » dsts = list(dsts, dst); |
| 646 » » dstcount++; |
| 647 » } |
| 648 » edgecount++; |
| 649 |
| 650 » dst->escflowsrc = list(dst->escflowsrc, src); |
| 651 } |
| 652 |
| 653 // Whenever we hit a reference node, the level goes up by one, and whenever |
| 654 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 |
| 655 // finding an OADDR just means we're following the upstream of a dereference, |
| 656 // so this address doesn't leak (yet). |
| 657 // If level == 0, it means the /value/ of this node can reach the root of this f
lood. |
| 658 // so if this node is an OADDR, it's argument should be marked as escaping iff |
| 659 // it's currfn/loopdepth are different from the flood's root. |
| 660 // Once an object has been moved to the heap, all of it's upstream should be con
sidered |
| 661 // escaping to the global scope. |
| 662 static void |
| 663 escflood(Node *dst) |
| 664 { |
| 665 » NodeList *l; |
| 666 |
| 667 » switch(dst->op) { |
| 668 » case ONAME: |
| 669 » case OCLOSURE: |
| 670 » » break; |
| 671 » default: |
| 672 » » return; |
| 673 » } |
| 674 |
| 675 » if(debug['m']>1) |
| 676 » » print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst, |
| 677 » » (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym
: S, |
| 678 » » dst->escloopdepth); |
| 679 |
| 680 » for (l = dst->escflowsrc; l; l=l->next) { |
| 681 » » floodgen++; |
| 682 » » escwalk(0, dst, l->n); |
| 683 » } |
| 684 } |
| 685 |
| 686 static void |
| 687 escwalk(int level, Node *dst, Node *src) |
| 688 { |
| 689 » NodeList* ll; |
| 690 » int leaks; |
| 691 |
| 692 » if (src->escfloodgen == floodgen) |
| 693 » » return; |
| 694 » src->escfloodgen = floodgen; |
| 695 |
| 696 » if(debug['m']>1) |
| 697 » » print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", |
| 698 » » level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, |
| 699 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym
: S, src->escloopdepth); |
| 700 |
| 701 » pdepth++; |
| 702 |
| 703 » leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth); |
| 704 |
| 705 » switch(src->op) { |
| 706 » case ONAME: |
| 707 » » if (src->class == PPARAM && leaks && src->esc == EscNone) { |
| 708 » » » src->esc = EscScope; |
| 709 » » » if(debug['m']) |
| 710 » » » » print("%L:leaking param: %hN\n", src->lineno, sr
c); |
| 711 » » } |
| 712 » » break; |
| 713 |
| 714 » case OADDR: |
| 715 » » if (leaks) |
| 716 » » » addrescapes(src->left); |
| 717 » » escwalk(level-1, dst, src->left); |
| 718 » » break; |
| 719 |
| 720 » case OINDEX: |
| 721 » » if(isfixedarray(src->type)) |
| 722 » » » break; |
| 723 » case OSLICE: |
554 case ODOTPTR: | 724 case ODOTPTR: |
555 // pretend all of the struct leaked | |
556 // expr->right is just an (untypechecked) ONAME | |
557 eaexpr(dst, expr->left); | |
558 break; | |
559 | |
560 case OSLICE: // the big thing leaks, the keys just need checking | |
561 case OSLICEARR: | |
562 case OSLICESTR: | |
563 eaexpr(dst, expr->left); | |
564 eaexpr(N, expr->right->left); // the OKEY | |
565 eaexpr(N, expr->right->right); | |
566 break; | |
567 | |
568 case OINDEX:··· | |
569 case OINDEXMAP: | 725 case OINDEXMAP: |
570 » » eaexpr(dst, expr->left); | 726 » case OIND: |
571 » » eaexpr(N, expr->right); | 727 » » escwalk(level+1, dst, src->left); |
572 » » break; | 728 » } |
573 | 729 |
574 » case OMAKECHAN: | 730 » for (ll=src->escflowsrc; ll; ll=ll->next) |
575 » case OMAKEMAP: | 731 » » escwalk(level, dst, ll->n); |
576 » case OMAKESLICE: | 732 |
577 » case ONEW:» » // first arg is type, all others need checking | 733 » pdepth--; |
578 » » for (ll=expr->list->next; ll; ll=ll->next) | 734 } |
579 » » » eaexpr(N, ll->n); | 735 |
580 » » flows(dst, expr); | 736 static void |
581 » » break; | 737 esctag(Node *func) |
582 | 738 { |
583 » case OCLOSURE: | 739 » Node *savefn; |
584 » » flows(dst, expr); // the closure flows to the lhs of the assign
ment, or to theSink if we're in a return. | |
585 » » // the cvars will have their address taken by walk. introduce a
fake OADDR node so eawalk can pick that up. | |
586 » » for (ll=expr->cvars; ll; ll=ll->next) | |
587 » » » flows(expr, nod(OADDR, ll->n, N)); | |
588 » » eastmtlist(expr, expr->nbody); // the declarations in the body
flow to the closure | |
589 » » break; | |
590 | |
591 » } | |
592 | |
593 » expr->trecur = 0; | |
594 } | |
595 | |
596 static int depth; | |
597 | |
598 static void eawalk(Node *sink) { | |
599 NodeList *ll; | 740 NodeList *ll; |
600 | 741 |
601 » if (debug['s']>1) | 742 » savefn = curfn; |
602 » print("eawalk %d: %N\n", depth, sink); | 743 » curfn = func; |
603 | 744 |
604 » depth++; | 745 » for(ll=curfn->dcl; ll; ll=ll->next) { |
605 » ll = sink->asrc; | 746 » » if(ll->n->op != ONAME || ll->n->class != PPARAM) |
606 » sink->asrc = nil; | 747 » » » continue; |
607 | 748 |
608 » for (; ll; ll=ll->next) | 749 » » switch (ll->n->esc) { |
609 » » eawalk(ll->n); | 750 » » case EscNone:» // not touched by escflood |
610 | 751 » » » if (haspointers(ll->n->type)) // don't bother tagging fo
r scalars |
611 » if (sink->op == OADDR) { | 752 » » » » ll->n->paramfld->note = safetag; |
612 » » addrescapes(sink->left); | 753 » » case EscHeap:» // touched by escflood, moved to heap |
613 » » eawalk(sink->left); | 754 » » case EscScope:» // touched by escflood, value leaves scope |
614 » } | 755 » » » break; |
615 | 756 » » default: |
616 » depth--; | 757 » » » fatal("messed up escape tagging: %N::%N", curfn, ll->n); |
617 } | 758 » » } |
618 | 759 » } |
619 | 760 |
620 | 761 » curfn = savefn; |
621 | 762 } |
622 | |
623 | |
624 | |
625 #if 0 | |
626 | |
627 » for(tl=tstruct->type; tl; tl=tl->down) { | |
628 » » t = tl->type; | |
629 » » if(tl->isddd) { | |
630 » » » if(isddd) { | |
631 » » » » if(nl == nil) | |
632 » » » » » goto notenough; | |
633 » » » » if(nl->next != nil) | |
634 » » » » » goto toomany; | |
635 » » » » n = nl->n; | |
636 » » » » setlineno(n); | |
637 » » » » if(n->type != T) { | |
638 » » » » » nl->n = assignconv(n, t, desc); | |
639 » » » » » if (call) argused(call, tl, n); | |
640 » » » » } | |
641 » » » » goto out; | |
642 » » » } | |
643 » » » for(; nl; nl=nl->next) { | |
644 » » » » n = nl->n; | |
645 » » » » setlineno(nl->n); | |
646 » » » » if(n->type != T) { | |
647 » » » » » nl->n = assignconv(n, t->type, desc); | |
648 » » » » » if (call) argused(call, tl, n); | |
649 » » » » } | |
650 » » » } | |
651 » » » goto out; | |
652 » » }· | |
653 | |
654 | |
655 // mark that value has been used as the argument arg | |
656 static void argused(Node* func, Type* arg, Node* val) { | |
657 //» if (arg->sym && arg->sym->pkg == localpkg) | |
658 //» » arg->note = strlit("used"); | |
659 //» print("Using %N :: %#T = %N\n", func, arg, val); | |
660 } | |
661 | |
662 | |
663 /* | |
664 * type check function definition | |
665 */ | |
666 static void | |
667 escanalfunc(Node *n) | |
668 { | |
669 //» Type *t, *t1, *rcvr; | |
670 » Type *t, *rcvr; | |
671 | |
672 » typecheck(&n->nname, Erv | Easgn); | |
673 » if((t = n->nname->type) == T) | |
674 » » return; | |
675 » n->type = t; | |
676 | |
677 » print("typecheckfunc %T\n", n->type); | |
678 » for(t1=getinargx(t)->type; t1; t1=t1->down) { | |
679 » » if (isptr[t1->type->etype]) | |
680 » » » t1->note = strlit("leaks"); | |
681 » » print("\t%T\n", t1); | |
682 | |
683 » } | |
684 | |
685 » rcvr = getthisx(t)->type; | |
686 » if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) | |
687 » » addmethod(n->shortname->sym, t, 1); | |
688 } | |
689 | |
690 #endif | |
LEFT | RIGHT |