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