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