1 | // Copyright 2013 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | package pointer |
6 | |
7 | // This file implements the generation and resolution rules for |
8 | // constraints arising from the use of reflection in the target |
9 | // program. See doc.go for explanation of the representation. |
10 | // |
11 | // For consistency, the names of all parameters match those of the |
12 | // actual functions in the "reflect" package. |
13 | // |
14 | // To avoid proliferation of equivalent labels, intrinsics should |
15 | // memoize as much as possible, like TypeOf and Zero do for their |
16 | // tagged objects. |
17 | // |
18 | // TODO(adonovan): this file is rather subtle. Explain how we derive |
19 | // the implementation of each reflect operator from its spec, |
20 | // including the subtleties of reflect.flag{Addr,RO,Indir}. |
21 | // [Hint: our implementation is as if reflect.flagIndir was always |
22 | // true, i.e. reflect.Values are pointers to tagged objects, there is |
23 | // no inline allocation optimization; and indirect tagged objects (not |
24 | // yet implemented) correspond to reflect.Values with |
25 | // reflect.flagAddr.] |
26 | // A picture would help too. |
27 | // |
28 | // TODO(adonovan): try factoring up the common parts of the majority of |
29 | // these constraints that are single input, single output. |
30 | |
31 | import ( |
32 | "fmt" |
33 | "go/constant" |
34 | "go/types" |
35 | "reflect" |
36 | |
37 | "golang.org/x/tools/go/ssa" |
38 | ) |
39 | |
40 | func init() { |
41 | for name, fn := range map[string]intrinsic{ |
42 | // reflect.Value methods. |
43 | "(reflect.Value).Addr": ext۰reflect۰Value۰Addr, |
44 | "(reflect.Value).Bool": ext۰NoEffect, |
45 | "(reflect.Value).Bytes": ext۰reflect۰Value۰Bytes, |
46 | "(reflect.Value).Call": ext۰reflect۰Value۰Call, |
47 | "(reflect.Value).CallSlice": ext۰reflect۰Value۰CallSlice, |
48 | "(reflect.Value).CanAddr": ext۰NoEffect, |
49 | "(reflect.Value).CanInterface": ext۰NoEffect, |
50 | "(reflect.Value).CanSet": ext۰NoEffect, |
51 | "(reflect.Value).Cap": ext۰NoEffect, |
52 | "(reflect.Value).Close": ext۰NoEffect, |
53 | "(reflect.Value).Complex": ext۰NoEffect, |
54 | "(reflect.Value).Convert": ext۰reflect۰Value۰Convert, |
55 | "(reflect.Value).Elem": ext۰reflect۰Value۰Elem, |
56 | "(reflect.Value).Field": ext۰reflect۰Value۰Field, |
57 | "(reflect.Value).FieldByIndex": ext۰reflect۰Value۰FieldByIndex, |
58 | "(reflect.Value).FieldByName": ext۰reflect۰Value۰FieldByName, |
59 | "(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc, |
60 | "(reflect.Value).Float": ext۰NoEffect, |
61 | "(reflect.Value).Index": ext۰reflect۰Value۰Index, |
62 | "(reflect.Value).Int": ext۰NoEffect, |
63 | "(reflect.Value).Interface": ext۰reflect۰Value۰Interface, |
64 | "(reflect.Value).InterfaceData": ext۰NoEffect, |
65 | "(reflect.Value).IsNil": ext۰NoEffect, |
66 | "(reflect.Value).IsValid": ext۰NoEffect, |
67 | "(reflect.Value).Kind": ext۰NoEffect, |
68 | "(reflect.Value).Len": ext۰NoEffect, |
69 | "(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex, |
70 | "(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys, |
71 | "(reflect.Value).Method": ext۰reflect۰Value۰Method, |
72 | "(reflect.Value).MethodByName": ext۰reflect۰Value۰MethodByName, |
73 | "(reflect.Value).NumField": ext۰NoEffect, |
74 | "(reflect.Value).NumMethod": ext۰NoEffect, |
75 | "(reflect.Value).OverflowComplex": ext۰NoEffect, |
76 | "(reflect.Value).OverflowFloat": ext۰NoEffect, |
77 | "(reflect.Value).OverflowInt": ext۰NoEffect, |
78 | "(reflect.Value).OverflowUint": ext۰NoEffect, |
79 | "(reflect.Value).Pointer": ext۰NoEffect, |
80 | "(reflect.Value).Recv": ext۰reflect۰Value۰Recv, |
81 | "(reflect.Value).Send": ext۰reflect۰Value۰Send, |
82 | "(reflect.Value).Set": ext۰reflect۰Value۰Set, |
83 | "(reflect.Value).SetBool": ext۰NoEffect, |
84 | "(reflect.Value).SetBytes": ext۰reflect۰Value۰SetBytes, |
85 | "(reflect.Value).SetComplex": ext۰NoEffect, |
86 | "(reflect.Value).SetFloat": ext۰NoEffect, |
87 | "(reflect.Value).SetInt": ext۰NoEffect, |
88 | "(reflect.Value).SetLen": ext۰NoEffect, |
89 | "(reflect.Value).SetMapIndex": ext۰reflect۰Value۰SetMapIndex, |
90 | "(reflect.Value).SetPointer": ext۰reflect۰Value۰SetPointer, |
91 | "(reflect.Value).SetString": ext۰NoEffect, |
92 | "(reflect.Value).SetUint": ext۰NoEffect, |
93 | "(reflect.Value).Slice": ext۰reflect۰Value۰Slice, |
94 | "(reflect.Value).String": ext۰NoEffect, |
95 | "(reflect.Value).TryRecv": ext۰reflect۰Value۰Recv, |
96 | "(reflect.Value).TrySend": ext۰reflect۰Value۰Send, |
97 | "(reflect.Value).Type": ext۰NoEffect, |
98 | "(reflect.Value).Uint": ext۰NoEffect, |
99 | "(reflect.Value).UnsafeAddr": ext۰NoEffect, |
100 | |
101 | // Standalone reflect.* functions. |
102 | "reflect.Append": ext۰reflect۰Append, |
103 | "reflect.AppendSlice": ext۰reflect۰AppendSlice, |
104 | "reflect.Copy": ext۰reflect۰Copy, |
105 | "reflect.ChanOf": ext۰reflect۰ChanOf, |
106 | "reflect.DeepEqual": ext۰NoEffect, |
107 | "reflect.Indirect": ext۰reflect۰Indirect, |
108 | "reflect.MakeChan": ext۰reflect۰MakeChan, |
109 | "reflect.MakeFunc": ext۰reflect۰MakeFunc, |
110 | "reflect.MakeMap": ext۰reflect۰MakeMap, |
111 | "reflect.MakeSlice": ext۰reflect۰MakeSlice, |
112 | "reflect.MapOf": ext۰reflect۰MapOf, |
113 | "reflect.New": ext۰reflect۰New, |
114 | "reflect.NewAt": ext۰reflect۰NewAt, |
115 | "reflect.PtrTo": ext۰reflect۰PtrTo, |
116 | "reflect.Select": ext۰reflect۰Select, |
117 | "reflect.SliceOf": ext۰reflect۰SliceOf, |
118 | "reflect.TypeOf": ext۰reflect۰TypeOf, |
119 | "reflect.ValueOf": ext۰reflect۰ValueOf, |
120 | "reflect.Zero": ext۰reflect۰Zero, |
121 | "reflect.init": ext۰NoEffect, |
122 | |
123 | // *reflect.rtype methods |
124 | "(*reflect.rtype).Align": ext۰NoEffect, |
125 | "(*reflect.rtype).AssignableTo": ext۰NoEffect, |
126 | "(*reflect.rtype).Bits": ext۰NoEffect, |
127 | "(*reflect.rtype).ChanDir": ext۰NoEffect, |
128 | "(*reflect.rtype).ConvertibleTo": ext۰NoEffect, |
129 | "(*reflect.rtype).Elem": ext۰reflect۰rtype۰Elem, |
130 | "(*reflect.rtype).Field": ext۰reflect۰rtype۰Field, |
131 | "(*reflect.rtype).FieldAlign": ext۰NoEffect, |
132 | "(*reflect.rtype).FieldByIndex": ext۰reflect۰rtype۰FieldByIndex, |
133 | "(*reflect.rtype).FieldByName": ext۰reflect۰rtype۰FieldByName, |
134 | "(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc, |
135 | "(*reflect.rtype).Implements": ext۰NoEffect, |
136 | "(*reflect.rtype).In": ext۰reflect۰rtype۰In, |
137 | "(*reflect.rtype).IsVariadic": ext۰NoEffect, |
138 | "(*reflect.rtype).Key": ext۰reflect۰rtype۰Key, |
139 | "(*reflect.rtype).Kind": ext۰NoEffect, |
140 | "(*reflect.rtype).Len": ext۰NoEffect, |
141 | "(*reflect.rtype).Method": ext۰reflect۰rtype۰Method, |
142 | "(*reflect.rtype).MethodByName": ext۰reflect۰rtype۰MethodByName, |
143 | "(*reflect.rtype).Name": ext۰NoEffect, |
144 | "(*reflect.rtype).NumField": ext۰NoEffect, |
145 | "(*reflect.rtype).NumIn": ext۰NoEffect, |
146 | "(*reflect.rtype).NumMethod": ext۰NoEffect, |
147 | "(*reflect.rtype).NumOut": ext۰NoEffect, |
148 | "(*reflect.rtype).Out": ext۰reflect۰rtype۰Out, |
149 | "(*reflect.rtype).PkgPath": ext۰NoEffect, |
150 | "(*reflect.rtype).Size": ext۰NoEffect, |
151 | "(*reflect.rtype).String": ext۰NoEffect, |
152 | } { |
153 | intrinsicsByName[name] = fn |
154 | } |
155 | } |
156 | |
157 | // -------------------- (reflect.Value) -------------------- |
158 | |
159 | func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
160 | |
161 | // ---------- func (Value).Bytes() Value ---------- |
162 | |
163 | // result = v.Bytes() |
164 | type rVBytesConstraint struct { |
165 | v nodeid // (ptr) |
166 | result nodeid // (indirect) |
167 | } |
168 | |
169 | func (c *rVBytesConstraint) ptr() nodeid { return c.v } |
170 | func (c *rVBytesConstraint) presolve(h *hvn) { |
171 | h.markIndirect(onodeid(c.result), "rVBytes.result") |
172 | } |
173 | func (c *rVBytesConstraint) renumber(mapping []nodeid) { |
174 | c.v = mapping[c.v] |
175 | c.result = mapping[c.result] |
176 | } |
177 | |
178 | func (c *rVBytesConstraint) String() string { |
179 | return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v) |
180 | } |
181 | |
182 | func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) { |
183 | changed := false |
184 | for _, x := range delta.AppendTo(a.deltaSpace) { |
185 | vObj := nodeid(x) |
186 | tDyn, slice, indirect := a.taggedValue(vObj) |
187 | if indirect { |
188 | // TODO(adonovan): we'll need to implement this |
189 | // when we start creating indirect tagged objects. |
190 | panic("indirect tagged object") |
191 | } |
192 | |
193 | tSlice, ok := tDyn.Underlying().(*types.Slice) |
194 | if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) { |
195 | if a.onlineCopy(c.result, slice) { |
196 | changed = true |
197 | } |
198 | } |
199 | } |
200 | if changed { |
201 | a.addWork(c.result) |
202 | } |
203 | } |
204 | |
205 | func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) { |
206 | a.addConstraint(&rVBytesConstraint{ |
207 | v: a.funcParams(cgn.obj), |
208 | result: a.funcResults(cgn.obj), |
209 | }) |
210 | } |
211 | |
212 | // ---------- func (Value).Call(in []Value) []Value ---------- |
213 | |
214 | // result = v.Call(in) |
215 | type rVCallConstraint struct { |
216 | cgn *cgnode |
217 | targets nodeid // (indirect) |
218 | v nodeid // (ptr) |
219 | arg nodeid // = in[*] |
220 | result nodeid // (indirect) |
221 | dotdotdot bool // interpret last arg as a "..." slice |
222 | } |
223 | |
224 | func (c *rVCallConstraint) ptr() nodeid { return c.v } |
225 | func (c *rVCallConstraint) presolve(h *hvn) { |
226 | h.markIndirect(onodeid(c.targets), "rVCall.targets") |
227 | h.markIndirect(onodeid(c.result), "rVCall.result") |
228 | } |
229 | func (c *rVCallConstraint) renumber(mapping []nodeid) { |
230 | c.targets = mapping[c.targets] |
231 | c.v = mapping[c.v] |
232 | c.arg = mapping[c.arg] |
233 | c.result = mapping[c.result] |
234 | } |
235 | |
236 | func (c *rVCallConstraint) String() string { |
237 | return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg) |
238 | } |
239 | |
240 | func (c *rVCallConstraint) solve(a *analysis, delta *nodeset) { |
241 | if c.targets == 0 { |
242 | panic("no targets") |
243 | } |
244 | |
245 | changed := false |
246 | for _, x := range delta.AppendTo(a.deltaSpace) { |
247 | vObj := nodeid(x) |
248 | tDyn, fn, indirect := a.taggedValue(vObj) |
249 | if indirect { |
250 | // TODO(adonovan): we'll need to implement this |
251 | // when we start creating indirect tagged objects. |
252 | panic("indirect tagged object") |
253 | } |
254 | |
255 | tSig, ok := tDyn.Underlying().(*types.Signature) |
256 | if !ok { |
257 | continue // not a function |
258 | } |
259 | if tSig.Recv() != nil { |
260 | panic(tSig) // TODO(adonovan): rethink when we implement Method() |
261 | } |
262 | |
263 | // Add dynamic call target. |
264 | if a.onlineCopy(c.targets, fn) { |
265 | a.addWork(c.targets) |
266 | // TODO(adonovan): is 'else continue' a sound optimisation here? |
267 | } |
268 | |
269 | // Allocate a P/R block. |
270 | tParams := tSig.Params() |
271 | tResults := tSig.Results() |
272 | params := a.addNodes(tParams, "rVCall.params") |
273 | results := a.addNodes(tResults, "rVCall.results") |
274 | |
275 | // Make a dynamic call to 'fn'. |
276 | a.store(fn, params, 1, a.sizeof(tParams)) |
277 | a.load(results, fn, 1+a.sizeof(tParams), a.sizeof(tResults)) |
278 | |
279 | // Populate P by type-asserting each actual arg (all merged in c.arg). |
280 | for i, n := 0, tParams.Len(); i < n; i++ { |
281 | T := tParams.At(i).Type() |
282 | a.typeAssert(T, params, c.arg, false) |
283 | params += nodeid(a.sizeof(T)) |
284 | } |
285 | |
286 | // Use R by tagging and copying each actual result to c.result. |
287 | for i, n := 0, tResults.Len(); i < n; i++ { |
288 | T := tResults.At(i).Type() |
289 | // Convert from an arbitrary type to a reflect.Value |
290 | // (like MakeInterface followed by reflect.ValueOf). |
291 | if isInterface(T) { |
292 | // (don't tag) |
293 | if a.onlineCopy(c.result, results) { |
294 | changed = true |
295 | } |
296 | } else { |
297 | obj := a.makeTagged(T, c.cgn, nil) |
298 | a.onlineCopyN(obj+1, results, a.sizeof(T)) |
299 | if a.addLabel(c.result, obj) { // (true) |
300 | changed = true |
301 | } |
302 | } |
303 | results += nodeid(a.sizeof(T)) |
304 | } |
305 | } |
306 | if changed { |
307 | a.addWork(c.result) |
308 | } |
309 | } |
310 | |
311 | // Common code for direct (inlined) and indirect calls to (reflect.Value).Call. |
312 | func reflectCallImpl(a *analysis, cgn *cgnode, site *callsite, recv, arg nodeid, dotdotdot bool) nodeid { |
313 | // Allocate []reflect.Value array for the result. |
314 | ret := a.nextNode() |
315 | a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "rVCall.ret") |
316 | a.endObject(ret, cgn, nil) |
317 | |
318 | // pts(targets) will be the set of possible call targets. |
319 | site.targets = a.addOneNode(tInvalid, "rvCall.targets", nil) |
320 | |
321 | // All arguments are merged since they arrive in a slice. |
322 | argelts := a.addOneNode(a.reflectValueObj.Type(), "rVCall.args", nil) |
323 | a.load(argelts, arg, 1, 1) // slice elements |
324 | |
325 | a.addConstraint(&rVCallConstraint{ |
326 | cgn: cgn, |
327 | targets: site.targets, |
328 | v: recv, |
329 | arg: argelts, |
330 | result: ret + 1, // results go into elements of ret |
331 | dotdotdot: dotdotdot, |
332 | }) |
333 | return ret |
334 | } |
335 | |
336 | func reflectCall(a *analysis, cgn *cgnode, dotdotdot bool) { |
337 | // This is the shared contour implementation of (reflect.Value).Call |
338 | // and CallSlice, as used by indirect calls (rare). |
339 | // Direct calls are inlined in gen.go, eliding the |
340 | // intermediate cgnode for Call. |
341 | site := new(callsite) |
342 | cgn.sites = append(cgn.sites, site) |
343 | recv := a.funcParams(cgn.obj) |
344 | arg := recv + 1 |
345 | ret := reflectCallImpl(a, cgn, site, recv, arg, dotdotdot) |
346 | a.addressOf(cgn.fn.Signature.Results().At(0).Type(), a.funcResults(cgn.obj), ret) |
347 | } |
348 | |
349 | func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) { |
350 | reflectCall(a, cgn, false) |
351 | } |
352 | |
353 | func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) { |
354 | // TODO(adonovan): implement. Also, inline direct calls in gen.go too. |
355 | if false { |
356 | reflectCall(a, cgn, true) |
357 | } |
358 | } |
359 | |
360 | func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
361 | |
362 | // ---------- func (Value).Elem() Value ---------- |
363 | |
364 | // result = v.Elem() |
365 | type rVElemConstraint struct { |
366 | cgn *cgnode |
367 | v nodeid // (ptr) |
368 | result nodeid // (indirect) |
369 | } |
370 | |
371 | func (c *rVElemConstraint) ptr() nodeid { return c.v } |
372 | func (c *rVElemConstraint) presolve(h *hvn) { |
373 | h.markIndirect(onodeid(c.result), "rVElem.result") |
374 | } |
375 | func (c *rVElemConstraint) renumber(mapping []nodeid) { |
376 | c.v = mapping[c.v] |
377 | c.result = mapping[c.result] |
378 | } |
379 | |
380 | func (c *rVElemConstraint) String() string { |
381 | return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v) |
382 | } |
383 | |
384 | func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) { |
385 | changed := false |
386 | for _, x := range delta.AppendTo(a.deltaSpace) { |
387 | vObj := nodeid(x) |
388 | tDyn, payload, indirect := a.taggedValue(vObj) |
389 | if indirect { |
390 | // TODO(adonovan): we'll need to implement this |
391 | // when we start creating indirect tagged objects. |
392 | panic("indirect tagged object") |
393 | } |
394 | |
395 | switch t := tDyn.Underlying().(type) { |
396 | case *types.Interface: |
397 | if a.onlineCopy(c.result, payload) { |
398 | changed = true |
399 | } |
400 | |
401 | case *types.Pointer: |
402 | obj := a.makeTagged(t.Elem(), c.cgn, nil) |
403 | a.load(obj+1, payload, 0, a.sizeof(t.Elem())) |
404 | if a.addLabel(c.result, obj) { |
405 | changed = true |
406 | } |
407 | } |
408 | } |
409 | if changed { |
410 | a.addWork(c.result) |
411 | } |
412 | } |
413 | |
414 | func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) { |
415 | a.addConstraint(&rVElemConstraint{ |
416 | cgn: cgn, |
417 | v: a.funcParams(cgn.obj), |
418 | result: a.funcResults(cgn.obj), |
419 | }) |
420 | } |
421 | |
422 | func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
423 | func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
424 | func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
425 | func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
426 | |
427 | // ---------- func (Value).Index() Value ---------- |
428 | |
429 | // result = v.Index() |
430 | type rVIndexConstraint struct { |
431 | cgn *cgnode |
432 | v nodeid // (ptr) |
433 | result nodeid // (indirect) |
434 | } |
435 | |
436 | func (c *rVIndexConstraint) ptr() nodeid { return c.v } |
437 | func (c *rVIndexConstraint) presolve(h *hvn) { |
438 | h.markIndirect(onodeid(c.result), "rVIndex.result") |
439 | } |
440 | func (c *rVIndexConstraint) renumber(mapping []nodeid) { |
441 | c.v = mapping[c.v] |
442 | c.result = mapping[c.result] |
443 | } |
444 | |
445 | func (c *rVIndexConstraint) String() string { |
446 | return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v) |
447 | } |
448 | |
449 | func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) { |
450 | changed := false |
451 | for _, x := range delta.AppendTo(a.deltaSpace) { |
452 | vObj := nodeid(x) |
453 | tDyn, payload, indirect := a.taggedValue(vObj) |
454 | if indirect { |
455 | // TODO(adonovan): we'll need to implement this |
456 | // when we start creating indirect tagged objects. |
457 | panic("indirect tagged object") |
458 | } |
459 | |
460 | var res nodeid |
461 | switch t := tDyn.Underlying().(type) { |
462 | case *types.Array: |
463 | res = a.makeTagged(t.Elem(), c.cgn, nil) |
464 | a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem())) |
465 | |
466 | case *types.Slice: |
467 | res = a.makeTagged(t.Elem(), c.cgn, nil) |
468 | a.load(res+1, payload, 1, a.sizeof(t.Elem())) |
469 | |
470 | case *types.Basic: |
471 | if t.Kind() == types.String { |
472 | res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil) |
473 | } |
474 | } |
475 | if res != 0 && a.addLabel(c.result, res) { |
476 | changed = true |
477 | } |
478 | } |
479 | if changed { |
480 | a.addWork(c.result) |
481 | } |
482 | } |
483 | |
484 | func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) { |
485 | a.addConstraint(&rVIndexConstraint{ |
486 | cgn: cgn, |
487 | v: a.funcParams(cgn.obj), |
488 | result: a.funcResults(cgn.obj), |
489 | }) |
490 | } |
491 | |
492 | // ---------- func (Value).Interface() Value ---------- |
493 | |
494 | // result = v.Interface() |
495 | type rVInterfaceConstraint struct { |
496 | v nodeid // (ptr) |
497 | result nodeid // (indirect) |
498 | } |
499 | |
500 | func (c *rVInterfaceConstraint) ptr() nodeid { return c.v } |
501 | func (c *rVInterfaceConstraint) presolve(h *hvn) { |
502 | h.markIndirect(onodeid(c.result), "rVInterface.result") |
503 | } |
504 | func (c *rVInterfaceConstraint) renumber(mapping []nodeid) { |
505 | c.v = mapping[c.v] |
506 | c.result = mapping[c.result] |
507 | } |
508 | |
509 | func (c *rVInterfaceConstraint) String() string { |
510 | return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v) |
511 | } |
512 | |
513 | func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) { |
514 | changed := false |
515 | for _, x := range delta.AppendTo(a.deltaSpace) { |
516 | vObj := nodeid(x) |
517 | tDyn, payload, indirect := a.taggedValue(vObj) |
518 | if indirect { |
519 | // TODO(adonovan): we'll need to implement this |
520 | // when we start creating indirect tagged objects. |
521 | panic("indirect tagged object") |
522 | } |
523 | |
524 | if isInterface(tDyn) { |
525 | if a.onlineCopy(c.result, payload) { |
526 | a.addWork(c.result) |
527 | } |
528 | } else { |
529 | if a.addLabel(c.result, vObj) { |
530 | changed = true |
531 | } |
532 | } |
533 | } |
534 | if changed { |
535 | a.addWork(c.result) |
536 | } |
537 | } |
538 | |
539 | func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) { |
540 | a.addConstraint(&rVInterfaceConstraint{ |
541 | v: a.funcParams(cgn.obj), |
542 | result: a.funcResults(cgn.obj), |
543 | }) |
544 | } |
545 | |
546 | // ---------- func (Value).MapIndex(Value) Value ---------- |
547 | |
548 | // result = v.MapIndex(_) |
549 | type rVMapIndexConstraint struct { |
550 | cgn *cgnode |
551 | v nodeid // (ptr) |
552 | result nodeid // (indirect) |
553 | } |
554 | |
555 | func (c *rVMapIndexConstraint) ptr() nodeid { return c.v } |
556 | func (c *rVMapIndexConstraint) presolve(h *hvn) { |
557 | h.markIndirect(onodeid(c.result), "rVMapIndex.result") |
558 | } |
559 | func (c *rVMapIndexConstraint) renumber(mapping []nodeid) { |
560 | c.v = mapping[c.v] |
561 | c.result = mapping[c.result] |
562 | } |
563 | |
564 | func (c *rVMapIndexConstraint) String() string { |
565 | return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v) |
566 | } |
567 | |
568 | func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) { |
569 | changed := false |
570 | for _, x := range delta.AppendTo(a.deltaSpace) { |
571 | vObj := nodeid(x) |
572 | tDyn, m, indirect := a.taggedValue(vObj) |
573 | tMap, _ := tDyn.Underlying().(*types.Map) |
574 | if tMap == nil { |
575 | continue // not a map |
576 | } |
577 | if indirect { |
578 | // TODO(adonovan): we'll need to implement this |
579 | // when we start creating indirect tagged objects. |
580 | panic("indirect tagged object") |
581 | } |
582 | |
583 | obj := a.makeTagged(tMap.Elem(), c.cgn, nil) |
584 | a.load(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem())) |
585 | if a.addLabel(c.result, obj) { |
586 | changed = true |
587 | } |
588 | } |
589 | if changed { |
590 | a.addWork(c.result) |
591 | } |
592 | } |
593 | |
594 | func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) { |
595 | a.addConstraint(&rVMapIndexConstraint{ |
596 | cgn: cgn, |
597 | v: a.funcParams(cgn.obj), |
598 | result: a.funcResults(cgn.obj), |
599 | }) |
600 | } |
601 | |
602 | // ---------- func (Value).MapKeys() []Value ---------- |
603 | |
604 | // result = v.MapKeys() |
605 | type rVMapKeysConstraint struct { |
606 | cgn *cgnode |
607 | v nodeid // (ptr) |
608 | result nodeid // (indirect) |
609 | } |
610 | |
611 | func (c *rVMapKeysConstraint) ptr() nodeid { return c.v } |
612 | func (c *rVMapKeysConstraint) presolve(h *hvn) { |
613 | h.markIndirect(onodeid(c.result), "rVMapKeys.result") |
614 | } |
615 | func (c *rVMapKeysConstraint) renumber(mapping []nodeid) { |
616 | c.v = mapping[c.v] |
617 | c.result = mapping[c.result] |
618 | } |
619 | |
620 | func (c *rVMapKeysConstraint) String() string { |
621 | return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v) |
622 | } |
623 | |
624 | func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) { |
625 | changed := false |
626 | for _, x := range delta.AppendTo(a.deltaSpace) { |
627 | vObj := nodeid(x) |
628 | tDyn, m, indirect := a.taggedValue(vObj) |
629 | tMap, _ := tDyn.Underlying().(*types.Map) |
630 | if tMap == nil { |
631 | continue // not a map |
632 | } |
633 | if indirect { |
634 | // TODO(adonovan): we'll need to implement this |
635 | // when we start creating indirect tagged objects. |
636 | panic("indirect tagged object") |
637 | } |
638 | |
639 | kObj := a.makeTagged(tMap.Key(), c.cgn, nil) |
640 | a.load(kObj+1, m, 0, a.sizeof(tMap.Key())) |
641 | if a.addLabel(c.result, kObj) { |
642 | changed = true |
643 | } |
644 | } |
645 | if changed { |
646 | a.addWork(c.result) |
647 | } |
648 | } |
649 | |
650 | func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) { |
651 | // Allocate an array for the result. |
652 | obj := a.nextNode() |
653 | T := types.NewSlice(a.reflectValueObj.Type()) |
654 | a.addNodes(sliceToArray(T), "reflect.MapKeys result") |
655 | a.endObject(obj, cgn, nil) |
656 | a.addressOf(T, a.funcResults(cgn.obj), obj) |
657 | |
658 | a.addConstraint(&rVMapKeysConstraint{ |
659 | cgn: cgn, |
660 | v: a.funcParams(cgn.obj), |
661 | result: obj + 1, // result is stored in array elems |
662 | }) |
663 | } |
664 | |
665 | func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
666 | func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
667 | |
668 | // ---------- func (Value).Recv(Value) Value ---------- |
669 | |
670 | // result, _ = v.Recv() |
671 | type rVRecvConstraint struct { |
672 | cgn *cgnode |
673 | v nodeid // (ptr) |
674 | result nodeid // (indirect) |
675 | } |
676 | |
677 | func (c *rVRecvConstraint) ptr() nodeid { return c.v } |
678 | func (c *rVRecvConstraint) presolve(h *hvn) { |
679 | h.markIndirect(onodeid(c.result), "rVRecv.result") |
680 | } |
681 | func (c *rVRecvConstraint) renumber(mapping []nodeid) { |
682 | c.v = mapping[c.v] |
683 | c.result = mapping[c.result] |
684 | } |
685 | |
686 | func (c *rVRecvConstraint) String() string { |
687 | return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v) |
688 | } |
689 | |
690 | func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) { |
691 | changed := false |
692 | for _, x := range delta.AppendTo(a.deltaSpace) { |
693 | vObj := nodeid(x) |
694 | tDyn, ch, indirect := a.taggedValue(vObj) |
695 | tChan, _ := tDyn.Underlying().(*types.Chan) |
696 | if tChan == nil { |
697 | continue // not a channel |
698 | } |
699 | if indirect { |
700 | // TODO(adonovan): we'll need to implement this |
701 | // when we start creating indirect tagged objects. |
702 | panic("indirect tagged object") |
703 | } |
704 | |
705 | tElem := tChan.Elem() |
706 | elemObj := a.makeTagged(tElem, c.cgn, nil) |
707 | a.load(elemObj+1, ch, 0, a.sizeof(tElem)) |
708 | if a.addLabel(c.result, elemObj) { |
709 | changed = true |
710 | } |
711 | } |
712 | if changed { |
713 | a.addWork(c.result) |
714 | } |
715 | } |
716 | |
717 | func ext۰reflect۰Value۰Recv(a *analysis, cgn *cgnode) { |
718 | a.addConstraint(&rVRecvConstraint{ |
719 | cgn: cgn, |
720 | v: a.funcParams(cgn.obj), |
721 | result: a.funcResults(cgn.obj), |
722 | }) |
723 | } |
724 | |
725 | // ---------- func (Value).Send(Value) ---------- |
726 | |
727 | // v.Send(x) |
728 | type rVSendConstraint struct { |
729 | cgn *cgnode |
730 | v nodeid // (ptr) |
731 | x nodeid |
732 | } |
733 | |
734 | func (c *rVSendConstraint) ptr() nodeid { return c.v } |
735 | func (c *rVSendConstraint) presolve(*hvn) {} |
736 | func (c *rVSendConstraint) renumber(mapping []nodeid) { |
737 | c.v = mapping[c.v] |
738 | c.x = mapping[c.x] |
739 | } |
740 | |
741 | func (c *rVSendConstraint) String() string { |
742 | return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x) |
743 | } |
744 | |
745 | func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) { |
746 | for _, x := range delta.AppendTo(a.deltaSpace) { |
747 | vObj := nodeid(x) |
748 | tDyn, ch, indirect := a.taggedValue(vObj) |
749 | tChan, _ := tDyn.Underlying().(*types.Chan) |
750 | if tChan == nil { |
751 | continue // not a channel |
752 | } |
753 | if indirect { |
754 | // TODO(adonovan): we'll need to implement this |
755 | // when we start creating indirect tagged objects. |
756 | panic("indirect tagged object") |
757 | } |
758 | |
759 | // Extract x's payload to xtmp, then store to channel. |
760 | tElem := tChan.Elem() |
761 | xtmp := a.addNodes(tElem, "Send.xtmp") |
762 | a.typeAssert(tElem, xtmp, c.x, false) |
763 | a.store(ch, xtmp, 0, a.sizeof(tElem)) |
764 | } |
765 | } |
766 | |
767 | func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) { |
768 | params := a.funcParams(cgn.obj) |
769 | a.addConstraint(&rVSendConstraint{ |
770 | cgn: cgn, |
771 | v: params, |
772 | x: params + 1, |
773 | }) |
774 | } |
775 | |
776 | func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
777 | |
778 | // ---------- func (Value).SetBytes(x []byte) ---------- |
779 | |
780 | // v.SetBytes(x) |
781 | type rVSetBytesConstraint struct { |
782 | cgn *cgnode |
783 | v nodeid // (ptr) |
784 | x nodeid |
785 | } |
786 | |
787 | func (c *rVSetBytesConstraint) ptr() nodeid { return c.v } |
788 | func (c *rVSetBytesConstraint) presolve(*hvn) {} |
789 | func (c *rVSetBytesConstraint) renumber(mapping []nodeid) { |
790 | c.v = mapping[c.v] |
791 | c.x = mapping[c.x] |
792 | } |
793 | |
794 | func (c *rVSetBytesConstraint) String() string { |
795 | return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x) |
796 | } |
797 | |
798 | func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) { |
799 | for _, x := range delta.AppendTo(a.deltaSpace) { |
800 | vObj := nodeid(x) |
801 | tDyn, slice, indirect := a.taggedValue(vObj) |
802 | if indirect { |
803 | // TODO(adonovan): we'll need to implement this |
804 | // when we start creating indirect tagged objects. |
805 | panic("indirect tagged object") |
806 | } |
807 | |
808 | tSlice, ok := tDyn.Underlying().(*types.Slice) |
809 | if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) { |
810 | if a.onlineCopy(slice, c.x) { |
811 | a.addWork(slice) |
812 | } |
813 | } |
814 | } |
815 | } |
816 | |
817 | func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) { |
818 | params := a.funcParams(cgn.obj) |
819 | a.addConstraint(&rVSetBytesConstraint{ |
820 | cgn: cgn, |
821 | v: params, |
822 | x: params + 1, |
823 | }) |
824 | } |
825 | |
826 | // ---------- func (Value).SetMapIndex(k Value, v Value) ---------- |
827 | |
828 | // v.SetMapIndex(key, val) |
829 | type rVSetMapIndexConstraint struct { |
830 | cgn *cgnode |
831 | v nodeid // (ptr) |
832 | key nodeid |
833 | val nodeid |
834 | } |
835 | |
836 | func (c *rVSetMapIndexConstraint) ptr() nodeid { return c.v } |
837 | func (c *rVSetMapIndexConstraint) presolve(*hvn) {} |
838 | func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) { |
839 | c.v = mapping[c.v] |
840 | c.key = mapping[c.key] |
841 | c.val = mapping[c.val] |
842 | } |
843 | |
844 | func (c *rVSetMapIndexConstraint) String() string { |
845 | return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val) |
846 | } |
847 | |
848 | func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) { |
849 | for _, x := range delta.AppendTo(a.deltaSpace) { |
850 | vObj := nodeid(x) |
851 | tDyn, m, indirect := a.taggedValue(vObj) |
852 | tMap, _ := tDyn.Underlying().(*types.Map) |
853 | if tMap == nil { |
854 | continue // not a map |
855 | } |
856 | if indirect { |
857 | // TODO(adonovan): we'll need to implement this |
858 | // when we start creating indirect tagged objects. |
859 | panic("indirect tagged object") |
860 | } |
861 | |
862 | keysize := a.sizeof(tMap.Key()) |
863 | |
864 | // Extract key's payload to keytmp, then store to map key. |
865 | keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp") |
866 | a.typeAssert(tMap.Key(), keytmp, c.key, false) |
867 | a.store(m, keytmp, 0, keysize) |
868 | |
869 | // Extract val's payload to vtmp, then store to map value. |
870 | valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp") |
871 | a.typeAssert(tMap.Elem(), valtmp, c.val, false) |
872 | a.store(m, valtmp, keysize, a.sizeof(tMap.Elem())) |
873 | } |
874 | } |
875 | |
876 | func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) { |
877 | params := a.funcParams(cgn.obj) |
878 | a.addConstraint(&rVSetMapIndexConstraint{ |
879 | cgn: cgn, |
880 | v: params, |
881 | key: params + 1, |
882 | val: params + 2, |
883 | }) |
884 | } |
885 | |
886 | func ext۰reflect۰Value۰SetPointer(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
887 | |
888 | // ---------- func (Value).Slice(v Value, i, j int) Value ---------- |
889 | |
890 | // result = v.Slice(_, _) |
891 | type rVSliceConstraint struct { |
892 | cgn *cgnode |
893 | v nodeid // (ptr) |
894 | result nodeid // (indirect) |
895 | } |
896 | |
897 | func (c *rVSliceConstraint) ptr() nodeid { return c.v } |
898 | func (c *rVSliceConstraint) presolve(h *hvn) { |
899 | h.markIndirect(onodeid(c.result), "rVSlice.result") |
900 | } |
901 | func (c *rVSliceConstraint) renumber(mapping []nodeid) { |
902 | c.v = mapping[c.v] |
903 | c.result = mapping[c.result] |
904 | } |
905 | |
906 | func (c *rVSliceConstraint) String() string { |
907 | return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v) |
908 | } |
909 | |
910 | func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) { |
911 | changed := false |
912 | for _, x := range delta.AppendTo(a.deltaSpace) { |
913 | vObj := nodeid(x) |
914 | tDyn, payload, indirect := a.taggedValue(vObj) |
915 | if indirect { |
916 | // TODO(adonovan): we'll need to implement this |
917 | // when we start creating indirect tagged objects. |
918 | panic("indirect tagged object") |
919 | } |
920 | |
921 | var res nodeid |
922 | switch t := tDyn.Underlying().(type) { |
923 | case *types.Pointer: |
924 | if tArr, ok := t.Elem().Underlying().(*types.Array); ok { |
925 | // pointer to array |
926 | res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil) |
927 | if a.onlineCopy(res+1, payload) { |
928 | a.addWork(res + 1) |
929 | } |
930 | } |
931 | |
932 | case *types.Array: |
933 | // TODO(adonovan): implement addressable |
934 | // arrays when we do indirect tagged objects. |
935 | |
936 | case *types.Slice: |
937 | res = vObj |
938 | |
939 | case *types.Basic: |
940 | if t == types.Typ[types.String] { |
941 | res = vObj |
942 | } |
943 | } |
944 | |
945 | if res != 0 && a.addLabel(c.result, res) { |
946 | changed = true |
947 | } |
948 | } |
949 | if changed { |
950 | a.addWork(c.result) |
951 | } |
952 | } |
953 | |
954 | func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) { |
955 | a.addConstraint(&rVSliceConstraint{ |
956 | cgn: cgn, |
957 | v: a.funcParams(cgn.obj), |
958 | result: a.funcResults(cgn.obj), |
959 | }) |
960 | } |
961 | |
962 | // -------------------- Standalone reflect functions -------------------- |
963 | |
964 | func ext۰reflect۰Append(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
965 | func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
966 | func ext۰reflect۰Copy(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
967 | |
968 | // ---------- func ChanOf(ChanDir, Type) Type ---------- |
969 | |
970 | // result = ChanOf(dir, t) |
971 | type reflectChanOfConstraint struct { |
972 | cgn *cgnode |
973 | t nodeid // (ptr) |
974 | result nodeid // (indirect) |
975 | dirs []types.ChanDir |
976 | } |
977 | |
978 | func (c *reflectChanOfConstraint) ptr() nodeid { return c.t } |
979 | func (c *reflectChanOfConstraint) presolve(h *hvn) { |
980 | h.markIndirect(onodeid(c.result), "reflectChanOf.result") |
981 | } |
982 | func (c *reflectChanOfConstraint) renumber(mapping []nodeid) { |
983 | c.t = mapping[c.t] |
984 | c.result = mapping[c.result] |
985 | } |
986 | |
987 | func (c *reflectChanOfConstraint) String() string { |
988 | return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t) |
989 | } |
990 | |
991 | func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) { |
992 | changed := false |
993 | for _, x := range delta.AppendTo(a.deltaSpace) { |
994 | tObj := nodeid(x) |
995 | T := a.rtypeTaggedValue(tObj) |
996 | |
997 | if typeTooHigh(T) { |
998 | continue |
999 | } |
1000 | |
1001 | for _, dir := range c.dirs { |
1002 | if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) { |
1003 | changed = true |
1004 | } |
1005 | } |
1006 | } |
1007 | if changed { |
1008 | a.addWork(c.result) |
1009 | } |
1010 | } |
1011 | |
1012 | // dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf. |
1013 | var dirMap = [...][]types.ChanDir{ |
1014 | 0: {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown |
1015 | reflect.RecvDir: {types.RecvOnly}, |
1016 | reflect.SendDir: {types.SendOnly}, |
1017 | reflect.BothDir: {types.SendRecv}, |
1018 | } |
1019 | |
1020 | func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) { |
1021 | // If we have access to the callsite, |
1022 | // and the channel argument is a constant (as is usual), |
1023 | // only generate the requested direction. |
1024 | var dir reflect.ChanDir // unknown |
1025 | if site := cgn.callersite; site != nil { |
1026 | if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
1027 | v := c.Int64() |
1028 | if 0 <= v && v <= int64(reflect.BothDir) { |
1029 | dir = reflect.ChanDir(v) |
1030 | } |
1031 | } |
1032 | } |
1033 | |
1034 | params := a.funcParams(cgn.obj) |
1035 | a.addConstraint(&reflectChanOfConstraint{ |
1036 | cgn: cgn, |
1037 | t: params + 1, |
1038 | result: a.funcResults(cgn.obj), |
1039 | dirs: dirMap[dir], |
1040 | }) |
1041 | } |
1042 | |
1043 | // ---------- func Indirect(v Value) Value ---------- |
1044 | |
1045 | // result = Indirect(v) |
1046 | type reflectIndirectConstraint struct { |
1047 | cgn *cgnode |
1048 | v nodeid // (ptr) |
1049 | result nodeid // (indirect) |
1050 | } |
1051 | |
1052 | func (c *reflectIndirectConstraint) ptr() nodeid { return c.v } |
1053 | func (c *reflectIndirectConstraint) presolve(h *hvn) { |
1054 | h.markIndirect(onodeid(c.result), "reflectIndirect.result") |
1055 | } |
1056 | func (c *reflectIndirectConstraint) renumber(mapping []nodeid) { |
1057 | c.v = mapping[c.v] |
1058 | c.result = mapping[c.result] |
1059 | } |
1060 | |
1061 | func (c *reflectIndirectConstraint) String() string { |
1062 | return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v) |
1063 | } |
1064 | |
1065 | func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) { |
1066 | changed := false |
1067 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1068 | vObj := nodeid(x) |
1069 | tDyn, _, _ := a.taggedValue(vObj) |
1070 | var res nodeid |
1071 | if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok { |
1072 | // load the payload of the pointer's tagged object |
1073 | // into a new tagged object |
1074 | res = a.makeTagged(tPtr.Elem(), c.cgn, nil) |
1075 | a.load(res+1, vObj+1, 0, a.sizeof(tPtr.Elem())) |
1076 | } else { |
1077 | res = vObj |
1078 | } |
1079 | |
1080 | if a.addLabel(c.result, res) { |
1081 | changed = true |
1082 | } |
1083 | } |
1084 | if changed { |
1085 | a.addWork(c.result) |
1086 | } |
1087 | } |
1088 | |
1089 | func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) { |
1090 | a.addConstraint(&reflectIndirectConstraint{ |
1091 | cgn: cgn, |
1092 | v: a.funcParams(cgn.obj), |
1093 | result: a.funcResults(cgn.obj), |
1094 | }) |
1095 | } |
1096 | |
1097 | // ---------- func MakeChan(Type) Value ---------- |
1098 | |
1099 | // result = MakeChan(typ) |
1100 | type reflectMakeChanConstraint struct { |
1101 | cgn *cgnode |
1102 | typ nodeid // (ptr) |
1103 | result nodeid // (indirect) |
1104 | } |
1105 | |
1106 | func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ } |
1107 | func (c *reflectMakeChanConstraint) presolve(h *hvn) { |
1108 | h.markIndirect(onodeid(c.result), "reflectMakeChan.result") |
1109 | } |
1110 | func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) { |
1111 | c.typ = mapping[c.typ] |
1112 | c.result = mapping[c.result] |
1113 | } |
1114 | |
1115 | func (c *reflectMakeChanConstraint) String() string { |
1116 | return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ) |
1117 | } |
1118 | |
1119 | func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) { |
1120 | changed := false |
1121 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1122 | typObj := nodeid(x) |
1123 | T := a.rtypeTaggedValue(typObj) |
1124 | tChan, ok := T.Underlying().(*types.Chan) |
1125 | if !ok || tChan.Dir() != types.SendRecv { |
1126 | continue // not a bidirectional channel type |
1127 | } |
1128 | |
1129 | obj := a.nextNode() |
1130 | a.addNodes(tChan.Elem(), "reflect.MakeChan.value") |
1131 | a.endObject(obj, c.cgn, nil) |
1132 | |
1133 | // put its address in a new T-tagged object |
1134 | id := a.makeTagged(T, c.cgn, nil) |
1135 | a.addLabel(id+1, obj) |
1136 | |
1137 | // flow the T-tagged object to the result |
1138 | if a.addLabel(c.result, id) { |
1139 | changed = true |
1140 | } |
1141 | } |
1142 | if changed { |
1143 | a.addWork(c.result) |
1144 | } |
1145 | } |
1146 | |
1147 | func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) { |
1148 | a.addConstraint(&reflectMakeChanConstraint{ |
1149 | cgn: cgn, |
1150 | typ: a.funcParams(cgn.obj), |
1151 | result: a.funcResults(cgn.obj), |
1152 | }) |
1153 | } |
1154 | |
1155 | func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
1156 | |
1157 | // ---------- func MakeMap(Type) Value ---------- |
1158 | |
1159 | // result = MakeMap(typ) |
1160 | type reflectMakeMapConstraint struct { |
1161 | cgn *cgnode |
1162 | typ nodeid // (ptr) |
1163 | result nodeid // (indirect) |
1164 | } |
1165 | |
1166 | func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ } |
1167 | func (c *reflectMakeMapConstraint) presolve(h *hvn) { |
1168 | h.markIndirect(onodeid(c.result), "reflectMakeMap.result") |
1169 | } |
1170 | func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) { |
1171 | c.typ = mapping[c.typ] |
1172 | c.result = mapping[c.result] |
1173 | } |
1174 | |
1175 | func (c *reflectMakeMapConstraint) String() string { |
1176 | return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ) |
1177 | } |
1178 | |
1179 | func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) { |
1180 | changed := false |
1181 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1182 | typObj := nodeid(x) |
1183 | T := a.rtypeTaggedValue(typObj) |
1184 | tMap, ok := T.Underlying().(*types.Map) |
1185 | if !ok { |
1186 | continue // not a map type |
1187 | } |
1188 | |
1189 | mapObj := a.nextNode() |
1190 | a.addNodes(tMap.Key(), "reflect.MakeMap.key") |
1191 | a.addNodes(tMap.Elem(), "reflect.MakeMap.value") |
1192 | a.endObject(mapObj, c.cgn, nil) |
1193 | |
1194 | // put its address in a new T-tagged object |
1195 | id := a.makeTagged(T, c.cgn, nil) |
1196 | a.addLabel(id+1, mapObj) |
1197 | |
1198 | // flow the T-tagged object to the result |
1199 | if a.addLabel(c.result, id) { |
1200 | changed = true |
1201 | } |
1202 | } |
1203 | if changed { |
1204 | a.addWork(c.result) |
1205 | } |
1206 | } |
1207 | |
1208 | func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) { |
1209 | a.addConstraint(&reflectMakeMapConstraint{ |
1210 | cgn: cgn, |
1211 | typ: a.funcParams(cgn.obj), |
1212 | result: a.funcResults(cgn.obj), |
1213 | }) |
1214 | } |
1215 | |
1216 | // ---------- func MakeSlice(Type) Value ---------- |
1217 | |
1218 | // result = MakeSlice(typ) |
1219 | type reflectMakeSliceConstraint struct { |
1220 | cgn *cgnode |
1221 | typ nodeid // (ptr) |
1222 | result nodeid // (indirect) |
1223 | } |
1224 | |
1225 | func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ } |
1226 | func (c *reflectMakeSliceConstraint) presolve(h *hvn) { |
1227 | h.markIndirect(onodeid(c.result), "reflectMakeSlice.result") |
1228 | } |
1229 | func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) { |
1230 | c.typ = mapping[c.typ] |
1231 | c.result = mapping[c.result] |
1232 | } |
1233 | |
1234 | func (c *reflectMakeSliceConstraint) String() string { |
1235 | return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ) |
1236 | } |
1237 | |
1238 | func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) { |
1239 | changed := false |
1240 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1241 | typObj := nodeid(x) |
1242 | T := a.rtypeTaggedValue(typObj) |
1243 | if _, ok := T.Underlying().(*types.Slice); !ok { |
1244 | continue // not a slice type |
1245 | } |
1246 | |
1247 | obj := a.nextNode() |
1248 | a.addNodes(sliceToArray(T), "reflect.MakeSlice") |
1249 | a.endObject(obj, c.cgn, nil) |
1250 | |
1251 | // put its address in a new T-tagged object |
1252 | id := a.makeTagged(T, c.cgn, nil) |
1253 | a.addLabel(id+1, obj) |
1254 | |
1255 | // flow the T-tagged object to the result |
1256 | if a.addLabel(c.result, id) { |
1257 | changed = true |
1258 | } |
1259 | } |
1260 | if changed { |
1261 | a.addWork(c.result) |
1262 | } |
1263 | } |
1264 | |
1265 | func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) { |
1266 | a.addConstraint(&reflectMakeSliceConstraint{ |
1267 | cgn: cgn, |
1268 | typ: a.funcParams(cgn.obj), |
1269 | result: a.funcResults(cgn.obj), |
1270 | }) |
1271 | } |
1272 | |
1273 | func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
1274 | |
1275 | // ---------- func New(Type) Value ---------- |
1276 | |
1277 | // result = New(typ) |
1278 | type reflectNewConstraint struct { |
1279 | cgn *cgnode |
1280 | typ nodeid // (ptr) |
1281 | result nodeid // (indirect) |
1282 | } |
1283 | |
1284 | func (c *reflectNewConstraint) ptr() nodeid { return c.typ } |
1285 | func (c *reflectNewConstraint) presolve(h *hvn) { |
1286 | h.markIndirect(onodeid(c.result), "reflectNew.result") |
1287 | } |
1288 | func (c *reflectNewConstraint) renumber(mapping []nodeid) { |
1289 | c.typ = mapping[c.typ] |
1290 | c.result = mapping[c.result] |
1291 | } |
1292 | |
1293 | func (c *reflectNewConstraint) String() string { |
1294 | return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ) |
1295 | } |
1296 | |
1297 | func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) { |
1298 | changed := false |
1299 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1300 | typObj := nodeid(x) |
1301 | T := a.rtypeTaggedValue(typObj) |
1302 | |
1303 | // allocate new T object |
1304 | newObj := a.nextNode() |
1305 | a.addNodes(T, "reflect.New") |
1306 | a.endObject(newObj, c.cgn, nil) |
1307 | |
1308 | // put its address in a new *T-tagged object |
1309 | id := a.makeTagged(types.NewPointer(T), c.cgn, nil) |
1310 | a.addLabel(id+1, newObj) |
1311 | |
1312 | // flow the pointer to the result |
1313 | if a.addLabel(c.result, id) { |
1314 | changed = true |
1315 | } |
1316 | } |
1317 | if changed { |
1318 | a.addWork(c.result) |
1319 | } |
1320 | } |
1321 | |
1322 | func ext۰reflect۰New(a *analysis, cgn *cgnode) { |
1323 | a.addConstraint(&reflectNewConstraint{ |
1324 | cgn: cgn, |
1325 | typ: a.funcParams(cgn.obj), |
1326 | result: a.funcResults(cgn.obj), |
1327 | }) |
1328 | } |
1329 | |
1330 | func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) { |
1331 | ext۰reflect۰New(a, cgn) |
1332 | |
1333 | // TODO(adonovan): also report dynamic calls to unsound intrinsics. |
1334 | if site := cgn.callersite; site != nil { |
1335 | a.warnf(site.pos(), "unsound: %s contains a reflect.NewAt() call", site.instr.Parent()) |
1336 | } |
1337 | } |
1338 | |
1339 | // ---------- func PtrTo(Type) Type ---------- |
1340 | |
1341 | // result = PtrTo(t) |
1342 | type reflectPtrToConstraint struct { |
1343 | cgn *cgnode |
1344 | t nodeid // (ptr) |
1345 | result nodeid // (indirect) |
1346 | } |
1347 | |
1348 | func (c *reflectPtrToConstraint) ptr() nodeid { return c.t } |
1349 | func (c *reflectPtrToConstraint) presolve(h *hvn) { |
1350 | h.markIndirect(onodeid(c.result), "reflectPtrTo.result") |
1351 | } |
1352 | func (c *reflectPtrToConstraint) renumber(mapping []nodeid) { |
1353 | c.t = mapping[c.t] |
1354 | c.result = mapping[c.result] |
1355 | } |
1356 | |
1357 | func (c *reflectPtrToConstraint) String() string { |
1358 | return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t) |
1359 | } |
1360 | |
1361 | func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) { |
1362 | changed := false |
1363 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1364 | tObj := nodeid(x) |
1365 | T := a.rtypeTaggedValue(tObj) |
1366 | |
1367 | if typeTooHigh(T) { |
1368 | continue |
1369 | } |
1370 | |
1371 | if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) { |
1372 | changed = true |
1373 | } |
1374 | } |
1375 | if changed { |
1376 | a.addWork(c.result) |
1377 | } |
1378 | } |
1379 | |
1380 | func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) { |
1381 | a.addConstraint(&reflectPtrToConstraint{ |
1382 | cgn: cgn, |
1383 | t: a.funcParams(cgn.obj), |
1384 | result: a.funcResults(cgn.obj), |
1385 | }) |
1386 | } |
1387 | |
1388 | func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
1389 | |
1390 | // ---------- func SliceOf(Type) Type ---------- |
1391 | |
1392 | // result = SliceOf(t) |
1393 | type reflectSliceOfConstraint struct { |
1394 | cgn *cgnode |
1395 | t nodeid // (ptr) |
1396 | result nodeid // (indirect) |
1397 | } |
1398 | |
1399 | func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t } |
1400 | func (c *reflectSliceOfConstraint) presolve(h *hvn) { |
1401 | h.markIndirect(onodeid(c.result), "reflectSliceOf.result") |
1402 | } |
1403 | func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) { |
1404 | c.t = mapping[c.t] |
1405 | c.result = mapping[c.result] |
1406 | } |
1407 | |
1408 | func (c *reflectSliceOfConstraint) String() string { |
1409 | return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t) |
1410 | } |
1411 | |
1412 | func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) { |
1413 | changed := false |
1414 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1415 | tObj := nodeid(x) |
1416 | T := a.rtypeTaggedValue(tObj) |
1417 | |
1418 | if typeTooHigh(T) { |
1419 | continue |
1420 | } |
1421 | |
1422 | if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) { |
1423 | changed = true |
1424 | } |
1425 | } |
1426 | if changed { |
1427 | a.addWork(c.result) |
1428 | } |
1429 | } |
1430 | |
1431 | func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) { |
1432 | a.addConstraint(&reflectSliceOfConstraint{ |
1433 | cgn: cgn, |
1434 | t: a.funcParams(cgn.obj), |
1435 | result: a.funcResults(cgn.obj), |
1436 | }) |
1437 | } |
1438 | |
1439 | // ---------- func TypeOf(v Value) Type ---------- |
1440 | |
1441 | // result = TypeOf(i) |
1442 | type reflectTypeOfConstraint struct { |
1443 | cgn *cgnode |
1444 | i nodeid // (ptr) |
1445 | result nodeid // (indirect) |
1446 | } |
1447 | |
1448 | func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i } |
1449 | func (c *reflectTypeOfConstraint) presolve(h *hvn) { |
1450 | h.markIndirect(onodeid(c.result), "reflectTypeOf.result") |
1451 | } |
1452 | func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) { |
1453 | c.i = mapping[c.i] |
1454 | c.result = mapping[c.result] |
1455 | } |
1456 | |
1457 | func (c *reflectTypeOfConstraint) String() string { |
1458 | return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i) |
1459 | } |
1460 | |
1461 | func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) { |
1462 | changed := false |
1463 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1464 | iObj := nodeid(x) |
1465 | tDyn, _, _ := a.taggedValue(iObj) |
1466 | if a.addLabel(c.result, a.makeRtype(tDyn)) { |
1467 | changed = true |
1468 | } |
1469 | } |
1470 | if changed { |
1471 | a.addWork(c.result) |
1472 | } |
1473 | } |
1474 | |
1475 | func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) { |
1476 | a.addConstraint(&reflectTypeOfConstraint{ |
1477 | cgn: cgn, |
1478 | i: a.funcParams(cgn.obj), |
1479 | result: a.funcResults(cgn.obj), |
1480 | }) |
1481 | } |
1482 | |
1483 | // ---------- func ValueOf(interface{}) Value ---------- |
1484 | |
1485 | func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) { |
1486 | // TODO(adonovan): when we start creating indirect tagged |
1487 | // objects, we'll need to handle them specially here since |
1488 | // they must never appear in the PTS of an interface{}. |
1489 | a.copy(a.funcResults(cgn.obj), a.funcParams(cgn.obj), 1) |
1490 | } |
1491 | |
1492 | // ---------- func Zero(Type) Value ---------- |
1493 | |
1494 | // result = Zero(typ) |
1495 | type reflectZeroConstraint struct { |
1496 | cgn *cgnode |
1497 | typ nodeid // (ptr) |
1498 | result nodeid // (indirect) |
1499 | } |
1500 | |
1501 | func (c *reflectZeroConstraint) ptr() nodeid { return c.typ } |
1502 | func (c *reflectZeroConstraint) presolve(h *hvn) { |
1503 | h.markIndirect(onodeid(c.result), "reflectZero.result") |
1504 | } |
1505 | func (c *reflectZeroConstraint) renumber(mapping []nodeid) { |
1506 | c.typ = mapping[c.typ] |
1507 | c.result = mapping[c.result] |
1508 | } |
1509 | |
1510 | func (c *reflectZeroConstraint) String() string { |
1511 | return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ) |
1512 | } |
1513 | |
1514 | func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) { |
1515 | changed := false |
1516 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1517 | typObj := nodeid(x) |
1518 | T := a.rtypeTaggedValue(typObj) |
1519 | |
1520 | // TODO(adonovan): if T is an interface type, we need |
1521 | // to create an indirect tagged object containing |
1522 | // new(T). To avoid updates of such shared values, |
1523 | // we'll need another flag on indirect tagged objects |
1524 | // that marks whether they are addressable or |
1525 | // readonly, just like the reflect package does. |
1526 | |
1527 | // memoize using a.reflectZeros[T] |
1528 | var id nodeid |
1529 | if z := a.reflectZeros.At(T); false && z != nil { |
1530 | id = z.(nodeid) |
1531 | } else { |
1532 | id = a.makeTagged(T, c.cgn, nil) |
1533 | a.reflectZeros.Set(T, id) |
1534 | } |
1535 | if a.addLabel(c.result, id) { |
1536 | changed = true |
1537 | } |
1538 | } |
1539 | if changed { |
1540 | a.addWork(c.result) |
1541 | } |
1542 | } |
1543 | |
1544 | func ext۰reflect۰Zero(a *analysis, cgn *cgnode) { |
1545 | a.addConstraint(&reflectZeroConstraint{ |
1546 | cgn: cgn, |
1547 | typ: a.funcParams(cgn.obj), |
1548 | result: a.funcResults(cgn.obj), |
1549 | }) |
1550 | } |
1551 | |
1552 | // -------------------- (*reflect.rtype) methods -------------------- |
1553 | |
1554 | // ---------- func (*rtype) Elem() Type ---------- |
1555 | |
1556 | // result = Elem(t) |
1557 | type rtypeElemConstraint struct { |
1558 | cgn *cgnode |
1559 | t nodeid // (ptr) |
1560 | result nodeid // (indirect) |
1561 | } |
1562 | |
1563 | func (c *rtypeElemConstraint) ptr() nodeid { return c.t } |
1564 | func (c *rtypeElemConstraint) presolve(h *hvn) { |
1565 | h.markIndirect(onodeid(c.result), "rtypeElem.result") |
1566 | } |
1567 | func (c *rtypeElemConstraint) renumber(mapping []nodeid) { |
1568 | c.t = mapping[c.t] |
1569 | c.result = mapping[c.result] |
1570 | } |
1571 | |
1572 | func (c *rtypeElemConstraint) String() string { |
1573 | return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t) |
1574 | } |
1575 | |
1576 | func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) { |
1577 | // Implemented by *types.{Map,Chan,Array,Slice,Pointer}. |
1578 | type hasElem interface { |
1579 | Elem() types.Type |
1580 | } |
1581 | changed := false |
1582 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1583 | tObj := nodeid(x) |
1584 | T := a.nodes[tObj].obj.data.(types.Type) |
1585 | if tHasElem, ok := T.Underlying().(hasElem); ok { |
1586 | if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) { |
1587 | changed = true |
1588 | } |
1589 | } |
1590 | } |
1591 | if changed { |
1592 | a.addWork(c.result) |
1593 | } |
1594 | } |
1595 | |
1596 | func ext۰reflect۰rtype۰Elem(a *analysis, cgn *cgnode) { |
1597 | a.addConstraint(&rtypeElemConstraint{ |
1598 | cgn: cgn, |
1599 | t: a.funcParams(cgn.obj), |
1600 | result: a.funcResults(cgn.obj), |
1601 | }) |
1602 | } |
1603 | |
1604 | // ---------- func (*rtype) Field(int) StructField ---------- |
1605 | // ---------- func (*rtype) FieldByName(string) (StructField, bool) ---------- |
1606 | |
1607 | // result = FieldByName(t, name) |
1608 | // result = Field(t, _) |
1609 | type rtypeFieldByNameConstraint struct { |
1610 | cgn *cgnode |
1611 | name string // name of field; "" for unknown |
1612 | t nodeid // (ptr) |
1613 | result nodeid // (indirect) |
1614 | } |
1615 | |
1616 | func (c *rtypeFieldByNameConstraint) ptr() nodeid { return c.t } |
1617 | func (c *rtypeFieldByNameConstraint) presolve(h *hvn) { |
1618 | h.markIndirect(onodeid(c.result+3), "rtypeFieldByName.result.Type") |
1619 | } |
1620 | func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) { |
1621 | c.t = mapping[c.t] |
1622 | c.result = mapping[c.result] |
1623 | } |
1624 | |
1625 | func (c *rtypeFieldByNameConstraint) String() string { |
1626 | return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name) |
1627 | } |
1628 | |
1629 | func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) { |
1630 | // type StructField struct { |
1631 | // 0 __identity__ |
1632 | // 1 Name string |
1633 | // 2 PkgPath string |
1634 | // 3 Type Type |
1635 | // 4 Tag StructTag |
1636 | // 5 Offset uintptr |
1637 | // 6 Index []int |
1638 | // 7 Anonymous bool |
1639 | // } |
1640 | |
1641 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1642 | tObj := nodeid(x) |
1643 | T := a.nodes[tObj].obj.data.(types.Type) |
1644 | tStruct, ok := T.Underlying().(*types.Struct) |
1645 | if !ok { |
1646 | continue // not a struct type |
1647 | } |
1648 | |
1649 | n := tStruct.NumFields() |
1650 | for i := 0; i < n; i++ { |
1651 | f := tStruct.Field(i) |
1652 | if c.name == "" || c.name == f.Name() { |
1653 | |
1654 | // a.offsetOf(Type) is 3. |
1655 | if id := c.result + 3; a.addLabel(id, a.makeRtype(f.Type())) { |
1656 | a.addWork(id) |
1657 | } |
1658 | // TODO(adonovan): StructField.Index should be non-nil. |
1659 | } |
1660 | } |
1661 | } |
1662 | } |
1663 | |
1664 | func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) { |
1665 | // If we have access to the callsite, |
1666 | // and the argument is a string constant, |
1667 | // return only that field. |
1668 | var name string |
1669 | if site := cgn.callersite; site != nil { |
1670 | if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
1671 | name = constant.StringVal(c.Value) |
1672 | } |
1673 | } |
1674 | |
1675 | a.addConstraint(&rtypeFieldByNameConstraint{ |
1676 | cgn: cgn, |
1677 | name: name, |
1678 | t: a.funcParams(cgn.obj), |
1679 | result: a.funcResults(cgn.obj), |
1680 | }) |
1681 | } |
1682 | |
1683 | func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) { |
1684 | // No-one ever calls Field with a constant argument, |
1685 | // so we don't specialize that case. |
1686 | a.addConstraint(&rtypeFieldByNameConstraint{ |
1687 | cgn: cgn, |
1688 | t: a.funcParams(cgn.obj), |
1689 | result: a.funcResults(cgn.obj), |
1690 | }) |
1691 | } |
1692 | |
1693 | func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
1694 | func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
1695 | |
1696 | // ---------- func (*rtype) In/Out(i int) Type ---------- |
1697 | |
1698 | // result = In/Out(t, i) |
1699 | type rtypeInOutConstraint struct { |
1700 | cgn *cgnode |
1701 | t nodeid // (ptr) |
1702 | result nodeid // (indirect) |
1703 | out bool |
1704 | i int // -ve if not a constant |
1705 | } |
1706 | |
1707 | func (c *rtypeInOutConstraint) ptr() nodeid { return c.t } |
1708 | func (c *rtypeInOutConstraint) presolve(h *hvn) { |
1709 | h.markIndirect(onodeid(c.result), "rtypeInOut.result") |
1710 | } |
1711 | func (c *rtypeInOutConstraint) renumber(mapping []nodeid) { |
1712 | c.t = mapping[c.t] |
1713 | c.result = mapping[c.result] |
1714 | } |
1715 | |
1716 | func (c *rtypeInOutConstraint) String() string { |
1717 | return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i) |
1718 | } |
1719 | |
1720 | func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) { |
1721 | changed := false |
1722 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1723 | tObj := nodeid(x) |
1724 | T := a.nodes[tObj].obj.data.(types.Type) |
1725 | sig, ok := T.Underlying().(*types.Signature) |
1726 | if !ok { |
1727 | continue // not a func type |
1728 | } |
1729 | |
1730 | tuple := sig.Params() |
1731 | if c.out { |
1732 | tuple = sig.Results() |
1733 | } |
1734 | for i, n := 0, tuple.Len(); i < n; i++ { |
1735 | if c.i < 0 || c.i == i { |
1736 | if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) { |
1737 | changed = true |
1738 | } |
1739 | } |
1740 | } |
1741 | } |
1742 | if changed { |
1743 | a.addWork(c.result) |
1744 | } |
1745 | } |
1746 | |
1747 | func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) { |
1748 | // If we have access to the callsite, |
1749 | // and the argument is an int constant, |
1750 | // return only that parameter. |
1751 | index := -1 |
1752 | if site := cgn.callersite; site != nil { |
1753 | if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
1754 | index = int(c.Int64()) |
1755 | } |
1756 | } |
1757 | a.addConstraint(&rtypeInOutConstraint{ |
1758 | cgn: cgn, |
1759 | t: a.funcParams(cgn.obj), |
1760 | result: a.funcResults(cgn.obj), |
1761 | out: out, |
1762 | i: index, |
1763 | }) |
1764 | } |
1765 | |
1766 | func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) { |
1767 | ext۰reflect۰rtype۰InOut(a, cgn, false) |
1768 | } |
1769 | |
1770 | func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) { |
1771 | ext۰reflect۰rtype۰InOut(a, cgn, true) |
1772 | } |
1773 | |
1774 | // ---------- func (*rtype) Key() Type ---------- |
1775 | |
1776 | // result = Key(t) |
1777 | type rtypeKeyConstraint struct { |
1778 | cgn *cgnode |
1779 | t nodeid // (ptr) |
1780 | result nodeid // (indirect) |
1781 | } |
1782 | |
1783 | func (c *rtypeKeyConstraint) ptr() nodeid { return c.t } |
1784 | func (c *rtypeKeyConstraint) presolve(h *hvn) { |
1785 | h.markIndirect(onodeid(c.result), "rtypeKey.result") |
1786 | } |
1787 | func (c *rtypeKeyConstraint) renumber(mapping []nodeid) { |
1788 | c.t = mapping[c.t] |
1789 | c.result = mapping[c.result] |
1790 | } |
1791 | |
1792 | func (c *rtypeKeyConstraint) String() string { |
1793 | return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t) |
1794 | } |
1795 | |
1796 | func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) { |
1797 | changed := false |
1798 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1799 | tObj := nodeid(x) |
1800 | T := a.nodes[tObj].obj.data.(types.Type) |
1801 | if tMap, ok := T.Underlying().(*types.Map); ok { |
1802 | if a.addLabel(c.result, a.makeRtype(tMap.Key())) { |
1803 | changed = true |
1804 | } |
1805 | } |
1806 | } |
1807 | if changed { |
1808 | a.addWork(c.result) |
1809 | } |
1810 | } |
1811 | |
1812 | func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) { |
1813 | a.addConstraint(&rtypeKeyConstraint{ |
1814 | cgn: cgn, |
1815 | t: a.funcParams(cgn.obj), |
1816 | result: a.funcResults(cgn.obj), |
1817 | }) |
1818 | } |
1819 | |
1820 | // ---------- func (*rtype) Method(int) (Method, bool) ---------- |
1821 | // ---------- func (*rtype) MethodByName(string) (Method, bool) ---------- |
1822 | |
1823 | // result = MethodByName(t, name) |
1824 | // result = Method(t, _) |
1825 | type rtypeMethodByNameConstraint struct { |
1826 | cgn *cgnode |
1827 | name string // name of method; "" for unknown |
1828 | t nodeid // (ptr) |
1829 | result nodeid // (indirect) |
1830 | } |
1831 | |
1832 | func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t } |
1833 | func (c *rtypeMethodByNameConstraint) presolve(h *hvn) { |
1834 | h.markIndirect(onodeid(c.result+3), "rtypeMethodByName.result.Type") |
1835 | h.markIndirect(onodeid(c.result+4), "rtypeMethodByName.result.Func") |
1836 | } |
1837 | func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) { |
1838 | c.t = mapping[c.t] |
1839 | c.result = mapping[c.result] |
1840 | } |
1841 | |
1842 | func (c *rtypeMethodByNameConstraint) String() string { |
1843 | return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name) |
1844 | } |
1845 | |
1846 | // changeRecv returns sig with Recv prepended to Params(). |
1847 | func changeRecv(sig *types.Signature) *types.Signature { |
1848 | params := sig.Params() |
1849 | n := params.Len() |
1850 | p2 := make([]*types.Var, n+1) |
1851 | p2[0] = sig.Recv() |
1852 | for i := 0; i < n; i++ { |
1853 | p2[i+1] = params.At(i) |
1854 | } |
1855 | return types.NewSignature(nil, types.NewTuple(p2...), sig.Results(), sig.Variadic()) |
1856 | } |
1857 | |
1858 | func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) { |
1859 | for _, x := range delta.AppendTo(a.deltaSpace) { |
1860 | tObj := nodeid(x) |
1861 | T := a.nodes[tObj].obj.data.(types.Type) |
1862 | |
1863 | isIface := isInterface(T) |
1864 | |
1865 | // We don't use Lookup(c.name) when c.name != "" to avoid |
1866 | // ambiguity: >1 unexported methods could match. |
1867 | mset := a.prog.MethodSets.MethodSet(T) |
1868 | for i, n := 0, mset.Len(); i < n; i++ { |
1869 | sel := mset.At(i) |
1870 | if c.name == "" || c.name == sel.Obj().Name() { |
1871 | // type Method struct { |
1872 | // 0 __identity__ |
1873 | // 1 Name string |
1874 | // 2 PkgPath string |
1875 | // 3 Type Type |
1876 | // 4 Func Value |
1877 | // 5 Index int |
1878 | // } |
1879 | |
1880 | var sig *types.Signature |
1881 | var fn *ssa.Function |
1882 | if isIface { |
1883 | sig = sel.Type().(*types.Signature) |
1884 | } else { |
1885 | fn = a.prog.MethodValue(sel) |
1886 | // move receiver to params[0] |
1887 | sig = changeRecv(fn.Signature) |
1888 | } |
1889 | |
1890 | // a.offsetOf(Type) is 3. |
1891 | if id := c.result + 3; a.addLabel(id, a.makeRtype(sig)) { |
1892 | a.addWork(id) |
1893 | } |
1894 | if fn != nil { |
1895 | // a.offsetOf(Func) is 4. |
1896 | if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) { |
1897 | a.addWork(id) |
1898 | } |
1899 | } |
1900 | } |
1901 | } |
1902 | } |
1903 | } |
1904 | |
1905 | func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) { |
1906 | // If we have access to the callsite, |
1907 | // and the argument is a string constant, |
1908 | // return only that method. |
1909 | var name string |
1910 | if site := cgn.callersite; site != nil { |
1911 | if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
1912 | name = constant.StringVal(c.Value) |
1913 | } |
1914 | } |
1915 | |
1916 | a.addConstraint(&rtypeMethodByNameConstraint{ |
1917 | cgn: cgn, |
1918 | name: name, |
1919 | t: a.funcParams(cgn.obj), |
1920 | result: a.funcResults(cgn.obj), |
1921 | }) |
1922 | } |
1923 | |
1924 | func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) { |
1925 | // No-one ever calls Method with a constant argument, |
1926 | // so we don't specialize that case. |
1927 | a.addConstraint(&rtypeMethodByNameConstraint{ |
1928 | cgn: cgn, |
1929 | t: a.funcParams(cgn.obj), |
1930 | result: a.funcResults(cgn.obj), |
1931 | }) |
1932 | } |
1933 | |
1934 | // typeHeight returns the "height" of the type, which is roughly |
1935 | // speaking the number of chan, map, pointer and slice type constructors |
1936 | // at the root of T; these are the four type kinds that can be created |
1937 | // via reflection. Chan and map constructors are counted as double the |
1938 | // height of slice and pointer constructors since they are less often |
1939 | // deeply nested. |
1940 | // |
1941 | // The solver rules for type constructors must somehow bound the set of |
1942 | // types they create to ensure termination of the algorithm in cases |
1943 | // where the output of a type constructor flows to its input, e.g. |
1944 | // |
1945 | // func f(t reflect.Type) { |
1946 | // f(reflect.PtrTo(t)) |
1947 | // } |
1948 | // |
1949 | // It does this by limiting the type height to k, but this still leaves |
1950 | // a potentially exponential (4^k) number of of types that may be |
1951 | // enumerated in pathological cases. |
1952 | func typeHeight(T types.Type) int { |
1953 | switch T := T.(type) { |
1954 | case *types.Chan: |
1955 | return 2 + typeHeight(T.Elem()) |
1956 | case *types.Map: |
1957 | k := typeHeight(T.Key()) |
1958 | v := typeHeight(T.Elem()) |
1959 | if v > k { |
1960 | k = v // max(k, v) |
1961 | } |
1962 | return 2 + k |
1963 | case *types.Slice: |
1964 | return 1 + typeHeight(T.Elem()) |
1965 | case *types.Pointer: |
1966 | return 1 + typeHeight(T.Elem()) |
1967 | } |
1968 | return 0 |
1969 | } |
1970 | |
1971 | func typeTooHigh(T types.Type) bool { |
1972 | return typeHeight(T) > 3 |
1973 | } |
1974 |
Members