| 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. |
| // |
| // To avoid proliferation of equivalent labels, intrinsics should |
| // memoize as much as possible, like TypeOf and Zero do for their |
| // tagged objects. |
| // |
| // TODO(adonovan): this file is rather subtle. Explain how we derive |
| // the implementation of each reflect operator from its spec, |
| // including the subtleties of reflect.flag{Addr,RO,Indir}. |
| // [Hint: our implementation is as if reflect.flagIndir was always |
| // true, i.e. reflect.Values are pointers to tagged objects, there is |
| // no inline allocation optimization; and indirect tagged objects (not |
| // yet implemented) correspond to reflect.Values with |
| // reflect.flagAddr.] |
| // A picture would help too. |
| // |
| // TODO(adonovan): try factoring up the common parts of the majority of |
| // these constraints that are single input, single output. |
| |
| import ( |
| "fmt" |
| "reflect" |
| |
| "golang.org/x/tools/go/exact" |
| "golang.org/x/tools/go/ssa" |
| "golang.org/x/tools/go/types" |
| ) |
| |
| func init() { |
| for name, fn := range map[string]intrinsic{ |
| // reflect.Value methods. |
| "(reflect.Value).Addr": ext۰reflect۰Value۰Addr, |
| "(reflect.Value).Bool": ext۰NoEffect, |
| "(reflect.Value).Bytes": ext۰reflect۰Value۰Bytes, |
| "(reflect.Value).Call": ext۰reflect۰Value۰Call, |
| "(reflect.Value).CallSlice": ext۰reflect۰Value۰CallSlice, |
| "(reflect.Value).CanAddr": ext۰NoEffect, |
| "(reflect.Value).CanInterface": ext۰NoEffect, |
| "(reflect.Value).CanSet": ext۰NoEffect, |
| "(reflect.Value).Cap": ext۰NoEffect, |
| "(reflect.Value).Close": ext۰NoEffect, |
| "(reflect.Value).Complex": ext۰NoEffect, |
| "(reflect.Value).Convert": ext۰reflect۰Value۰Convert, |
| "(reflect.Value).Elem": ext۰reflect۰Value۰Elem, |
| "(reflect.Value).Field": ext۰reflect۰Value۰Field, |
| "(reflect.Value).FieldByIndex": ext۰reflect۰Value۰FieldByIndex, |
| "(reflect.Value).FieldByName": ext۰reflect۰Value۰FieldByName, |
| "(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc, |
| "(reflect.Value).Float": ext۰NoEffect, |
| "(reflect.Value).Index": ext۰reflect۰Value۰Index, |
| "(reflect.Value).Int": ext۰NoEffect, |
| "(reflect.Value).Interface": ext۰reflect۰Value۰Interface, |
| "(reflect.Value).InterfaceData": ext۰NoEffect, |
| "(reflect.Value).IsNil": ext۰NoEffect, |
| "(reflect.Value).IsValid": ext۰NoEffect, |
| "(reflect.Value).Kind": ext۰NoEffect, |
| "(reflect.Value).Len": ext۰NoEffect, |
| "(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex, |
| "(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys, |
| "(reflect.Value).Method": ext۰reflect۰Value۰Method, |
| "(reflect.Value).MethodByName": ext۰reflect۰Value۰MethodByName, |
| "(reflect.Value).NumField": ext۰NoEffect, |
| "(reflect.Value).NumMethod": ext۰NoEffect, |
| "(reflect.Value).OverflowComplex": ext۰NoEffect, |
| "(reflect.Value).OverflowFloat": ext۰NoEffect, |
| "(reflect.Value).OverflowInt": ext۰NoEffect, |
| "(reflect.Value).OverflowUint": ext۰NoEffect, |
| "(reflect.Value).Pointer": ext۰NoEffect, |
| "(reflect.Value).Recv": ext۰reflect۰Value۰Recv, |
| "(reflect.Value).Send": ext۰reflect۰Value۰Send, |
| "(reflect.Value).Set": ext۰reflect۰Value۰Set, |
| "(reflect.Value).SetBool": ext۰NoEffect, |
| "(reflect.Value).SetBytes": ext۰reflect۰Value۰SetBytes, |
| "(reflect.Value).SetComplex": ext۰NoEffect, |
| "(reflect.Value).SetFloat": ext۰NoEffect, |
| "(reflect.Value).SetInt": ext۰NoEffect, |
| "(reflect.Value).SetLen": ext۰NoEffect, |
| "(reflect.Value).SetMapIndex": ext۰reflect۰Value۰SetMapIndex, |
| "(reflect.Value).SetPointer": ext۰reflect۰Value۰SetPointer, |
| "(reflect.Value).SetString": ext۰NoEffect, |
| "(reflect.Value).SetUint": ext۰NoEffect, |
| "(reflect.Value).Slice": ext۰reflect۰Value۰Slice, |
| "(reflect.Value).String": ext۰NoEffect, |
| "(reflect.Value).TryRecv": ext۰reflect۰Value۰Recv, |
| "(reflect.Value).TrySend": ext۰reflect۰Value۰Send, |
| "(reflect.Value).Type": ext۰NoEffect, |
| "(reflect.Value).Uint": ext۰NoEffect, |
| "(reflect.Value).UnsafeAddr": ext۰NoEffect, |
| |
| // Standalone reflect.* functions. |
| "reflect.Append": ext۰reflect۰Append, |
| "reflect.AppendSlice": ext۰reflect۰AppendSlice, |
| "reflect.Copy": ext۰reflect۰Copy, |
| "reflect.ChanOf": ext۰reflect۰ChanOf, |
| "reflect.DeepEqual": ext۰NoEffect, |
| "reflect.Indirect": ext۰reflect۰Indirect, |
| "reflect.MakeChan": ext۰reflect۰MakeChan, |
| "reflect.MakeFunc": ext۰reflect۰MakeFunc, |
| "reflect.MakeMap": ext۰reflect۰MakeMap, |
| "reflect.MakeSlice": ext۰reflect۰MakeSlice, |
| "reflect.MapOf": ext۰reflect۰MapOf, |
| "reflect.New": ext۰reflect۰New, |
| "reflect.NewAt": ext۰reflect۰NewAt, |
| "reflect.PtrTo": ext۰reflect۰PtrTo, |
| "reflect.Select": ext۰reflect۰Select, |
| "reflect.SliceOf": ext۰reflect۰SliceOf, |
| "reflect.TypeOf": ext۰reflect۰TypeOf, |
| "reflect.ValueOf": ext۰reflect۰ValueOf, |
| "reflect.Zero": ext۰reflect۰Zero, |
| "reflect.init": ext۰NoEffect, |
| |
| // *reflect.rtype methods |
| "(*reflect.rtype).Align": ext۰NoEffect, |
| "(*reflect.rtype).AssignableTo": ext۰NoEffect, |
| "(*reflect.rtype).Bits": ext۰NoEffect, |
| "(*reflect.rtype).ChanDir": ext۰NoEffect, |
| "(*reflect.rtype).ConvertibleTo": ext۰NoEffect, |
| "(*reflect.rtype).Elem": ext۰reflect۰rtype۰Elem, |
| "(*reflect.rtype).Field": ext۰reflect۰rtype۰Field, |
| "(*reflect.rtype).FieldAlign": ext۰NoEffect, |
| "(*reflect.rtype).FieldByIndex": ext۰reflect۰rtype۰FieldByIndex, |
| "(*reflect.rtype).FieldByName": ext۰reflect۰rtype۰FieldByName, |
| "(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc, |
| "(*reflect.rtype).Implements": ext۰NoEffect, |
| "(*reflect.rtype).In": ext۰reflect۰rtype۰In, |
| "(*reflect.rtype).IsVariadic": ext۰NoEffect, |
| "(*reflect.rtype).Key": ext۰reflect۰rtype۰Key, |
| "(*reflect.rtype).Kind": ext۰NoEffect, |
| "(*reflect.rtype).Len": ext۰NoEffect, |
| "(*reflect.rtype).Method": ext۰reflect۰rtype۰Method, |
| "(*reflect.rtype).MethodByName": ext۰reflect۰rtype۰MethodByName, |
| "(*reflect.rtype).Name": ext۰NoEffect, |
| "(*reflect.rtype).NumField": ext۰NoEffect, |
| "(*reflect.rtype).NumIn": ext۰NoEffect, |
| "(*reflect.rtype).NumMethod": ext۰NoEffect, |
| "(*reflect.rtype).NumOut": ext۰NoEffect, |
| "(*reflect.rtype).Out": ext۰reflect۰rtype۰Out, |
| "(*reflect.rtype).PkgPath": ext۰NoEffect, |
| "(*reflect.rtype).Size": ext۰NoEffect, |
| "(*reflect.rtype).String": ext۰NoEffect, |
| } { |
| intrinsicsByName[name] = fn |
| } |
| } |
| |
| // -------------------- (reflect.Value) -------------------- |
| |
| func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func (Value).Bytes() Value ---------- |
| |
| // result = v.Bytes() |
| type rVBytesConstraint struct { |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rVBytesConstraint) ptr() nodeid { return c.v } |
| func (c *rVBytesConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVBytes.result") |
| } |
| func (c *rVBytesConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVBytesConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v) |
| } |
| |
| func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, slice, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| tSlice, ok := tDyn.Underlying().(*types.Slice) |
| if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) { |
| if a.onlineCopy(c.result, slice) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVBytesConstraint{ |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func (Value).Call(in []Value) []Value ---------- |
| |
| // result = v.Call(in) |
| type rVCallConstraint struct { |
| cgn *cgnode |
| targets nodeid // (indirect) |
| v nodeid // (ptr) |
| arg nodeid // = in[*] |
| result nodeid // (indirect) |
| dotdotdot bool // interpret last arg as a "..." slice |
| } |
| |
| func (c *rVCallConstraint) ptr() nodeid { return c.v } |
| func (c *rVCallConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.targets), "rVCall.targets") |
| h.markIndirect(onodeid(c.result), "rVCall.result") |
| } |
| func (c *rVCallConstraint) renumber(mapping []nodeid) { |
| c.targets = mapping[c.targets] |
| c.v = mapping[c.v] |
| c.arg = mapping[c.arg] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVCallConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg) |
| } |
| |
| func (c *rVCallConstraint) solve(a *analysis, delta *nodeset) { |
| if c.targets == 0 { |
| panic("no targets") |
| } |
| |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, fn, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| tSig, ok := tDyn.Underlying().(*types.Signature) |
| if !ok { |
| continue // not a function |
| } |
| if tSig.Recv() != nil { |
| panic(tSig) // TODO(adonovan): rethink when we implement Method() |
| } |
| |
| // Add dynamic call target. |
| if a.onlineCopy(c.targets, fn) { |
| a.addWork(c.targets) |
| // TODO(adonovan): is 'else continue' a sound optimisation here? |
| } |
| |
| // Allocate a P/R block. |
| tParams := tSig.Params() |
| tResults := tSig.Results() |
| params := a.addNodes(tParams, "rVCall.params") |
| results := a.addNodes(tResults, "rVCall.results") |
| |
| // Make a dynamic call to 'fn'. |
| a.store(fn, params, 1, a.sizeof(tParams)) |
| a.load(results, fn, 1+a.sizeof(tParams), a.sizeof(tResults)) |
| |
| // Populate P by type-asserting each actual arg (all merged in c.arg). |
| for i, n := 0, tParams.Len(); i < n; i++ { |
| T := tParams.At(i).Type() |
| a.typeAssert(T, params, c.arg, false) |
| params += nodeid(a.sizeof(T)) |
| } |
| |
| // Use R by tagging and copying each actual result to c.result. |
| for i, n := 0, tResults.Len(); i < n; i++ { |
| T := tResults.At(i).Type() |
| // Convert from an arbitrary type to a reflect.Value |
| // (like MakeInterface followed by reflect.ValueOf). |
| if isInterface(T) { |
| // (don't tag) |
| if a.onlineCopy(c.result, results) { |
| changed = true |
| } |
| } else { |
| obj := a.makeTagged(T, c.cgn, nil) |
| a.onlineCopyN(obj+1, results, a.sizeof(T)) |
| if a.addLabel(c.result, obj) { // (true) |
| changed = true |
| } |
| } |
| results += nodeid(a.sizeof(T)) |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| // Common code for direct (inlined) and indirect calls to (reflect.Value).Call. |
| func reflectCallImpl(a *analysis, cgn *cgnode, site *callsite, recv, arg nodeid, dotdotdot bool) nodeid { |
| // Allocate []reflect.Value array for the result. |
| ret := a.nextNode() |
| a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "rVCall.ret") |
| a.endObject(ret, cgn, nil) |
| |
| // pts(targets) will be the set of possible call targets. |
| site.targets = a.addOneNode(tInvalid, "rvCall.targets", nil) |
| |
| // All arguments are merged since they arrive in a slice. |
| argelts := a.addOneNode(a.reflectValueObj.Type(), "rVCall.args", nil) |
| a.load(argelts, arg, 1, 1) // slice elements |
| |
| a.addConstraint(&rVCallConstraint{ |
| cgn: cgn, |
| targets: site.targets, |
| v: recv, |
| arg: argelts, |
| result: ret + 1, // results go into elements of ret |
| dotdotdot: dotdotdot, |
| }) |
| return ret |
| } |
| |
| func reflectCall(a *analysis, cgn *cgnode, dotdotdot bool) { |
| // This is the shared contour implementation of (reflect.Value).Call |
| // and CallSlice, as used by indirect calls (rare). |
| // Direct calls are inlined in gen.go, eliding the |
| // intermediate cgnode for Call. |
| site := new(callsite) |
| cgn.sites = append(cgn.sites, site) |
| recv := a.funcParams(cgn.obj) |
| arg := recv + 1 |
| ret := reflectCallImpl(a, cgn, site, recv, arg, dotdotdot) |
| a.addressOf(cgn.fn.Signature.Results().At(0).Type(), a.funcResults(cgn.obj), ret) |
| } |
| |
| func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) { |
| reflectCall(a, cgn, false) |
| } |
| |
| func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) { |
| // TODO(adonovan): implement. Also, inline direct calls in gen.go too. |
| if false { |
| reflectCall(a, cgn, true) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func (Value).Elem() Value ---------- |
| |
| // result = v.Elem() |
| type rVElemConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rVElemConstraint) ptr() nodeid { return c.v } |
| func (c *rVElemConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVElem.result") |
| } |
| func (c *rVElemConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVElemConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v) |
| } |
| |
| func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, payload, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| switch t := tDyn.Underlying().(type) { |
| case *types.Interface: |
| if a.onlineCopy(c.result, payload) { |
| changed = true |
| } |
| |
| case *types.Pointer: |
| obj := a.makeTagged(t.Elem(), c.cgn, nil) |
| a.load(obj+1, payload, 0, a.sizeof(t.Elem())) |
| if a.addLabel(c.result, obj) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVElemConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func (Value).Index() Value ---------- |
| |
| // result = v.Index() |
| type rVIndexConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rVIndexConstraint) ptr() nodeid { return c.v } |
| func (c *rVIndexConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVIndex.result") |
| } |
| func (c *rVIndexConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVIndexConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v) |
| } |
| |
| func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, payload, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| var res nodeid |
| switch t := tDyn.Underlying().(type) { |
| case *types.Array: |
| res = a.makeTagged(t.Elem(), c.cgn, nil) |
| a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem())) |
| |
| case *types.Slice: |
| res = a.makeTagged(t.Elem(), c.cgn, nil) |
| a.load(res+1, payload, 1, a.sizeof(t.Elem())) |
| |
| case *types.Basic: |
| if t.Kind() == types.String { |
| res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil) |
| } |
| } |
| if res != 0 && a.addLabel(c.result, res) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVIndexConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func (Value).Interface() Value ---------- |
| |
| // result = v.Interface() |
| type rVInterfaceConstraint struct { |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rVInterfaceConstraint) ptr() nodeid { return c.v } |
| func (c *rVInterfaceConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVInterface.result") |
| } |
| func (c *rVInterfaceConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVInterfaceConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v) |
| } |
| |
| func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, payload, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| if isInterface(tDyn) { |
| if a.onlineCopy(c.result, payload) { |
| a.addWork(c.result) |
| } |
| } else { |
| if a.addLabel(c.result, 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 // (indirect) |
| } |
| |
| func (c *rVMapIndexConstraint) ptr() nodeid { return c.v } |
| func (c *rVMapIndexConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVMapIndex.result") |
| } |
| func (c *rVMapIndexConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVMapIndexConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v) |
| } |
| |
| func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| 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 // (indirect) |
| } |
| |
| func (c *rVMapKeysConstraint) ptr() nodeid { return c.v } |
| func (c *rVMapKeysConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVMapKeys.result") |
| } |
| func (c *rVMapKeysConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVMapKeysConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v) |
| } |
| |
| func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| 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() |
| T := types.NewSlice(a.reflectValueObj.Type()) |
| a.addNodes(sliceToArray(T), "reflect.MapKeys result") |
| a.endObject(obj, cgn, nil) |
| a.addressOf(T, 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) {} // TODO(adonovan) |
| func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func (Value).Recv(Value) Value ---------- |
| |
| // result, _ = v.Recv() |
| type rVRecvConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rVRecvConstraint) ptr() nodeid { return c.v } |
| func (c *rVRecvConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVRecv.result") |
| } |
| func (c *rVRecvConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVRecvConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v) |
| } |
| |
| func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| 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) ptr() nodeid { return c.v } |
| func (c *rVSendConstraint) presolve(*hvn) {} |
| func (c *rVSendConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.x = mapping[c.x] |
| } |
| |
| func (c *rVSendConstraint) String() string { |
| return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x) |
| } |
| |
| func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) { |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| 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, false) |
| 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) {} // TODO(adonovan) |
| |
| // ---------- func (Value).SetBytes(x []byte) ---------- |
| |
| // v.SetBytes(x) |
| type rVSetBytesConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| x nodeid |
| } |
| |
| func (c *rVSetBytesConstraint) ptr() nodeid { return c.v } |
| func (c *rVSetBytesConstraint) presolve(*hvn) {} |
| func (c *rVSetBytesConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.x = mapping[c.x] |
| } |
| |
| func (c *rVSetBytesConstraint) String() string { |
| return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x) |
| } |
| |
| func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) { |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, slice, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| tSlice, ok := tDyn.Underlying().(*types.Slice) |
| if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) { |
| if a.onlineCopy(slice, c.x) { |
| a.addWork(slice) |
| } |
| } |
| } |
| } |
| |
| func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) { |
| params := a.funcParams(cgn.obj) |
| a.addConstraint(&rVSetBytesConstraint{ |
| cgn: cgn, |
| v: params, |
| x: params + 1, |
| }) |
| } |
| |
| // ---------- 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) ptr() nodeid { return c.v } |
| func (c *rVSetMapIndexConstraint) presolve(*hvn) {} |
| func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.key = mapping[c.key] |
| c.val = mapping[c.val] |
| } |
| |
| 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) solve(a *analysis, delta *nodeset) { |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| 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, false) |
| 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, false) |
| 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) {} // TODO(adonovan) |
| |
| // ---------- func (Value).Slice(v Value, i, j int) Value ---------- |
| |
| // result = v.Slice(_, _) |
| type rVSliceConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rVSliceConstraint) ptr() nodeid { return c.v } |
| func (c *rVSliceConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rVSlice.result") |
| } |
| func (c *rVSliceConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rVSliceConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v) |
| } |
| |
| func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, payload, indirect := a.taggedValue(vObj) |
| if indirect { |
| // TODO(adonovan): we'll need to implement this |
| // when we start creating indirect tagged objects. |
| panic("indirect tagged object") |
| } |
| |
| var res nodeid |
| switch t := tDyn.Underlying().(type) { |
| case *types.Pointer: |
| if tArr, ok := t.Elem().Underlying().(*types.Array); ok { |
| // pointer to array |
| res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil) |
| if a.onlineCopy(res+1, payload) { |
| a.addWork(res + 1) |
| } |
| } |
| |
| case *types.Array: |
| // TODO(adonovan): implement addressable |
| // arrays when we do indirect tagged objects. |
| |
| case *types.Slice: |
| res = vObj |
| |
| case *types.Basic: |
| if t == types.Typ[types.String] { |
| res = vObj |
| } |
| } |
| |
| if res != 0 && a.addLabel(c.result, res) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) { |
| a.addConstraint(&rVSliceConstraint{ |
| cgn: cgn, |
| v: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // -------------------- Standalone reflect functions -------------------- |
| |
| func ext۰reflect۰Append(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| func ext۰reflect۰Copy(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func ChanOf(ChanDir, Type) Type ---------- |
| |
| // result = ChanOf(dir, t) |
| type reflectChanOfConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid // (indirect) |
| dirs []types.ChanDir |
| } |
| |
| func (c *reflectChanOfConstraint) ptr() nodeid { return c.t } |
| func (c *reflectChanOfConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectChanOf.result") |
| } |
| func (c *reflectChanOfConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectChanOfConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t) |
| } |
| |
| func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.rtypeTaggedValue(tObj) |
| |
| if typeTooHigh(T) { |
| continue |
| } |
| |
| for _, dir := range c.dirs { |
| if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) { |
| changed = true |
| } |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| // dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf. |
| var dirMap = [...][]types.ChanDir{ |
| 0: {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown |
| reflect.RecvDir: {types.RecvOnly}, |
| reflect.SendDir: {types.SendOnly}, |
| reflect.BothDir: {types.SendRecv}, |
| } |
| |
| func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) { |
| // If we have access to the callsite, |
| // and the channel argument is a constant (as is usual), |
| // only generate the requested direction. |
| var dir reflect.ChanDir // unknown |
| if site := cgn.callersite; site != nil { |
| if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
| v, _ := exact.Int64Val(c.Value) |
| if 0 <= v && v <= int64(reflect.BothDir) { |
| dir = reflect.ChanDir(v) |
| } |
| } |
| } |
| |
| params := a.funcParams(cgn.obj) |
| a.addConstraint(&reflectChanOfConstraint{ |
| cgn: cgn, |
| t: params + 1, |
| result: a.funcResults(cgn.obj), |
| dirs: dirMap[dir], |
| }) |
| } |
| |
| // ---------- func Indirect(v Value) Value ---------- |
| |
| // result = Indirect(v) |
| type reflectIndirectConstraint struct { |
| cgn *cgnode |
| v nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectIndirectConstraint) ptr() nodeid { return c.v } |
| func (c *reflectIndirectConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectIndirect.result") |
| } |
| func (c *reflectIndirectConstraint) renumber(mapping []nodeid) { |
| c.v = mapping[c.v] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectIndirectConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v) |
| } |
| |
| func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| vObj := nodeid(x) |
| tDyn, _, _ := a.taggedValue(vObj) |
| 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 // (indirect) |
| } |
| |
| func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ } |
| func (c *reflectMakeChanConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectMakeChan.result") |
| } |
| func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) { |
| c.typ = mapping[c.typ] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectMakeChanConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| typObj := nodeid(x) |
| T := a.rtypeTaggedValue(typObj) |
| tChan, ok := T.Underlying().(*types.Chan) |
| if !ok || tChan.Dir() != types.SendRecv { |
| 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) {} // TODO(adonovan) |
| |
| // ---------- func MakeMap(Type) Value ---------- |
| |
| // result = MakeMap(typ) |
| type reflectMakeMapConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ } |
| func (c *reflectMakeMapConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectMakeMap.result") |
| } |
| func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) { |
| c.typ = mapping[c.typ] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectMakeMapConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| typObj := nodeid(x) |
| 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 MakeSlice(Type) Value ---------- |
| |
| // result = MakeSlice(typ) |
| type reflectMakeSliceConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ } |
| func (c *reflectMakeSliceConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectMakeSlice.result") |
| } |
| func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) { |
| c.typ = mapping[c.typ] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectMakeSliceConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| typObj := nodeid(x) |
| T := a.rtypeTaggedValue(typObj) |
| if _, ok := T.Underlying().(*types.Slice); !ok { |
| continue // not a slice type |
| } |
| |
| obj := a.nextNode() |
| a.addNodes(sliceToArray(T), "reflect.MakeSlice") |
| 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۰MakeSlice(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectMakeSliceConstraint{ |
| cgn: cgn, |
| typ: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func New(Type) Value ---------- |
| |
| // result = New(typ) |
| type reflectNewConstraint struct { |
| cgn *cgnode |
| typ nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectNewConstraint) ptr() nodeid { return c.typ } |
| func (c *reflectNewConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectNew.result") |
| } |
| func (c *reflectNewConstraint) renumber(mapping []nodeid) { |
| c.typ = mapping[c.typ] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectNewConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| typObj := nodeid(x) |
| 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): also report dynamic calls to unsound intrinsics. |
| if site := cgn.callersite; site != nil { |
| a.warnf(site.pos(), "unsound: %s contains a reflect.NewAt() call", site.instr.Parent()) |
| } |
| } |
| |
| // ---------- func PtrTo(Type) Type ---------- |
| |
| // result = PtrTo(t) |
| type reflectPtrToConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectPtrToConstraint) ptr() nodeid { return c.t } |
| func (c *reflectPtrToConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectPtrTo.result") |
| } |
| func (c *reflectPtrToConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectPtrToConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t) |
| } |
| |
| func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.rtypeTaggedValue(tObj) |
| |
| if typeTooHigh(T) { |
| continue |
| } |
| |
| if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectPtrToConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func SliceOf(Type) Type ---------- |
| |
| // result = SliceOf(t) |
| type reflectSliceOfConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t } |
| func (c *reflectSliceOfConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectSliceOf.result") |
| } |
| func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectSliceOfConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t) |
| } |
| |
| func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.rtypeTaggedValue(tObj) |
| |
| if typeTooHigh(T) { |
| continue |
| } |
| |
| if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.result) |
| } |
| } |
| |
| func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) { |
| a.addConstraint(&reflectSliceOfConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // ---------- func TypeOf(v Value) Type ---------- |
| |
| // result = TypeOf(i) |
| type reflectTypeOfConstraint struct { |
| cgn *cgnode |
| i nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i } |
| func (c *reflectTypeOfConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectTypeOf.result") |
| } |
| func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) { |
| c.i = mapping[c.i] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectTypeOfConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i) |
| } |
| |
| func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| iObj := nodeid(x) |
| tDyn, _, _ := a.taggedValue(iObj) |
| 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 // (indirect) |
| } |
| |
| func (c *reflectZeroConstraint) ptr() nodeid { return c.typ } |
| func (c *reflectZeroConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "reflectZero.result") |
| } |
| func (c *reflectZeroConstraint) renumber(mapping []nodeid) { |
| c.typ = mapping[c.typ] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *reflectZeroConstraint) String() string { |
| return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ) |
| } |
| |
| func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| typObj := nodeid(x) |
| T := a.rtypeTaggedValue(typObj) |
| |
| // TODO(adonovan): if T is an interface type, we need |
| // to create an indirect tagged object containing |
| // new(T). To avoid updates of such shared values, |
| // we'll need another flag on indirect tagged objects |
| // that marks whether they are addressable or |
| // readonly, just like the reflect package does. |
| |
| // 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 // (indirect) |
| } |
| |
| func (c *rtypeElemConstraint) ptr() nodeid { return c.t } |
| func (c *rtypeElemConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rtypeElem.result") |
| } |
| func (c *rtypeElemConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rtypeElemConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t) |
| } |
| |
| func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) { |
| // Implemented by *types.{Map,Chan,Array,Slice,Pointer}. |
| type hasElem interface { |
| Elem() types.Type |
| } |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.nodes[tObj].obj.data.(types.Type) |
| 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 (*rtype) Field(int) StructField ---------- |
| // ---------- func (*rtype) FieldByName(string) (StructField, bool) ---------- |
| |
| // result = FieldByName(t, name) |
| // result = Field(t, _) |
| type rtypeFieldByNameConstraint struct { |
| cgn *cgnode |
| name string // name of field; "" for unknown |
| t nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rtypeFieldByNameConstraint) ptr() nodeid { return c.t } |
| func (c *rtypeFieldByNameConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result+3), "rtypeFieldByName.result.Type") |
| } |
| func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rtypeFieldByNameConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name) |
| } |
| |
| func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) { |
| // type StructField struct { |
| // 0 __identity__ |
| // 1 Name string |
| // 2 PkgPath string |
| // 3 Type Type |
| // 4 Tag StructTag |
| // 5 Offset uintptr |
| // 6 Index []int |
| // 7 Anonymous bool |
| // } |
| |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.nodes[tObj].obj.data.(types.Type) |
| tStruct, ok := T.Underlying().(*types.Struct) |
| if !ok { |
| continue // not a struct type |
| } |
| |
| n := tStruct.NumFields() |
| for i := 0; i < n; i++ { |
| f := tStruct.Field(i) |
| if c.name == "" || c.name == f.Name() { |
| |
| // a.offsetOf(Type) is 3. |
| if id := c.result + 3; a.addLabel(id, a.makeRtype(f.Type())) { |
| a.addWork(id) |
| } |
| // TODO(adonovan): StructField.Index should be non-nil. |
| } |
| } |
| } |
| } |
| |
| func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) { |
| // If we have access to the callsite, |
| // and the argument is a string constant, |
| // return only that field. |
| var name string |
| if site := cgn.callersite; site != nil { |
| if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
| name = exact.StringVal(c.Value) |
| } |
| } |
| |
| a.addConstraint(&rtypeFieldByNameConstraint{ |
| cgn: cgn, |
| name: name, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) { |
| // No-one ever calls Field with a constant argument, |
| // so we don't specialize that case. |
| a.addConstraint(&rtypeFieldByNameConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) |
| |
| // ---------- func (*rtype) In/Out(i int) Type ---------- |
| |
| // result = In/Out(t, i) |
| type rtypeInOutConstraint struct { |
| cgn *cgnode |
| t nodeid // (ptr) |
| result nodeid // (indirect) |
| out bool |
| i int // -ve if not a constant |
| } |
| |
| func (c *rtypeInOutConstraint) ptr() nodeid { return c.t } |
| func (c *rtypeInOutConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rtypeInOut.result") |
| } |
| func (c *rtypeInOutConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rtypeInOutConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i) |
| } |
| |
| func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.nodes[tObj].obj.data.(types.Type) |
| sig, ok := T.Underlying().(*types.Signature) |
| if !ok { |
| continue // not a func type |
| } |
| |
| tuple := sig.Params() |
| if c.out { |
| tuple = sig.Results() |
| } |
| for i, n := 0, tuple.Len(); i < n; i++ { |
| if c.i < 0 || c.i == 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) { |
| // If we have access to the callsite, |
| // and the argument is an int constant, |
| // return only that parameter. |
| index := -1 |
| if site := cgn.callersite; site != nil { |
| if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
| v, _ := exact.Int64Val(c.Value) |
| index = int(v) |
| } |
| } |
| a.addConstraint(&rtypeInOutConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| out: out, |
| i: index, |
| }) |
| } |
| |
| 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 // (indirect) |
| } |
| |
| func (c *rtypeKeyConstraint) ptr() nodeid { return c.t } |
| func (c *rtypeKeyConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result), "rtypeKey.result") |
| } |
| func (c *rtypeKeyConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rtypeKeyConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t) |
| } |
| |
| func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) { |
| changed := false |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.nodes[tObj].obj.data.(types.Type) |
| 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 (*rtype) Method(int) (Method, bool) ---------- |
| // ---------- func (*rtype) MethodByName(string) (Method, bool) ---------- |
| |
| // result = MethodByName(t, name) |
| // result = Method(t, _) |
| type rtypeMethodByNameConstraint struct { |
| cgn *cgnode |
| name string // name of method; "" for unknown |
| t nodeid // (ptr) |
| result nodeid // (indirect) |
| } |
| |
| func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t } |
| func (c *rtypeMethodByNameConstraint) presolve(h *hvn) { |
| h.markIndirect(onodeid(c.result+3), "rtypeMethodByName.result.Type") |
| h.markIndirect(onodeid(c.result+4), "rtypeMethodByName.result.Func") |
| } |
| func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) { |
| c.t = mapping[c.t] |
| c.result = mapping[c.result] |
| } |
| |
| func (c *rtypeMethodByNameConstraint) String() string { |
| return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name) |
| } |
| |
| // changeRecv returns sig with Recv prepended to Params(). |
| func changeRecv(sig *types.Signature) *types.Signature { |
| params := sig.Params() |
| n := params.Len() |
| p2 := make([]*types.Var, n+1) |
| p2[0] = sig.Recv() |
| for i := 0; i < n; i++ { |
| p2[i+1] = params.At(i) |
| } |
| return types.NewSignature(nil, types.NewTuple(p2...), sig.Results(), sig.Variadic()) |
| } |
| |
| func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) { |
| for _, x := range delta.AppendTo(a.deltaSpace) { |
| tObj := nodeid(x) |
| T := a.nodes[tObj].obj.data.(types.Type) |
| |
| isIface := isInterface(T) |
| |
| // We don't use Lookup(c.name) when c.name != "" to avoid |
| // ambiguity: >1 unexported methods could match. |
| mset := a.prog.MethodSets.MethodSet(T) |
| for i, n := 0, mset.Len(); i < n; i++ { |
| sel := mset.At(i) |
| if c.name == "" || c.name == sel.Obj().Name() { |
| // type Method struct { |
| // 0 __identity__ |
| // 1 Name string |
| // 2 PkgPath string |
| // 3 Type Type |
| // 4 Func Value |
| // 5 Index int |
| // } |
| |
| var sig *types.Signature |
| var fn *ssa.Function |
| if isIface { |
| sig = sel.Type().(*types.Signature) |
| } else { |
| fn = a.prog.Method(sel) |
| // move receiver to params[0] |
| sig = changeRecv(fn.Signature) |
| } |
| |
| // a.offsetOf(Type) is 3. |
| if id := c.result + 3; a.addLabel(id, a.makeRtype(sig)) { |
| a.addWork(id) |
| } |
| if fn != nil { |
| // a.offsetOf(Func) is 4. |
| if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) { |
| a.addWork(id) |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) { |
| // If we have access to the callsite, |
| // and the argument is a string constant, |
| // return only that method. |
| var name string |
| if site := cgn.callersite; site != nil { |
| if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { |
| name = exact.StringVal(c.Value) |
| } |
| } |
| |
| a.addConstraint(&rtypeMethodByNameConstraint{ |
| cgn: cgn, |
| name: name, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) { |
| // No-one ever calls Method with a constant argument, |
| // so we don't specialize that case. |
| a.addConstraint(&rtypeMethodByNameConstraint{ |
| cgn: cgn, |
| t: a.funcParams(cgn.obj), |
| result: a.funcResults(cgn.obj), |
| }) |
| } |
| |
| // typeHeight returns the "height" of the type, which is roughly |
| // speaking the number of chan, map, pointer and slice type constructors |
| // at the root of T; these are the four type kinds that can be created |
| // via reflection. Chan and map constructors are counted as double the |
| // height of slice and pointer constructors since they are less often |
| // deeply nested. |
| // |
| // The solver rules for type constructors must somehow bound the set of |
| // types they create to ensure termination of the algorithm in cases |
| // where the output of a type constructor flows to its input, e.g. |
| // |
| // func f(t reflect.Type) { |
| // f(reflect.PtrTo(t)) |
| // } |
| // |
| // It does this by limiting the type height to k, but this still leaves |
| // a potentially exponential (4^k) number of of types that may be |
| // enumerated in pathological cases. |
| // |
| func typeHeight(T types.Type) int { |
| switch T := T.(type) { |
| case *types.Chan: |
| return 2 + typeHeight(T.Elem()) |
| case *types.Map: |
| k := typeHeight(T.Key()) |
| v := typeHeight(T.Elem()) |
| if v > k { |
| k = v // max(k, v) |
| } |
| return 2 + k |
| case *types.Slice: |
| return 1 + typeHeight(T.Elem()) |
| case *types.Pointer: |
| return 1 + typeHeight(T.Elem()) |
| } |
| return 0 |
| } |
| |
| func typeTooHigh(T types.Type) bool { |
| return typeHeight(T) > 3 |
| } |