Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
rsc
2011/07/01 17:43:35
new code, so 2011
| |
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 of operation: | 5 // The base version before this file existed, active with debug['s'] |
6 // A variable must be moved to the heap (from the stack) if | 6 // == 0, assumes any node that has a reference to it created at some |
7 // it's address escapes the current function scope, meaning | 7 // point, may flow to the global scope except |
8 // - the address is taken (with the unary-& operator OADDR) | 8 // - if its address is dereferenced immediately with only CONVNOPs in |
9 // - AND this pointer value leaks outside the current function scope. | 9 // between the * and the & |
10 //· | 10 // - if it is for a closure variable and the closure executed at the |
11 // A (pointer) *value* leaks if and only if any of the following happens | 11 // place it's defined |
12 // - it is assigned to (part of) a global variable (class PEXTERN) | |
13 // part-of can mean: struct member, array or slice element, | |
14 // sent on channel, map key or value. | |
15 // * in case of doubt, e.g. if the lhs of the assignment is | |
16 // a pointer indirection, we have to assume the rhs leaks. | |
17 // - it is returned from a function (== assigned to an out-parameter) | |
18 // - it is passed to a function as the this- or input parameter, | |
19 // AND this parameter itself leaks from its function scope | |
20 // * in case of doubt, e.g. if the function is not a named one | |
21 // but a func-typed variable, we have to assume it leaks. | |
22 // - it is used in an expression inside a go (OPROC) statement | |
23 // - it is assigned to (part of) a node that itself leaks | |
24 // | 12 // |
25 // Whether parameters leak or not is not different to analyse than any | 13 // Flag -s disables the old codepaths and switches on the code here: |
26 // other variable. | |
27 //· | |
28 // To find out the leak graph for pointer values (and arrays, slices, | |
29 // maps and structs that contain them) escanalfunc walks the AST to | |
30 // emit directed edges of 'leakage' between nodes, known leaks get an | |
31 // edge to a fake node theSink. When the graph is complete, it is | |
32 // traversed starting from theSink and any reachable node that is of | |
33 // type OADDR will have it's argument marked for moving to the heap. | |
34 // | 14 // |
35 // Note that this will cover the previously special case of * (pointer cast)(uns afe.Pointer(& x)): | 15 // First escfunc, escstmt and escexpr recurse over the ast of each |
36 // OADDR x will leak all the way up to the top OREF and stop there, unless someo ne takes the OADDR of that again. | 16 // function to dig out flow(dst,src) edges between any |
37 // if i code it all up right that is. | 17 // pointer-containing nodes and store them in dst->escflowsrc. For |
18 // variables assigned to a variable in an outer scope or used as a | |
19 // return value, they store a flow(theSink, src) edge to a fake node | |
20 // 'the Sink'.» For variables referenced in closures, an edge | |
21 // flow(closure, &var) is recorded and the flow of a closure itself to | |
22 // an outer scope is tracked the same way as other variables. | |
23 // | |
24 // Then escflood walks the graph starting at theSink and tags all | |
25 // variables of it can reach an & node as escaping and all function | |
26 // parameters it can reach as leaking. | |
27 // | |
28 // Watch the variables moved to the heap and parameters tagged as | |
29 // unsafe with -m, more detailed analysis output with -mm | |
30 // | |
38 | 31 |
39 #include "go.h" | 32 #include "go.h" |
40 | 33 |
41 static void eastmtlist(NodeList *l); | 34 static void escfunc(Node *func); |
42 static void eastmt(Node *n); | 35 static void escstmtlist(NodeList *stmts); |
43 static void eaexpr(Node *dst, Node *expr); | 36 static void escstmt(Node *stmt); |
44 | 37 static void escexpr(Node *dst, Node *expr); |
45 // Fake node that all return values, global variables and | 38 static void escexprcall(Node *dst, Node *callexpr); |
46 // goroutine-used variables leak to. | 39 static void escflows(Node* dst, Node* src); |
47 static Node theSink; | 40 static void escflood(Node *dst); |
41 static void escwalk(int level, Node *dst, Node *src); | |
42 static void esctag(Node *func); | |
43 | |
44 // Fake node that all | |
45 // - return values and output variables | |
46 // - parameters on imported functions not marked 'safe' | |
47 // - assignments to global variables | |
48 // flow to. | |
49 static Node» theSink; | |
50 | |
51 static NodeList* dsts;» » // all dst nodes | |
52 static int» loopdepth;» // for detecting nested loop scopes | |
53 static int» pdepth;»» // for debug printing in recursions. | |
54 static int» floodgen;» // loop prevention in flood/walk | |
55 static Strlit*» safetag;» // gets slapped on safe parameters' field types for export | |
56 static int» dstcount, edgecount;» // diagnostic | |
48 | 57 |
49 void | 58 void |
50 escanalfunc(Node *func) | 59 escapes(void) |
rsc
2011/07/01 17:43:35
sort of awkward. i suggest ,s/escanal/esc/ or s//
| |
51 { | 60 { |
61 » NodeList *l; | |
62 | |
63 » theSink.op = ONAME; | |
64 » theSink.class = PEXTERN; | |
65 » theSink.sym = lookup(".sink"); | |
66 » theSink.escloopdepth = -1; | |
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; | |
52 curfn = func; | 98 curfn = func; |
53 » // Walk the AST's to find the graph of src-leaksto-dst. | 99 |
54 » eastmtlist(func->nbody); | 100 » for(ll=curfn->dcl; ll; ll=ll->next) { |
55 » curfn = nil; | 101 » » if(ll->n->op != ONAME) |
56 } | 102 » » » continue; |
57 | 103 » » switch (ll->n->class) { |
58 void | 104 » » case PPARAMOUT: |
59 escanalfinish() | 105 » » » // output parameters flow to the sink |
60 { | 106 » » » escflows(&theSink, ll->n); |
61 » // walk the graph, mark nodes as 'leaking' | 107 » » » ll->n->escloopdepth = loopdepth; |
62 » // and move to heap any OADDR's arguments that do. | 108 » » » break; |
63 } | 109 » » case PPARAM: |
64 | 110 » » » ll->n->esc = EscNone;» // prime for escflood later |
65 | 111 » » » ll->n->escloopdepth = loopdepth; |
66 static int | 112 » » » break; |
67 isinteresting(Type *t) { | 113 » » } |
68 | 114 » } |
69 » if (t == T) | 115 |
70 » » fatal("no type for isinteresting"); | 116 » // walk will take the address of cvar->closure later and assign it to cv ar. |
71 | 117 » // handle that here by linking a fake oaddr node directly to the closure . |
72 » switch(t->etype) { | 118 » for (ll=curfn->cvars; ll; ll=ll->next) { |
73 » case TPTR32: | 119 » » if(ll->n->op == OXXX) // see dcl.c:398 |
74 » case TPTR64: | 120 » » » continue; |
75 » case TINTER: | 121 |
76 » » return 1; | 122 » » n = nod(OADDR, ll->n->closure, N); |
77 » case TARRAY: | 123 » » n->lineno = ll->n->lineno; |
78 » case TCHAN: | 124 » » typecheck(&n, Erv); |
79 » » return isinteresting(t->type); | 125 » » escexpr(curfn, n); |
80 » case TMAP: | 126 » } |
81 » » return isinteresting(t->type) || isinteresting(t->down); | 127 |
82 » case TSTRUCT: | 128 » escstmtlist(curfn->nbody); |
83 » » for(t=t->type; t!=T; t=t->down) { | 129 » curfn = savefn; |
84 » » » if(t->etype != TFIELD) | 130 » loopdepth = saveld; |
85 » » » » fatal("isinteresting: not TFIELD: %lT", t); | 131 } |
86 » » » if (isinteresting(t->type)) | 132 |
87 » » » » return 1; | 133 static void |
88 » » } | 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; | |
144 » NodeList *ll, *lr; | |
145 » Node *dst; | |
146 | |
147 » if(stmt == N) | |
148 » » return; | |
149 | |
150 » lno = setlineno(stmt); | |
151 | |
152 » if(stmt->typecheck == 0 && stmt->op != ODCL) {» // TODO something with OAS2 | |
153 » » dump("escstmt missing typecheck", stmt); | |
154 » » fatal("missing typecheck."); | |
155 » } | |
156 | |
157 » // Common to almost all statements, and nil if n/a. | |
158 » escstmtlist(stmt->ninit); | |
159 | |
160 » if(debug['m'] > 1) | |
161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth, | |
162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt); | |
163 | |
164 » switch(stmt->op) { | |
165 » case ODCL: | |
166 » case ODCLFIELD: | |
167 » » // a declaration ties the node to the current | |
168 » » // function, but we already have that edge in | |
169 » » // curfn->dcl and will follow it explicitly in | |
170 » » // escflood to avoid storing redundant information | |
171 » » // What does have to happen here is note if the name | |
172 » » // is declared inside a looping scope. | |
173 » » stmt->left->escloopdepth = loopdepth; | |
174 » » break; | |
175 | |
176 » case OLABEL: // TODO: new loop/scope only if there are backjumps to it. | |
177 » » loopdepth++; | |
178 » » break; | |
179 | |
180 » case OBLOCK: | |
181 » » escstmtlist(stmt->list); | |
182 » » break; | |
183 | |
184 » case OFOR: | |
185 » » if(stmt->ntest != N) { | |
186 » » » escstmtlist(stmt->ntest->ninit); | |
187 » » » escexpr(N, stmt->ntest); | |
188 » » } | |
189 » » escstmt(stmt->nincr); | |
190 » » loopdepth++; | |
191 » » escstmtlist(stmt->nbody); | |
192 » » loopdepth--; | |
193 » » break; | |
194 | |
195 » case ORANGE:» » // for» <list> = range <right> { <nbody> } | |
196 » » switch(stmt->type->etype) { | |
197 » » case TSTRING:» // never flows | |
198 » » » escexpr(stmt->list->n, N); | |
199 » » » if(stmt->list->next) | |
200 » » » » escexpr(stmt->list->next->n, N); | |
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; | |
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); | |
212 » » » break; | |
213 » » case TCHAN:» // v = range chan | |
214 » » » escexpr(stmt->list->n, stmt->right); | |
215 » » » break; | |
216 » » } | |
217 » » loopdepth++; | |
218 » » escstmtlist(stmt->nbody); | |
219 » » loopdepth--; | |
220 » » break; | |
221 | |
222 » case OIF: | |
223 » » escexpr(N, stmt->ntest); | |
224 » » escstmtlist(stmt->nbody); | |
225 » » escstmtlist(stmt->nelse); | |
226 » » break; | |
227 | |
228 » case OSELECT: | |
229 » » for(ll=stmt->list; ll; ll=ll->next) { // cases | |
230 » » » escstmt(ll->n->left); | |
231 » » » escstmtlist(ll->n->nbody); | |
232 » » } | |
233 » » break; | |
234 | |
235 » case OSELRECV2:» // v, ok := <-ch ntest:ok | |
236 » » escexpr(N, stmt->ntest); | |
89 // fallthrough | 237 // fallthrough |
90 » } | 238 » case OSELRECV:» // v := <-ch» left: v right->op = ORECV |
91 » return 0; | 239 » » escexpr(N, stmt->left); |
92 } | 240 » » escexpr(stmt->left, stmt->right); |
93 | 241 » » break; |
94 | 242 |
95 // Assert that leaking dst will cause expr to leak as well, | 243 » case OSWITCH: |
rsc
2011/07/01 17:43:35
s/expr/n/
| |
96 // and check the parts of expr themselves for leakage. | 244 » » if(stmt->ntest && stmt->ntest->op == OTYPESW) { |
97 // examples: | 245 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases |
98 // dst = src» assign to scalar | 246 » » » » // ntest->right is the argument of the .(type), |
99 // dst[idx] = src» assign to array, slice or map element | 247 » » » » // ll->n->nname is the variable per case |
100 // dst[src] = ... assign to map key | 248 » » » » escexpr(ll->n->nname, stmt->ntest->right); |
101 // dst <- src» send to channel | 249 » » » » escstmtlist(ll->n->nbody); |
102 // dst.fld = src assign to struct field | 250 » » » } |
103 // | 251 » » } else { |
104 static void | 252 » » » escexpr(N, stmt->ntest); |
105 eaexpr(Node *dst, Node *n) | 253 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases |
106 { | 254 » » » » for(lr=ll->n->list; lr; lr=lr->next) |
255 » » » » » escexpr(N, lr->n); | |
256 » » » » escstmtlist(ll->n->nbody); | |
257 » » » } | |
258 » » } | |
259 » » break; | |
260 | |
261 » case OAS: | |
262 » case OASOP: | |
263 » » escexpr(stmt->left, stmt->right); | |
264 » » break; | |
265 | |
266 » » // escape analysis happens after typecheck, so the | |
267 » » // OAS2xxx have already been substituted. | |
268 » case OAS2:» // x,y = a,b | |
269 » » cl = count(stmt->list); | |
270 » » cr = count(stmt->rlist); | |
271 » » if(cl > 1 && cr == 1) { | |
272 » » » for(ll=stmt->list; ll; ll=ll->next) | |
273 » » » » escexpr(ll->n, stmt->rlist->n); | |
274 » » } else { | |
275 » » » if(cl != cr) | |
276 » » » » fatal("escstmt: bad OAS2: %N", stmt); | |
277 » » » for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l r->next) | |
278 » » » » escexpr(ll->n, lr->n); | |
279 » » } | |
280 » » break; | |
281 | |
282 » case OAS2RECV:» » // v, ok = <-ch | |
283 » case OAS2MAPR:» » // v, ok = m[k] | |
284 » case OAS2DOTTYPE:» // v, ok = x.(type) | |
285 » » escexpr(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); | |
292 » » break; | |
293 | |
294 » case ORECV:» » // unary <-ch as statement | |
295 » » escexpr(N, stmt->left); | |
296 » » break; | |
297 | |
298 » case OSEND:» » // ch <- x | |
299 » » escexpr(&theSink, stmt->right);» // for now. TODO escexpr(stmt-> left, stmt->right); | |
300 » » break; | |
301 | |
302 » case OCOPY:» // todo: treat as *dst=*src instead of as dst=src | |
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); | |
310 » » break; | |
311 | |
312 » case OCALLINTER: | |
313 » case OCALLFUNC: | |
314 » case OCALLMETH: | |
315 » » escexpr(N, stmt); | |
316 » » break; | |
317 | |
318 » case OPROC: | |
319 » case ODEFER: | |
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); | |
331 » » break; | |
332 | |
333 » case ORETURN: | |
334 » » for(ll=stmt->list; ll; ll=ll->next) | |
335 » » » escexpr(&theSink, ll->n); | |
336 » » break; | |
337 | |
338 » case OCLOSE: | |
339 » case OPRINT: | |
340 » case OPRINTN: | |
341 » » escexpr(N, stmt->left); | |
342 » » for(ll=stmt->list; ll; ll=ll->next) | |
343 » » » escexpr(N, ll->n); | |
344 » » break; | |
345 | |
346 » case OPANIC: | |
347 » » // Argument could leak through recover. | |
348 » » escexpr(&theSink, stmt->left); | |
349 » » break; | |
350 » } | |
351 | |
352 » lineno = lno; | |
353 } | |
354 | |
355 // Assert that expr somehow gets assigned to dst, if non nil. for | |
356 // dst==nil, any name node expr still must be marked as being | |
357 // evaluated in curfn.» For expr==nil, dst must still be examined for | |
358 // evaluations inside it (e.g *f(x) = y) | |
359 static void | |
360 escexpr(Node *dst, Node *expr) | |
361 { | |
362 » int lno; | |
107 NodeList *ll; | 363 NodeList *ll; |
108 | 364 |
109 » if(n == N || n == dst) | 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 | |
390 » » » // fallthrough | |
391 » » case OIND: | |
392 » » case ODOTPTR: | |
393 » » case OSLICEARR:» // ->left is the OADDR of the array | |
394 » » doref: | |
395 » » » escexpr(N, dst->left); | |
396 » » » // assignment to dereferences: for now we lose track | |
397 » » » escexpr(&theSink, expr); | |
398 » » » return; | |
399 » » } | |
400 | |
401 » } | |
402 | |
403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX) | |
110 return; | 404 return; |
111 | 405 |
112 » setlineno(n); | 406 » if(expr->typecheck == 0 && expr->op != OKEY) { |
113 | 407 » » dump("escexpr missing typecheck", expr); |
114 » if (n->trecur) | |
115 » » return; | |
116 » n->trecur = 1; | |
117 | |
118 » if (n->typecheck == 0) {·· | |
119 » » dump("eaexpr missing typecheck", n); | |
120 fatal("Missing typecheck."); | 408 fatal("Missing typecheck."); |
121 } | 409 } |
122 | 410 |
123 » // if dst is not interesting, save some work down the recursion | 411 » lno = setlineno(expr); |
124 » if (dst != &theSink) | 412 » pdepth++; |
125 » if ((dst == N) || isblank(dst) || !isinteresting(dst->type)) | 413 |
126 » » dst = N; | 414 » if(debug['m'] > 1) |
127 | 415 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth, |
128 » // here: turn dests x[k] into x, etc. see part-of in top comment | 416 » » (curfn && curfn->nname) ? curfn->nname->sym : S, dst, |
129 | 417 » » 2*pdepth, ".\t.\t.\t.\t.\t", expr); |
130 » if (dst && dst != &theSink && !dst->type) dump("typeless dst", dst); | 418 |
131 »······· | 419 |
132 » if (dst && dst->op == ONAME && dst->class == PEXTERN) | 420 » switch(expr->op) { |
133 » » dst = &theSink; | 421 » case OADDR:» // dst = &x |
134 | 422 » case OIND:» // dst = *x |
135 » if (dst != &theSink) | 423 » case ODOTPTR:» // dst = (*x).f |
136 » » print("%L %S::%#N <= %#N\n", lineno, curfn->nname->sym, dst, n); | 424 » » // restart the recursion at x to figure out where it came from |
137 » else | 425 » » escexpr(expr->left, expr->left); |
138 » » print("%L %S:: leaks %#N\n", lineno, curfn->nname->sym, n); | |
139 | |
140 | |
141 » switch(n->op) { | |
142 » default: | |
143 » » dump("eaexpr", n); | |
144 » » fatal("eaexpr %O", n->op); | |
145 » » break; | |
146 | |
147 » case OADDR: | |
148 » » eaexpr(N, n->left); | |
149 // fallthrough | 426 // fallthrough |
150 case ONAME: | 427 case ONAME: |
151 case OPARAM: | 428 case OPARAM: |
152 » case OCOMPLIT: | 429 » » // loopdepth was set in the defining statement or function heade r |
430 » » escflows(dst, expr); | |
431 » » break; | |
432 | |
433 » case OARRAYLIT: | |
434 » case OSTRUCTLIT: | |
153 case OMAPLIT: | 435 case OMAPLIT: |
154 » case OSTRUCTLIT: | 436 » » expr->escloopdepth = loopdepth; |
155 » case OARRAYLIT: | 437 » » escflows(dst, expr); |
156 » » if (dst) dst->asrc = list(dst->asrc, n); | 438 » » for(ll=expr->list; ll; ll=ll->next) { |
157 » » break; | 439 » » » escexpr(expr, ll->n->left); |
158 | 440 » » » escexpr(expr, ll->n->right); |
159 » case OIND: // dst = *y | 441 » » } |
160 » » eaexpr(N, n->left); | 442 » » break; |
161 » » break; | 443 |
162 | 444 » case OMAKECHAN: |
163 » case OLITERAL: | 445 » case OMAKEMAP: |
164 » case ORECOVER: | 446 » case OMAKESLICE: |
165 » » break; | 447 » case ONEW: |
166 | 448 » » expr->curfn = curfn; // should have been done in parse, but pat ch it up here. |
167 » // harmless unary | 449 » » expr->escloopdepth = loopdepth; |
168 » case ONOT:» case OCOM: | 450 » » escflows(dst, expr); |
169 » case OCAP:» case OLEN: | 451 » » // first arg is type, all others need checking |
170 » case OREAL:» case OIMAG: | 452 » » for(ll=expr->list->next; ll; ll=ll->next) |
171 » case OARRAYBYTESTR: | 453 » » » escexpr(N, ll->n); |
172 » case OARRAYRUNESTR: | 454 » » break; |
173 » case OSTRARRAYBYTE: | 455 |
174 » case OSTRARRAYRUNE: | 456 » case OCLOSURE: |
175 » case ORUNESTR: | 457 » » expr->curfn = curfn; // should have been done in parse, but pat ch it up here. |
176 » » eaexpr(N, n->left); | 458 » » expr->escloopdepth = loopdepth; |
177 » » break; | 459 » » escflows(dst, expr); |
178 | 460 » » escfunc(expr); |
179 » // harmless binary | 461 » » break; |
180 » case OADD:» case OAND:» case OANDAND: | 462 |
181 » case OANDNOT:» case ODIV:» case OEQ: | 463 » // end of the leaf cases. no calls to escflows() in the cases below. |
182 » case OGE:» case OGT:» case OLE: | 464 |
183 » case OLT:» case OLSH:» case ORSH: | 465 |
184 » case OMOD:» case OMUL:» case ONE: | 466 » case OCONV:» // unaries that pass the value through |
185 » case OOR:» case OOROR:» case OSUB: | |
186 » case OXOR:» case OADDSTR:» case OASOP: | |
187 » case OCMPIFACE:»case OCMPSTR: » case OCOMPLEX: | |
188 » » eaexpr(N, n->left); | |
189 » » eaexpr(N, n->right); | |
190 » » break; | |
191 | |
192 » // unaries that pass the value through | |
193 » case OCONV: | |
194 case OCONVIFACE: | 467 case OCONVIFACE: |
195 case OCONVNOP: | 468 case OCONVNOP: |
196 case ODOTTYPE: | 469 case ODOTTYPE: |
197 case ODOTTYPE2: | 470 case ODOTTYPE2: |
198 » case ORECV: // leaks the whole channel | 471 » case ORECV:» // leaks the whole channel |
199 » » eaexpr(dst, n->left); | 472 » case ODOTMETH:» // expr->right is just the field or method name |
473 » case ODOTINTER: | |
474 » case ODOT: | |
475 » » escexpr(dst, expr->left); | |
200 break; | 476 break; |
201 | 477 |
202 case OCOPY: | 478 case OCOPY: |
203 // left leaks to right, but the return value is harmless | 479 // left leaks to right, but the return value is harmless |
204 » » eaexpr(n->left, n->right); | 480 » » // TODO: treat as *dst = *src, rather than as dst = src |
481 » » escexpr(expr->left, expr->right); | |
205 break; | 482 break; |
206 | 483 |
207 case OAPPEND: | 484 case OAPPEND: |
208 » » eaexpr(dst, n->list->n); | 485 » » // See TODO for OCOPY |
209 » » for (ll=n->list->next; ll; ll=ll->next) | 486 » » escexpr(dst, expr->list->n); |
210 » » » eaexpr(n->list->n, ll->n); | 487 » » for(ll=expr->list->next; ll; ll=ll->next) |
211 » » break; | 488 » » » escexpr(expr->list->n, ll->n); |
212 | 489 » » break; |
213 » // all arguments leak to the parameter nodes. | 490 |
214 » // TODO ...when we figure out how to get our hands at them. | |
215 case OCALLMETH: | 491 case OCALLMETH: |
492 case OCALLFUNC: | |
216 case OCALLINTER: | 493 case OCALLINTER: |
217 » » eaexpr(&theSink, n->left->left); // DOTMETH->this or DOTINTER->t his | 494 » » // Moved to separate function to isolate the hair. |
218 » » // 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) { | |
219 case OCALLFUNC: | 536 case OCALLFUNC: |
220 » » for (ll=n->list; ll; ll=ll->next) | 537 » » fn = expr->left; |
221 » » » eaexpr(&theSink , ll->n); | 538 » » escexpr(N, fn); |
222 » » break; | 539 » » fntype = fn->type; |
223 | 540 » » break; |
224 » case ODOT: | 541 |
542 » case OCALLMETH: | |
543 » » fn = expr->left->right;» // ODOTxx name | |
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: | |
225 case ODOTPTR: | 724 case ODOTPTR: |
226 // pretend all of the struct leaked | |
227 // n->right is just an (untypechecked) ONAME | |
228 eaexpr(dst, n->left); | |
229 break; | |
230 | |
231 case OSLICE: // the big thing leaks, the keys just need checking | |
232 case OSLICEARR: | |
233 case OSLICESTR: | |
234 case OINDEX:··· | |
235 case OINDEXMAP: | 725 case OINDEXMAP: |
236 » » eaexpr(dst, n->left); | 726 » case OIND: |
237 » » eaexpr(N, n->right); | 727 » » escwalk(level+1, dst, src->left); |
238 » » break; | 728 » } |
239 | 729 |
240 » case OMAKECHAN: | 730 » for (ll=src->escflowsrc; ll; ll=ll->next) |
241 » case OMAKEMAP: | 731 » » escwalk(level, dst, ll->n); |
242 » case OMAKESLICE: | 732 |
243 » case ONEW:» » // first arg is type, all others need checking | 733 » pdepth--; |
244 » » for (ll=n->list->next; ll; ll=ll->next) | 734 } |
245 » » » eaexpr(N, ll->n); | 735 |
246 » » if (dst) dst->asrc = list(dst->asrc, n); // but this is intere sting | 736 static void |
247 » » break; | 737 esctag(Node *func) |
248 | 738 { |
249 » » break; | 739 » Node *savefn; |
250 » } | 740 » NodeList *ll; |
251 | 741 |
252 » n->trecur = 0; | 742 » savefn = curfn; |
253 } | 743 » curfn = func; |
254 | 744 |
255 static void | 745 » for(ll=curfn->dcl; ll; ll=ll->next) { |
256 eastmtlist(NodeList* l) | 746 » » if(ll->n->op != ONAME || ll->n->class != PPARAM) |
257 { | 747 » » » continue; |
258 » for (; l; l = l->next) | 748 |
259 » » eastmt(l->n); | 749 » » switch (ll->n->esc) { |
260 } | 750 » » case EscNone:» // not touched by escflood |
261 | 751 » » » if (haspointers(ll->n->type)) // don't bother tagging fo r scalars |
262 static void | 752 » » » » ll->n->paramfld->note = safetag; |
263 eastmt(Node *n) | 753 » » case EscHeap:» // touched by escflood, moved to heap |
264 { | 754 » » case EscScope:» // touched by escflood, value leaves scope |
265 » int cl, cr; | |
266 » NodeList *ll, *lr; | |
267 | |
268 » if(n == N) | |
269 » » return; | |
270 | |
271 » if (n->trecur) | |
272 » » return; | |
273 » n->trecur = 1; | |
274 | |
275 » setlineno(n); | |
276 | |
277 » if (n->typecheck == 0) { | |
278 » » dump("eastmt missing typecheck", n); | |
279 » » fatal("Missing typecheck."); | |
280 » } | |
281 | |
282 » switch(n->op) { | |
283 » default: | |
284 » » // things i haven't thought of | |
285 » » dump("eastmt", n); | |
286 » » // fallthrough | |
287 » » // things that shouldn't happen here | |
288 » case OCASE: case OXCASE: | |
289 » case OFALL: | |
290 » case OCALL: | |
291 » » fatal("eastmt %O", n->op); | |
292 » » break; | |
293 | |
294 » case ODCL: | |
295 » case ODCLCONST: | |
296 » case ODCLTYPE: | |
297 » case OBREAK: | |
298 » case OCONTINUE: | |
299 » case OXFALL: | |
300 » case OGOTO: | |
301 » case OLABEL: | |
302 » case OEMPTY: | |
303 » case OCLOSE: | |
304 » case OPRINT: | |
305 » case OPRINTN: | |
306 » » // all harmless | |
307 » » break; | |
308 | |
309 » case OBLOCK: | |
310 » » eastmtlist(n->list); | |
311 » » break; | |
312 | |
313 » case OFOR: | |
314 » » eastmtlist(n->ninit); | |
315 » » if(n->ntest != N) { | |
316 » » » eastmtlist(n->ntest->ninit); | |
317 » » » eaexpr(N, n->ntest); | |
318 » » } | |
319 » » eastmt(n->nincr); | |
320 » » eastmtlist(n->nbody); | |
321 » » break; | |
322 | |
323 » case ORANGE:» » // for <list> = range <right> { <nbody> } | |
324 » » switch(n->type->etype) { | |
325 » » case TARRAY:» // i, v = range sliceorarrayorstring | |
326 » » case TSTRING: | |
327 » » » if(n->list->next) | |
328 » » » » eaexpr(n->list->next->n, n->right); | |
329 » » » break;» » »······· | |
330 » » case TMAP:» // k, v = range map | |
331 » » » eaexpr(n->list->n, n->right); | |
332 » » » if(n->list->next) | |
333 » » » » eaexpr(n->list->next->n, n->right); | |
334 break; | 755 break; |
335 » » case TCHAN:» // v = range chan | 756 » » default: |
336 » » » eaexpr(n->list->n, n->right); | 757 » » » fatal("messed up escape tagging: %N::%N", curfn, ll->n); |
337 » » » break; | 758 » » } |
338 » » } | 759 » } |
339 »······· | 760 |
340 » » eastmtlist(n->nbody); | 761 » curfn = savefn; |
341 » » break; | 762 } |
342 | |
343 » case OIF: | |
344 » » eastmtlist(n->ninit); | |
345 » » eaexpr(N, n->ntest); | |
346 » » eastmtlist(n->nbody); | |
347 » » eastmtlist(n->nelse); | |
348 » » break; | |
349 | |
350 » case OSELECT: | |
351 » » eastmtlist(n->ninit); | |
352 » » for(ll=n->list; ll; ll=ll->next) { // todo what about the test? | |
353 » » » eastmt(ll->n->left); | |
354 » » » eastmtlist(ll->n->nbody); | |
355 » » } | |
356 » » break; | |
357 | |
358 » case OSELRECV2: | |
359 » case OSELRECV: | |
360 » » eaexpr(n->left, n->right); | |
361 » » break; | |
362 | |
363 » case OSWITCH: | |
364 » » eastmtlist(n->ninit); | |
365 » » if(n->ntest && n->ntest->op == OTYPESW) { | |
366 » » » eaexpr(n->ntest->left, n->ntest->right); | |
367 » » » for(ll=n->list; ll; ll=ll->next) | |
368 » » » » eastmtlist(ll->n->nbody); | |
369 » » } else { | |
370 » » » eaexpr(N, n->ntest); | |
371 » » » for(ll=n->list; ll; ll=ll->next) { // cases | |
372 » » » » for (lr=ll->n->list; lr; lr=lr->next) | |
373 » » » » » eaexpr(N, lr->n); | |
374 » » » » eastmtlist(ll->n->nbody); | |
375 » » » } | |
376 » » } | |
377 » » break; | |
378 | |
379 » case OAS:· | |
380 » case OASOP:» » // v += x etc. never applies to pointers, but l eave it to eaexpr to figure that out. | |
381 » » eaexpr(n->left, n->right); | |
382 » » break; | |
383 | |
384 » » // escape analysis happens after typecheck, so the | |
385 » » // OAS2xxx have already been substituted. | |
386 » case OAS2: | |
387 » » cl = count(n->list); | |
388 » » cr = count(n->rlist); | |
389 » » if (cl > 1 && cr == 1) { | |
390 » » » for(ll=n->list; ll; ll=ll->next) | |
391 » » » » eaexpr(ll->n, n->rlist->n); | |
392 » » } else {· | |
393 » » » if(cl != cr) | |
394 » » » » fatal("eastmt: bad OAS2: %N", n); | |
395 » » » for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->nex t) | |
396 » » » » eaexpr(ll->n, lr->n); | |
397 » » } | |
398 » » break; | |
399 » »······· | |
400 » case OAS2RECV:» » // v, ok = <-ch | |
401 » case OAS2MAPR:» » // v, ok = m[k] | |
402 » case OAS2DOTTYPE:» // v, ok = x.(type) | |
403 » » eaexpr(n->list->n, n->rlist->n->left); | |
404 » » break; | |
405 | |
406 » case OAS2MAPW: » // m[k] = x, ok | |
407 » » eaexpr(n->list->n->left, n->rlist->n); | |
408 » » break; | |
409 | |
410 » case ORECV:» » // unary <-ch as statement | |
411 » » eaexpr(N, n->left); | |
412 » » break; | |
413 | |
414 » case OSEND:» » // ch <- x | |
415 » » eaexpr(n->left, n->right); | |
416 » » break; | |
417 | |
418 » case OCOPY:» // second arguments leaks to the first | |
419 » » eaexpr(n->left, n->right); | |
420 » » break; | |
421 | |
422 » case OAPPEND: // note: append also leaks all its args in an expression | |
423 » » for (ll=n->list->next; ll; ll=ll->next) | |
424 » » » eaexpr(n->list->n, ll->n); | |
425 » » break; | |
426 | |
427 » case OAS2FUNC:» // x,y,z = f() | |
428 » » // the return values already escape because they're return value s | |
429 » » // no need to tie them to x, y or z here. | |
430 » » eaexpr(N, n->rlist->n); | |
431 » » break; | |
432 » »······· | |
433 » case OCALLINTER: | |
434 » case OCALLFUNC: | |
435 » case OCALLMETH: | |
436 » » n->trecur = 0; | |
437 » » eaexpr(N, n); | |
438 » » break; | |
439 | |
440 » case ODEFER: | |
441 » » eastmt(n->left); | |
442 » » break; | |
443 | |
444 » case ORETURN: | |
445 » » for (ll=n->list; ll; ll=ll->next) | |
446 » » » eaexpr(&theSink, ll->n); | |
447 » » break; | |
448 | |
449 » case OPROC: // n->left is a pseudocall, consider all arguments leaking | |
450 » » for (ll=n->left->list; ll; ll=ll->next) | |
451 » » » eaexpr(&theSink, ll->n); | |
452 » » break; | |
453 | |
454 » case OPANIC: | |
455 » » // Argument could leak through recover. | |
456 » » eaexpr(&theSink, n->left); | |
457 » » break; | |
458 » } | |
459 | |
460 » n->trecur = 0; | |
461 } | |
462 | |
463 | |
464 | |
465 #if 0 | |
466 | |
467 » for(tl=tstruct->type; tl; tl=tl->down) { | |
468 » » t = tl->type; | |
469 » » if(tl->isddd) { | |
470 » » » if(isddd) { | |
471 » » » » if(nl == nil) | |
472 » » » » » goto notenough; | |
473 » » » » if(nl->next != nil) | |
474 » » » » » goto toomany; | |
475 » » » » n = nl->n; | |
476 » » » » setlineno(n); | |
477 » » » » if(n->type != T) { | |
478 » » » » » nl->n = assignconv(n, t, desc); | |
479 » » » » » if (call) argused(call, tl, n); | |
480 » » » » } | |
481 » » » » goto out; | |
482 » » » } | |
483 » » » for(; nl; nl=nl->next) { | |
484 » » » » n = nl->n; | |
485 » » » » setlineno(nl->n); | |
486 » » » » if(n->type != T) { | |
487 » » » » » nl->n = assignconv(n, t->type, desc); | |
488 » » » » » if (call) argused(call, tl, n); | |
489 » » » » } | |
490 » » » } | |
491 » » » goto out; | |
492 » » }· | |
493 | |
494 | |
495 // mark that value has been used as the argument arg | |
496 static void argused(Node* func, Type* arg, Node* val) { | |
497 //» if (arg->sym && arg->sym->pkg == localpkg) | |
498 //» » arg->note = strlit("used"); | |
499 //» print("Using %N :: %#T = %N\n", func, arg, val); | |
500 } | |
501 | |
502 | |
503 /* | |
504 * type check function definition | |
505 */ | |
506 static void | |
507 escanalfunc(Node *n) | |
508 { | |
509 //» Type *t, *t1, *rcvr; | |
510 » Type *t, *rcvr; | |
511 | |
512 » typecheck(&n->nname, Erv | Easgn); | |
513 » if((t = n->nname->type) == T) | |
514 » » return; | |
515 » n->type = t; | |
516 | |
517 » print("typecheckfunc %T\n", n->type); | |
518 » for(t1=getinargx(t)->type; t1; t1=t1->down) { | |
519 » » if (isptr[t1->type->etype]) | |
520 » » » t1->note = strlit("leaks"); | |
521 » » print("\t%T\n", t1); | |
522 | |
523 » } | |
524 | |
525 » rcvr = getthisx(t)->type; | |
526 » if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) | |
527 » » addmethod(n->shortname->sym, t, 1); | |
528 } | |
529 | |
530 #endif | |
LEFT | RIGHT |