+++ /dev/null
-// 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"
- "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, _ := constant.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 = constant.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, _ := constant.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 = constant.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
-}