wide/vendor/golang.org/x/tools/go/pointer/reflect.go

1976 lines
54 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
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"
exact "go/constant"
"go/types"
"reflect"
"golang.org/x/tools/go/ssa"
)
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.MethodValue(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
}