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