| package pointer |
| |
| // This file implements the generation and resolution rules for |
| // constraints arising from the use of reflection in the target |
| // program. See doc.go for explanation of the representation. |
| // |
| // For consistency, the names of all parameters match those of the |
| // actual functions in the "reflect" package. |
| // |
| // TODO(adonovan): fix: most of the reflect API permits implicit |
| // conversions due to assignability, e.g. m.MapIndex(k) is ok if T(k) |
| // is assignable to T(M).key. It's not yet clear how best to model |
| // that; perhaps a more lenient version of typeAssertConstraint is |
| // needed. |
| // |
| // To avoid proliferation of equivalent labels, instrinsics should |
| // memoize as much as possible, like TypeOf and Zero do for their |
| // tagged objects. |
| // |
| // TODO(adonovan): all {} functions are TODO. |
| |
| import ( |
| "fmt" |
| "go/ast" |
| |
| "code.google.com/p/go.tools/go/types" |
| ) |
| |
| // -------------------- (reflect.Value) -------------------- |
| |
| func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func (Value).Interface() Value ---------- |
| |
| // result = v.Interface() |
| type rVInterfaceConstraint struct { |
| v nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *rVInterfaceConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v) |
| } |
| |
| func (c *rVInterfaceConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| resultPts := &a.nodes[c.result].pts |
| changed := false |
| for vObj := range delta { |
| tDyn, _, indirect := a.taggedValue(vObj) |
| if tDyn == nil { |
| panic("not a tagged object") |
| } |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| if resultPts.add(vObj) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVInterfaceConstraint{ |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func (Value).MapIndex(Value) Value ---------- |
| |
| // result = v.MapIndex(_) |
| type rVMapIndexConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *rVMapIndexConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v) |
| } |
| |
| func (c *rVMapIndexConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for vObj := range delta { |
| tDyn, m, indirect := a.taggedValue(vObj) |
| tMap, _ := tDyn.Underlying().(*types.Map) |
| if tMap == nil { |
| continue // not a map |
| } |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| obj := a.makeTagged(tMap.Elem(), c.cgn, nil) |
| a.load(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem())) |
| if a.addLabel(c.result, obj) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVMapIndexConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func (Value).MapKeys() []Value ---------- |
| |
| // result = v.MapKeys() |
| type rVMapKeysConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *rVMapKeysConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v) |
| } |
| |
| func (c *rVMapKeysConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for vObj := range delta { |
| tDyn, m, indirect := a.taggedValue(vObj) |
| tMap, _ := tDyn.Underlying().(*types.Map) |
| if tMap == nil { |
| continue // not a map |
| } |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| kObj := a.makeTagged(tMap.Key(), c.cgn, nil) |
| a.load(kObj+1, m, 0, a.sizeof(tMap.Key())) |
| if a.addLabel(c.result, kObj) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) { |
| // Allocate an array for the result. |
| obj := a.nextNode() |
| a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "reflect.MapKeys result") |
| a.endObject(obj, cgn, nil) |
| a.addressOf(a.funcResults(cgn.obj), obj) |
| |
| a.addConstraint(&rVMapKeysConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: obj + 1, // result is stored in array elems |
| }) |
| } |
| |
| func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func (Value).Recv(Value) ---------- |
| |
| // result, _ = v.Recv() |
| type rVRecvConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *rVRecvConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v) |
| } |
| |
| func (c *rVRecvConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *rVRecvConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for vObj := range delta { |
| tDyn, ch, indirect := a.taggedValue(vObj) |
| tChan, _ := tDyn.Underlying().(*types.Chan) |
| if tChan == nil { |
| continue // not a channel |
| } |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| tElem := tChan.Elem() |
| elemObj := a.makeTagged(tElem, c.cgn, nil) |
| a.load(elemObj+1, ch, 0, a.sizeof(tElem)) |
| if a.addLabel(c.result, elemObj) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Recv(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVRecvConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func (Value).Send(Value) ---------- |
| |
| // v.Send(x) |
| type rVSendConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| x nodeid |
| } |
| |
| func (c *rVSendConstraint) String() string { |
| return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x) |
| } |
| |
| func (c *rVSendConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| for vObj := range delta { |
| tDyn, ch, indirect := a.taggedValue(vObj) |
| tChan, _ := tDyn.Underlying().(*types.Chan) |
| if tChan == nil { |
| continue // not a channel |
| } |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| // Extract x's payload to xtmp, then store to channel. |
| tElem := tChan.Elem() |
| xtmp := a.addNodes(tElem, "Send.xtmp") |
| a.typeAssert(tElem, xtmp, c.x) |
| a.store(ch, xtmp, 0, a.sizeof(tElem)) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) { |
| params := a.funcParams(cgn.obj) |
| a.addConstraint(&rVSendConstraint{ |
| cgn: cgn, |
| v: params, |
| x: params + 1, |
| }) |
| } |
| |
| func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func (Value).SetMapIndex(k Value, v Value) ---------- |
| |
| // v.SetMapIndex(key, val) |
| type rVSetMapIndexConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| key nodeid |
| val nodeid |
| } |
| |
| func (c *rVSetMapIndexConstraint) String() string { |
| return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val) |
| } |
| |
| func (c *rVSetMapIndexConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| for vObj := range delta { |
| tDyn, m, indirect := a.taggedValue(vObj) |
| tMap, _ := tDyn.Underlying().(*types.Map) |
| if tMap == nil { |
| continue // not a map |
| } |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| keysize := a.sizeof(tMap.Key()) |
| |
| // Extract key's payload to keytmp, then store to map key. |
| keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp") |
| a.typeAssert(tMap.Key(), keytmp, c.key) |
| a.store(m, keytmp, 0, keysize) |
| |
| // Extract val's payload to vtmp, then store to map value. |
| valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp") |
| a.typeAssert(tMap.Elem(), valtmp, c.val) |
| a.store(m, valtmp, keysize, a.sizeof(tMap.Elem())) |
| } |
| } |
| |
| func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) { |
| params := a.funcParams(cgn.obj) |
| a.addConstraint(&rVSetMapIndexConstraint{ |
| cgn: cgn, |
| v: params, |
| key: params + 1, |
| val: params + 2, |
| }) |
| } |
| |
| func ext۰reflect۰Value۰SetPointer(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) {} |
| |
| // -------------------- Standalone reflect functions -------------------- |
| |
| func ext۰reflect۰Append(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Copy(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func ChanOf(ChanDir, Type) Type ---------- |
| |
| // result = ChanOf(_, t) |
| type reflectChanOfConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectChanOfConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t) |
| } |
| |
| func (c *reflectChanOfConstraint) ptr() nodeid { |
| return c.t |
| } |
| |
| func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for tObj := range delta { |
| T := a.rtypeTaggedValue(tObj) |
| // TODO(adonovan): use only the channel direction |
| // provided at the callsite, if constant. |
| for _, dir := range []ast.ChanDir{1, 2, 3} { |
| if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) { |
| params := a.funcParams(cgn.obj) |
| a.addConstraint(&reflectChanOfConstraint{ |
| cgn: cgn, |
| t: params + 1, |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func Indirect(v Value) Value ---------- |
| |
| // result = Indirect(v) |
| type reflectIndirectConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectIndirectConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v) |
| } |
| |
| func (c *reflectIndirectConstraint) ptr() nodeid { |
| return c.v |
| } |
| |
| func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for vObj := range delta { |
| tDyn, _, _ := a.taggedValue(vObj) |
| if tDyn == nil { |
| panic("not a tagged value") |
| } |
| |
| var res nodeid |
| if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok { |
| // load the payload of the pointer's tagged object |
| // into a new tagged object |
| res = a.makeTagged(tPtr.Elem(), c.cgn, nil) |
| a.load(res+1, vObj+1, 0, a.sizeof(tPtr.Elem())) |
| } else { |
| res = vObj |
| } |
| |
| if a.addLabel(c.result, res) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectIndirectConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func MakeChan(Type) Value ---------- |
| |
| // result = MakeChan(typ) |
| type reflectMakeChanConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectMakeChanConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectMakeChanConstraint) ptr() nodeid { |
| return c.typ |
| } |
| |
| func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for typObj := range delta { |
| T := a.rtypeTaggedValue(typObj) |
| tChan, ok := T.Underlying().(*types.Chan) |
| if !ok || tChan.Dir() != ast.SEND|ast.RECV { |
| continue // not a bidirectional channel type |
| } |
| |
| obj := a.nextNode() |
| a.addNodes(tChan.Elem(), "reflect.MakeChan.value") |
| a.endObject(obj, c.cgn, nil) |
| |
| // put its address in a new T-tagged object |
| id := a.makeTagged(T, c.cgn, nil) |
| a.addLabel(id+1, obj) |
| |
| // flow the T-tagged object to the result |
| if a.addLabel(c.result, id) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectMakeChanConstraint{ |
| cgn: cgn, |
| typ: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func MakeMap(Type) Value ---------- |
| |
| // result = MakeMap(typ) |
| type reflectMakeMapConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectMakeMapConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectMakeMapConstraint) ptr() nodeid { |
| return c.typ |
| } |
| |
| func (c *reflectMakeMapConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for typObj := range delta { |
| T := a.rtypeTaggedValue(typObj) |
| tMap, ok := T.Underlying().(*types.Map) |
| if !ok { |
| continue // not a map type |
| } |
| |
| mapObj := a.nextNode() |
| a.addNodes(tMap.Key(), "reflect.MakeMap.key") |
| a.addNodes(tMap.Elem(), "reflect.MakeMap.value") |
| a.endObject(mapObj, c.cgn, nil) |
| |
| // put its address in a new T-tagged object |
| id := a.makeTagged(T, c.cgn, nil) |
| a.addLabel(id+1, mapObj) |
| |
| // flow the T-tagged object to the result |
| if a.addLabel(c.result, id) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectMakeMapConstraint{ |
| cgn: cgn, |
| typ: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func New(Type) Value ---------- |
| |
| // result = New(typ) |
| type reflectNewConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectNewConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectNewConstraint) ptr() nodeid { |
| return c.typ |
| } |
| |
| func (c *reflectNewConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for typObj := range delta { |
| T := a.rtypeTaggedValue(typObj) |
| |
| // allocate new T object |
| newObj := a.nextNode() |
| a.addNodes(T, "reflect.New") |
| a.endObject(newObj, c.cgn, nil) |
| |
| // put its address in a new *T-tagged object |
| id := a.makeTagged(types.NewPointer(T), c.cgn, nil) |
| a.addLabel(id+1, newObj) |
| |
| // flow the pointer to the result |
| if a.addLabel(c.result, id) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰New(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectNewConstraint{ |
| cgn: cgn, |
| typ: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) { |
| ext۰reflect۰New(a, cgn) |
| |
| // TODO(adonovan): make it easier to report errors of this form, |
| // which includes the callsite: |
| // a.warnf("unsound: main.reflectNewAt contains a reflect.NewAt() call") |
| a.warnf(cgn.Func().Pos(), "unsound: reflect.NewAt() call") |
| } |
| |
| func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func TypeOf(v Value) Type ---------- |
| |
| // result = TypeOf(i) |
| type reflectTypeOfConstraint struct { |
| cgn *cgnode |
| i nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectTypeOfConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i) |
| } |
| |
| func (c *reflectTypeOfConstraint) ptr() nodeid { |
| return c.i |
| } |
| |
| func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for iObj := range delta { |
| tDyn, _, _ := a.taggedValue(iObj) |
| if tDyn == nil { |
| panic("not a tagged value") |
| } |
| |
| if a.addLabel(c.result, a.makeRtype(tDyn)) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectTypeOfConstraint{ |
| cgn: cgn, |
| i: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func ValueOf(interface{}) Value ---------- |
| |
| func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) { |
| // TODO(adonovan): when we start creating indirect tagged |
| // objects, we'll need to handle them specially here since |
| // they must never appear in the PTS of an interface{}. |
| a.copy(a.funcResults(cgn.obj), a.funcParams(cgn.obj), 1) |
| } |
| |
| // ---------- func Zero(Type) Value ---------- |
| |
| // result = Zero(typ) |
| type reflectZeroConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *reflectZeroConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectZeroConstraint) ptr() nodeid { |
| return c.typ |
| } |
| |
| func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for typObj := range delta { |
| T := a.rtypeTaggedValue(typObj) |
| |
| // memoize using a.reflectZeros[T] |
| var id nodeid |
| if z := a.reflectZeros.At(T); false && z != nil { |
| id = z.(nodeid) |
| } else { |
| id = a.makeTagged(T, c.cgn, nil) |
| a.reflectZeros.Set(T, id) |
| } |
| if a.addLabel(c.result, id) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Zero(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectZeroConstraint{ |
| cgn: cgn, |
| typ: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // -------------------- (*reflect.rtype) methods -------------------- |
| |
| // ---------- func (*rtype) Elem() Type ---------- |
| |
| // result = Elem(t) |
| type rtypeElemConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *rtypeElemConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t) |
| } |
| |
| func (c *rtypeElemConstraint) ptr() nodeid { |
| return c.t |
| } |
| |
| func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| // Implemented by *types.{Map,Chan,Array,Slice,Pointer}. |
| type hasElem interface { |
| Elem() types.Type |
| } |
| changed := false |
| for tObj := range delta { |
| T := a.nodes[tObj].obj.rtype |
| if tHasElem, ok := T.Underlying().(hasElem); ok { |
| if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰rtype۰Elem(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rtypeElemConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} |
| |
| // ---------- func (*rtype) In/Out() Type ---------- |
| |
| // result = In/Out(t) |
| type rtypeInOutConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid |
| out bool |
| } |
| |
| func (c *rtypeInOutConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d)", c.result, c.t) |
| } |
| |
| func (c *rtypeInOutConstraint) ptr() nodeid { |
| return c.t |
| } |
| |
| func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for tObj := range delta { |
| T := a.nodes[tObj].obj.rtype |
| sig, ok := T.Underlying().(*types.Signature) |
| if !ok { |
| continue // not a func type |
| } |
| |
| tuple := sig.Params() |
| if c.out { |
| tuple = sig.Results() |
| } |
| // TODO(adonovan): when a function is analyzed |
| // context-sensitively, we should be able to see its |
| // caller's actual parameter's ssa.Values. Refactor |
| // the intrinsic mechanism to allow this. Then if the |
| // value is an int const K, skip the loop and use |
| // tuple.At(K). |
| for i, n := 0, tuple.Len(); i < n; i++ { |
| if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) { |
| a.addConstraint(&rtypeInOutConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| out: out, |
| }) |
| } |
| |
| func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) { |
| ext۰reflect۰rtype۰InOut(a, cgn, false) |
| } |
| |
| func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) { |
| ext۰reflect۰rtype۰InOut(a, cgn, true) |
| } |
| |
| // ---------- func (*rtype) Key() Type ---------- |
| |
| // result = Key(t) |
| type rtypeKeyConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid |
| } |
| |
| func (c *rtypeKeyConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t) |
| } |
| |
| func (c *rtypeKeyConstraint) ptr() nodeid { |
| return c.t |
| } |
| |
| func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) { |
| changed := false |
| for tObj := range delta { |
| T := a.nodes[tObj].obj.rtype |
| if tMap, ok := T.Underlying().(*types.Map); ok { |
| if a.addLabel(c.result, a.makeRtype(tMap.Key())) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rtypeKeyConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) {} |
| func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {} |