mirror of
https://github.com/golang/go
synced 2024-11-05 17:26:11 -07:00
go.tools/pointer: reflect, part 2: channels.
(reflect.Value).Send (reflect.Value).TrySend (reflect.Value).Recv (reflect.Value).TryRecv (reflect.Type).ChanOf (reflect.Type).In (reflect.Type).Out reflect.Indirect reflect.MakeChan Also: - specialize genInvoke when the receiver is a reflect.Type under the assumption that there's only one possible concrete type. This makes all reflect.Type operations context-sensitive since the calls are no longer dynamic. - Rename all variables to match the actual parameter names used in the reflect API. - Add pointer.Config.Reflection flag (exposed in oracle as --reflect, default false) to enable reflection. It currently adds about 20% running time. I'll make it true after the presolver is implemented. - Simplified worklist datatype and solver main loop slightly (~10% speed improvement). - Use addLabel() utility to add a label to a PTS. (Working on my 3 yr old 2x2GHz+4GB Mac vs 8x4GHz+24GB workstation, one really notices the cost of pointer analysis. Note to self: time to implement presolver.) R=crawshaw CC=golang-dev https://golang.org/cl/13242062
This commit is contained in:
parent
25a0cc4bfd
commit
3371b79a96
@ -41,6 +41,9 @@ var ptalogFlag = flag.String("ptalog", "",
|
||||
|
||||
var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")
|
||||
|
||||
// TODO(adonovan): eliminate or flip this flag after PTA presolver is implemented.
|
||||
var reflectFlag = flag.Bool("reflect", true, "Analyze reflection soundly (slow).")
|
||||
|
||||
const useHelp = "Run 'oracle -help' for more information.\n"
|
||||
|
||||
const helpMessage = `Go source code oracle.
|
||||
@ -68,8 +71,8 @@ The user manual is available here: http://golang.org/s/oracle-user-manual
|
||||
|
||||
Examples:
|
||||
|
||||
Describe the syntax at offset 670 in this file (an import spec):
|
||||
% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#670 \
|
||||
Describe the syntax at offset 530 in this file (an import spec):
|
||||
% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#530 \
|
||||
code.google.com/p/go.tools/cmd/oracle
|
||||
|
||||
Print the callgraph of the trivial web-server in JSON format:
|
||||
@ -149,7 +152,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Ask the oracle.
|
||||
res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, &build.Default)
|
||||
res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, &build.Default, *reflectFlag)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n"+useHelp, err)
|
||||
os.Exit(1)
|
||||
|
@ -43,7 +43,7 @@ import (
|
||||
type Oracle struct {
|
||||
out io.Writer // standard output
|
||||
prog *ssa.Program // the SSA program [only populated if need&SSA]
|
||||
config pointer.Config // pointer analysis configuration
|
||||
config pointer.Config // pointer analysis configuration [TODO rename ptaConfig]
|
||||
|
||||
// need&AllTypeInfo
|
||||
typeInfo map[*types.Package]*importer.PackageInfo // type info for all ASTs in the program
|
||||
@ -149,6 +149,7 @@ func (res *Result) MarshalJSON() ([]byte, error) {
|
||||
// mode is the query mode ("callers", etc).
|
||||
// ptalog is the (optional) pointer-analysis log file.
|
||||
// buildContext is the go/build configuration for locating packages.
|
||||
// reflection determines whether to model reflection soundly (currently slow).
|
||||
//
|
||||
// Clients that intend to perform multiple queries against the same
|
||||
// analysis scope should use this pattern instead:
|
||||
@ -169,14 +170,14 @@ func (res *Result) MarshalJSON() ([]byte, error) {
|
||||
// TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos
|
||||
// depends on the query mode; how should we expose this?
|
||||
//
|
||||
func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context) (*Result, error) {
|
||||
func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context, reflection bool) (*Result, error) {
|
||||
minfo := findMode(mode)
|
||||
if minfo == nil {
|
||||
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
||||
}
|
||||
|
||||
imp := importer.New(&importer.Config{Build: buildContext})
|
||||
o, err := New(imp, args, ptalog)
|
||||
o, err := New(imp, args, ptalog, reflection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -216,17 +217,19 @@ func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *buil
|
||||
// args specify the main package in importer.CreatePackageFromArgs syntax.
|
||||
//
|
||||
// ptalog is the (optional) pointer-analysis log file.
|
||||
// reflection determines whether to model reflection soundly (currently slow).
|
||||
//
|
||||
func New(imp *importer.Importer, args []string, ptalog io.Writer) (*Oracle, error) {
|
||||
return newOracle(imp, args, ptalog, needAll)
|
||||
func New(imp *importer.Importer, args []string, ptalog io.Writer, reflection bool) (*Oracle, error) {
|
||||
return newOracle(imp, args, ptalog, needAll, reflection)
|
||||
}
|
||||
|
||||
func newOracle(imp *importer.Importer, args []string, ptalog io.Writer, needs int) (*Oracle, error) {
|
||||
func newOracle(imp *importer.Importer, args []string, ptalog io.Writer, needs int, reflection bool) (*Oracle, error) {
|
||||
o := &Oracle{
|
||||
prog: ssa.NewProgram(imp.Fset, 0),
|
||||
timers: make(map[string]time.Duration),
|
||||
}
|
||||
o.config.Log = ptalog
|
||||
o.config.Reflection = reflection
|
||||
|
||||
// Load/parse/type-check program from args.
|
||||
start := time.Now()
|
||||
|
@ -165,7 +165,9 @@ func doQuery(out io.Writer, q *query, useJson bool) {
|
||||
res, err := oracle.Query([]string{q.filename},
|
||||
q.verb,
|
||||
fmt.Sprintf("%s:#%d,#%d", q.filename, q.start, q.end),
|
||||
/*PTA-log=*/ nil, &buildContext)
|
||||
nil, // ptalog,
|
||||
&buildContext,
|
||||
true) // reflection
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "\nError: %s\n", stripLocation(err.Error()))
|
||||
return
|
||||
@ -259,7 +261,7 @@ func TestMultipleQueries(t *testing.T) {
|
||||
|
||||
// Oracle
|
||||
filename := "testdata/src/main/multi.go"
|
||||
o, err := oracle.New(imp, []string{filename}, nil)
|
||||
o, err := oracle.New(imp, []string{filename}, nil, true)
|
||||
if err != nil {
|
||||
t.Fatalf("oracle.New failed: %s", err)
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ CONSTRAINT GENERATION:
|
||||
|
||||
PRESOLVER OPTIMISATIONS
|
||||
- use HVN, HRU, LE, PE, HCD, LCD.
|
||||
But: LE would lose the precise detail we currently enjoy in each label.
|
||||
|
||||
SOLVER:
|
||||
- use BDDs and/or sparse bitvectors for ptsets
|
||||
|
@ -185,7 +185,8 @@ type analysis struct {
|
||||
hasher typemap.Hasher // cache of type hashes
|
||||
reflectValueObj types.Object // type symbol for reflect.Value (if present)
|
||||
reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
|
||||
reflectRtype *types.Pointer // *reflect.rtype
|
||||
reflectRtypePtr *types.Pointer // *reflect.rtype
|
||||
reflectType *types.Named // reflect.Type
|
||||
rtypes typemap.M // nodeid of canonical *rtype-tagged object for type T
|
||||
reflectZeros typemap.M // nodeid of canonical T-tagged object for zero value
|
||||
}
|
||||
@ -244,8 +245,9 @@ func Analyze(config *Config) CallGraphNode {
|
||||
|
||||
if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
|
||||
a.reflectValueObj = reflect.Object.Scope().Lookup("Value")
|
||||
a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named)
|
||||
a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
|
||||
a.reflectRtype = types.NewPointer(a.reflectRtypeObj.Type())
|
||||
a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
|
||||
|
||||
// Override flattening of reflect.Value, treating it like a basic type.
|
||||
tReflectValue := a.reflectValueObj.Type()
|
||||
@ -265,11 +267,7 @@ func Analyze(config *Config) CallGraphNode {
|
||||
|
||||
root := a.generate()
|
||||
|
||||
// ---------- Presolver ----------
|
||||
|
||||
// TODO(adonovan): opt: presolver optimisations.
|
||||
|
||||
// ---------- Solver ----------
|
||||
//a.optimize()
|
||||
|
||||
a.solve()
|
||||
|
||||
|
@ -20,6 +20,13 @@ type Config struct {
|
||||
Mains []*ssa.Package // set of 'main' packages to analyze
|
||||
root *ssa.Function // synthetic analysis root
|
||||
|
||||
// Reflection determines whether to handle reflection
|
||||
// operators soundly, which is currently rather slow since it
|
||||
// causes constraint to be generated during solving
|
||||
// proportional to the number of constraint variables, which
|
||||
// has not yet been reduced by presolver optimisation.
|
||||
Reflection bool
|
||||
|
||||
// -------- Optional callbacks invoked by the analysis --------
|
||||
|
||||
// Call is invoked for each discovered call-graph edge. The
|
||||
|
@ -10,11 +10,13 @@ pointer analysis algorithm first described in (Andersen, 1994).
|
||||
The implementation is similar to that described in (Pearce et al,
|
||||
PASTE'04). Unlike many algorithms which interleave constraint
|
||||
generation and solving, constructing the callgraph as they go, this
|
||||
implementation has a strict phase ordering: generation before solving.
|
||||
Only simple (copy) constraints may be generated during solving. This
|
||||
improves the traction of presolver optimisations, but imposes certain
|
||||
restrictions, e.g. potential context sensitivity is limited since all
|
||||
variants must be created a priori.
|
||||
implementation for the most part observes a phase ordering (generation
|
||||
before solving), with only simple (copy) constraints being generated
|
||||
during solving. (The exception is reflection, which creates various
|
||||
constraints during solving as new types flow to reflect.Value
|
||||
operations.) This improves the traction of presolver optimisations,
|
||||
but imposes certain restrictions, e.g. potential context sensitivity
|
||||
is limited since all variants must be created a priori.
|
||||
|
||||
We intend to add various presolving optimisations such as Pointer and
|
||||
Location Equivalence from (Hardekopf & Lin, SAS '07) and solver
|
||||
|
@ -235,7 +235,7 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
|
||||
a.addOneNode(T, "reflect.rtype", nil)
|
||||
a.endObject(obj, nil, nil).rtype = T
|
||||
|
||||
id := a.makeTagged(a.reflectRtype, nil, nil)
|
||||
id := a.makeTagged(a.reflectRtypePtr, nil, nil)
|
||||
a.nodes[id].obj.rtype = T
|
||||
a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
|
||||
a.addressOf(id+1, obj)
|
||||
@ -244,6 +244,15 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
|
||||
return id
|
||||
}
|
||||
|
||||
// rtypeValue returns the type of the *reflect.rtype-tagged object obj.
|
||||
func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type {
|
||||
tDyn, t, _ := a.taggedValue(obj)
|
||||
if tDyn != a.reflectRtypePtr {
|
||||
panic(fmt.Sprintf("not a *reflect.rtype-tagged value: obj=n%d tag=%v payload=n%d", obj, tDyn, t))
|
||||
}
|
||||
return a.nodes[t].typ
|
||||
}
|
||||
|
||||
// valueNode returns the id of the value node for v, creating it (and
|
||||
// the association) as needed. It may return zero for uninteresting
|
||||
// values containing no pointers.
|
||||
@ -432,6 +441,11 @@ func (a *analysis) offsetAddr(dst, src nodeid, offset uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// typeAssert creates a typeAssert constraint of the form dst = src.(T).
|
||||
func (a *analysis) typeAssert(T types.Type, dst, src nodeid) {
|
||||
a.addConstraint(&typeAssertConstraint{T, dst, src})
|
||||
}
|
||||
|
||||
// addConstraint adds c to the constraint set.
|
||||
func (a *analysis) addConstraint(c constraint) {
|
||||
a.constraints = append(a.constraints, c)
|
||||
@ -720,13 +734,11 @@ func (a *analysis) genDynamicCall(call *ssa.CallCommon, result nodeid) nodeid {
|
||||
// It returns a node whose pts() will be the set of possible call targets.
|
||||
//
|
||||
func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
|
||||
sig := call.Signature()
|
||||
if call.Value.Type() == a.reflectType {
|
||||
return a.genInvokeReflectType(call, result)
|
||||
}
|
||||
|
||||
// TODO(adonovan): optimise this into a static call when there
|
||||
// can be at most one type that implements the interface (due
|
||||
// to unexported methods). This is particularly important for
|
||||
// methods of interface reflect.Type (sole impl:
|
||||
// *reflect.rtype), so we can realize context sensitivity.
|
||||
sig := call.Signature()
|
||||
|
||||
// Allocate a contiguous targets/params/results block for this call.
|
||||
block := a.nextNode()
|
||||
@ -753,6 +765,63 @@ func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
|
||||
return targets
|
||||
}
|
||||
|
||||
// genInvokeReflectType is a specialization of genInvoke where the
|
||||
// receiver type is a reflect.Type, under the assumption that there
|
||||
// can be at most one implementation of this interface, *reflect.rtype.
|
||||
//
|
||||
// (Though this may appear to be an instance of a pattern---method
|
||||
// calls on interfaces known to have exactly one implementation---in
|
||||
// practice it occurs rarely, so we special case for reflect.Type.)
|
||||
//
|
||||
// In effect we treat this:
|
||||
// var rt reflect.Type = ...
|
||||
// rt.F()
|
||||
// as this:
|
||||
// rt.(*reflect.rtype).F()
|
||||
//
|
||||
// It returns a node whose pts() will be the (singleton) set of
|
||||
// possible call targets.
|
||||
//
|
||||
func (a *analysis) genInvokeReflectType(call *ssa.CallCommon, result nodeid) nodeid {
|
||||
// Unpack receiver into rtype
|
||||
rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
|
||||
recv := a.valueNode(call.Value)
|
||||
a.typeAssert(a.reflectRtypePtr, rtype, recv)
|
||||
|
||||
// Look up the concrete method.
|
||||
meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name())
|
||||
fn := a.prog.Method(meth)
|
||||
|
||||
obj := a.makeFunctionObject(fn) // new contour for this call
|
||||
|
||||
// From now on, it's essentially a static call, but little is
|
||||
// gained by factoring together the code for both cases.
|
||||
|
||||
sig := fn.Signature // concrete method
|
||||
targets := a.addOneNode(sig, "call.targets", nil)
|
||||
a.addressOf(targets, obj) // (a singleton)
|
||||
|
||||
// Copy receiver.
|
||||
params := a.funcParams(obj)
|
||||
a.copy(params, rtype, 1)
|
||||
params++
|
||||
|
||||
// Copy actual parameters into formal params block.
|
||||
// Must loop, since the actuals aren't contiguous.
|
||||
for i, arg := range call.Args {
|
||||
sz := a.sizeof(sig.Params().At(i).Type())
|
||||
a.copy(params, a.valueNode(arg), sz)
|
||||
params += nodeid(sz)
|
||||
}
|
||||
|
||||
// Copy formal results block to actual result.
|
||||
if result != 0 {
|
||||
a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// genCall generates contraints for call instruction instr.
|
||||
func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
|
||||
call := instr.Common()
|
||||
@ -927,8 +996,7 @@ func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
|
||||
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
|
||||
|
||||
case *ssa.TypeAssert:
|
||||
dst, src := a.valueNode(instr), a.valueNode(instr.X)
|
||||
a.addConstraint(&typeAssertConstraint{instr.AssertedType, dst, src})
|
||||
a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X))
|
||||
|
||||
case *ssa.Slice:
|
||||
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
|
||||
@ -1117,7 +1185,9 @@ func (a *analysis) generate() *cgnode {
|
||||
a.panicNode = a.addNodes(tEface, "panic")
|
||||
|
||||
// Create nodes and constraints for all methods of reflect.rtype.
|
||||
if rtype := a.reflectRtype; rtype != nil {
|
||||
// (Shared contours are used by dynamic calls to reflect.Type
|
||||
// methods---typically just String().)
|
||||
if rtype := a.reflectRtypePtr; rtype != nil {
|
||||
mset := rtype.MethodSet()
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
a.valueNode(a.prog.Method(mset.At(i)))
|
||||
@ -1135,9 +1205,5 @@ func (a *analysis) generate() *cgnode {
|
||||
a.genFunc(cgn)
|
||||
}
|
||||
|
||||
// Create a dummy node to avoid out-of-range indexing in case
|
||||
// the last allocated type was of zero length.
|
||||
a.addNodes(tInvalid, "(max)")
|
||||
|
||||
return root
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ func init() {
|
||||
"(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,
|
||||
@ -83,6 +85,8 @@ func init() {
|
||||
"(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,
|
||||
@ -275,9 +279,13 @@ func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
|
||||
if !ok {
|
||||
impl = intrinsicsByName[fn.String()] // may be nil
|
||||
|
||||
// Ensure all "reflect" code is treated intrinsically.
|
||||
if impl == nil && fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object {
|
||||
impl = ext۰NotYetImplemented
|
||||
if fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object {
|
||||
if !a.config.Reflection {
|
||||
impl = ext۰NoEffect // reflection disabled
|
||||
} else if impl == nil {
|
||||
// Ensure all "reflect" code is treated intrinsically.
|
||||
impl = ext۰NotYetImplemented
|
||||
}
|
||||
}
|
||||
|
||||
a.intrinsics[fn] = impl
|
||||
|
@ -33,9 +33,11 @@ var inputs = []string{
|
||||
// "testdata/tmp.go",
|
||||
|
||||
// Working:
|
||||
"testdata/a_test.go",
|
||||
"testdata/another.go",
|
||||
"testdata/arrays.go",
|
||||
"testdata/channels.go",
|
||||
"testdata/chanreflect.go",
|
||||
"testdata/context.go",
|
||||
"testdata/conv.go",
|
||||
"testdata/flow.go",
|
||||
@ -43,21 +45,19 @@ var inputs = []string{
|
||||
"testdata/func.go",
|
||||
"testdata/hello.go",
|
||||
"testdata/interfaces.go",
|
||||
"testdata/funcreflect.go",
|
||||
"testdata/mapreflect.go",
|
||||
"testdata/maps.go",
|
||||
"testdata/panic.go",
|
||||
"testdata/recur.go",
|
||||
"testdata/reflect.go",
|
||||
"testdata/structs.go",
|
||||
"testdata/a_test.go",
|
||||
"testdata/mapreflect.go",
|
||||
|
||||
// TODO(adonovan): get these tests (of reflection) passing.
|
||||
// (The tests are mostly sound since they were used for a
|
||||
// previous implementation.)
|
||||
// "testdata/funcreflect.go",
|
||||
// "testdata/arrayreflect.go",
|
||||
// "testdata/chanreflect.go",
|
||||
// "testdata/finalizer.go",
|
||||
// "testdata/reflect.go",
|
||||
// "testdata/structreflect.go",
|
||||
}
|
||||
|
||||
@ -290,8 +290,9 @@ func doOneInput(input, filename string) bool {
|
||||
|
||||
// Run the analysis.
|
||||
config := &pointer.Config{
|
||||
Mains: []*ssa.Package{ptrmain},
|
||||
Log: &log,
|
||||
Reflection: true,
|
||||
Mains: []*ssa.Package{ptrmain},
|
||||
Log: &log,
|
||||
Print: func(site *ssa.CallCommon, p pointer.Pointer) {
|
||||
probes = append(probes, probe{site, p})
|
||||
},
|
||||
|
@ -4,10 +4,14 @@ package pointer
|
||||
// constraints arising from the use of reflection in the target
|
||||
// program. See doc.go for explanation of the representation.
|
||||
//
|
||||
// For consistency, the names of all parameters match those of the
|
||||
// actual functions in the "reflect" package.
|
||||
//
|
||||
// TODO(adonovan): fix: most of the reflect API permits implicit
|
||||
// conversions due to assignability, e.g. m.MapIndex(k) is ok if T(k)
|
||||
// is assignable to T(M).key. It's not yet clear how best to model
|
||||
// that.
|
||||
// that; perhaps a more lenient version of typeAssertConstraint is
|
||||
// needed.
|
||||
//
|
||||
// To avoid proliferation of equivalent labels, instrinsics should
|
||||
// memoize as much as possible, like TypeOf and Zero do for their
|
||||
@ -17,6 +21,7 @@ package pointer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
@ -37,25 +42,25 @@ func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func (Value).Interface() Value ----------
|
||||
|
||||
// result = rv.Interface()
|
||||
// result = v.Interface()
|
||||
type rVInterfaceConstraint struct {
|
||||
rv nodeid // (ptr)
|
||||
v nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *rVInterfaceConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.rv)
|
||||
return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVInterfaceConstraint) ptr() nodeid {
|
||||
return c.rv
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
resultPts := &a.nodes[c.result].pts
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
tDyn, _, indirect := a.taggedValue(obj)
|
||||
for vObj := range delta {
|
||||
tDyn, _, indirect := a.taggedValue(vObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged object")
|
||||
}
|
||||
@ -65,7 +70,7 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
panic("indirect tagged object")
|
||||
}
|
||||
|
||||
if resultPts.add(obj) {
|
||||
if resultPts.add(vObj) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -76,33 +81,33 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
|
||||
func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&rVInterfaceConstraint{
|
||||
rv: a.funcParams(cgn.obj),
|
||||
v: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- func (Value).MapIndex(Value) Value ----------
|
||||
|
||||
// result = rv.MapIndex(key)
|
||||
// result = v.MapIndex(_)
|
||||
type rVMapIndexConstraint struct {
|
||||
cgn *cgnode
|
||||
rv nodeid // (ptr)
|
||||
v nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *rVMapIndexConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.rv)
|
||||
return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVMapIndexConstraint) ptr() nodeid {
|
||||
return c.rv
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
tDyn, m, indirect := a.taggedValue(obj)
|
||||
tMap, _ := tDyn.(*types.Map)
|
||||
for vObj := range delta {
|
||||
tDyn, m, indirect := a.taggedValue(vObj)
|
||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||
if tMap == nil {
|
||||
continue // not a map
|
||||
}
|
||||
@ -112,9 +117,9 @@ func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
panic("indirect tagged object")
|
||||
}
|
||||
|
||||
vObj := a.makeTagged(tMap.Elem(), c.cgn, nil)
|
||||
a.loadOffset(vObj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
|
||||
if a.nodes[c.result].pts.add(vObj) {
|
||||
obj := a.makeTagged(tMap.Elem(), c.cgn, nil)
|
||||
a.loadOffset(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
|
||||
if a.addLabel(c.result, obj) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -126,33 +131,33 @@ func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&rVMapIndexConstraint{
|
||||
cgn: cgn,
|
||||
rv: a.funcParams(cgn.obj),
|
||||
v: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- func (Value).MapKeys() []Value ----------
|
||||
|
||||
// result = rv.MapKeys()
|
||||
// result = v.MapKeys()
|
||||
type rVMapKeysConstraint struct {
|
||||
cgn *cgnode
|
||||
rv nodeid // (ptr)
|
||||
v nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *rVMapKeysConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.rv)
|
||||
return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVMapKeysConstraint) ptr() nodeid {
|
||||
return c.rv
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
tDyn, m, indirect := a.taggedValue(obj)
|
||||
tMap, _ := tDyn.(*types.Map)
|
||||
for vObj := range delta {
|
||||
tDyn, m, indirect := a.taggedValue(vObj)
|
||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||
if tMap == nil {
|
||||
continue // not a map
|
||||
}
|
||||
@ -164,7 +169,7 @@ func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
|
||||
kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
|
||||
a.load(kObj+1, m, a.sizeof(tMap.Key()))
|
||||
if a.nodes[c.result].pts.add(kObj) {
|
||||
if a.addLabel(c.result, kObj) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -180,41 +185,139 @@ func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) {
|
||||
a.endObject(obj, cgn, nil)
|
||||
a.addressOf(a.funcResults(cgn.obj), obj)
|
||||
|
||||
// resolution rule attached to rv
|
||||
a.addConstraint(&rVMapKeysConstraint{
|
||||
cgn: cgn,
|
||||
rv: a.funcParams(cgn.obj),
|
||||
v: a.funcParams(cgn.obj),
|
||||
result: obj + 1, // result is stored in array elems
|
||||
})
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func (Value).Recv(Value) ----------
|
||||
|
||||
// result, _ = v.Recv()
|
||||
type rVRecvConstraint struct {
|
||||
cgn *cgnode
|
||||
v nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *rVRecvConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVRecvConstraint) ptr() nodeid {
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVRecvConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
tDyn, ch, indirect := a.taggedValue(vObj)
|
||||
tChan, _ := tDyn.Underlying().(*types.Chan)
|
||||
if tChan == nil {
|
||||
continue // not a channel
|
||||
}
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
panic("indirect tagged object")
|
||||
}
|
||||
|
||||
tElem := tChan.Elem()
|
||||
elemObj := a.makeTagged(tElem, c.cgn, nil)
|
||||
a.load(elemObj+1, ch, a.sizeof(tElem))
|
||||
if a.addLabel(c.result, elemObj) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰Recv(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&rVRecvConstraint{
|
||||
cgn: cgn,
|
||||
v: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- func (Value).Send(Value) ----------
|
||||
|
||||
// v.Send(x)
|
||||
type rVSendConstraint struct {
|
||||
cgn *cgnode
|
||||
v nodeid // (ptr)
|
||||
x nodeid
|
||||
}
|
||||
|
||||
func (c *rVSendConstraint) String() string {
|
||||
return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
|
||||
}
|
||||
|
||||
func (c *rVSendConstraint) ptr() nodeid {
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for vObj := range delta {
|
||||
tDyn, ch, indirect := a.taggedValue(vObj)
|
||||
tChan, _ := tDyn.Underlying().(*types.Chan)
|
||||
if tChan == nil {
|
||||
continue // not a channel
|
||||
}
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
panic("indirect tagged object")
|
||||
}
|
||||
|
||||
// Extract x's payload to xtmp, then store to channel.
|
||||
tElem := tChan.Elem()
|
||||
xtmp := a.addNodes(tElem, "Send.xtmp")
|
||||
a.typeAssert(tElem, xtmp, c.x)
|
||||
a.store(ch, xtmp, a.sizeof(tElem))
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) {
|
||||
params := a.funcParams(cgn.obj)
|
||||
a.addConstraint(&rVSendConstraint{
|
||||
cgn: cgn,
|
||||
v: params,
|
||||
x: params + 1,
|
||||
})
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func (Value).SetMapIndex(k Value, v Value) ----------
|
||||
|
||||
// rv.SetMapIndex(k, v)
|
||||
// v.SetMapIndex(key, val)
|
||||
type rVSetMapIndexConstraint struct {
|
||||
cgn *cgnode
|
||||
rv nodeid // (ptr)
|
||||
k nodeid
|
||||
v nodeid
|
||||
v nodeid // (ptr)
|
||||
key nodeid
|
||||
val nodeid
|
||||
}
|
||||
|
||||
func (c *rVSetMapIndexConstraint) String() string {
|
||||
return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.rv, c.k, c.v)
|
||||
return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val)
|
||||
}
|
||||
|
||||
func (c *rVSetMapIndexConstraint) ptr() nodeid {
|
||||
return c.rv
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for obj := range delta {
|
||||
tDyn, m, indirect := a.taggedValue(obj)
|
||||
tMap, _ := tDyn.(*types.Map)
|
||||
for vObj := range delta {
|
||||
tDyn, m, indirect := a.taggedValue(vObj)
|
||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||
if tMap == nil {
|
||||
continue // not a map
|
||||
}
|
||||
@ -224,28 +327,27 @@ func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
panic("indirect tagged object")
|
||||
}
|
||||
|
||||
ksize := a.sizeof(tMap.Key())
|
||||
keysize := a.sizeof(tMap.Key())
|
||||
|
||||
// Extract k Value's payload to ktmp, then store to map key.
|
||||
ktmp := a.addNodes(tMap.Key(), "SetMapIndex.ktmp")
|
||||
a.addConstraint(&typeAssertConstraint{tMap.Key(), ktmp, c.k})
|
||||
a.store(m, ktmp, ksize)
|
||||
// Extract key's payload to keytmp, then store to map key.
|
||||
keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
|
||||
a.typeAssert(tMap.Key(), keytmp, c.key)
|
||||
a.store(m, keytmp, keysize)
|
||||
|
||||
// Extract v Value's payload to vtmp, then store to map value.
|
||||
vtmp := a.addNodes(tMap.Elem(), "SetMapIndex.vtmp")
|
||||
a.addConstraint(&typeAssertConstraint{tMap.Elem(), vtmp, c.v})
|
||||
a.storeOffset(m, vtmp, ksize, a.sizeof(tMap.Elem()))
|
||||
// Extract val's payload to vtmp, then store to map value.
|
||||
valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp")
|
||||
a.typeAssert(tMap.Elem(), valtmp, c.val)
|
||||
a.storeOffset(m, valtmp, keysize, a.sizeof(tMap.Elem()))
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
|
||||
// resolution rule attached to rv
|
||||
rv := a.funcParams(cgn.obj)
|
||||
params := a.funcParams(cgn.obj)
|
||||
a.addConstraint(&rVSetMapIndexConstraint{
|
||||
cgn: cgn,
|
||||
rv: rv,
|
||||
k: rv + 1,
|
||||
v: rv + 2,
|
||||
v: params,
|
||||
key: params + 1,
|
||||
val: params + 2,
|
||||
})
|
||||
}
|
||||
|
||||
@ -257,45 +359,300 @@ func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Append(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Copy(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰New(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Select(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func TypeOf(v Value) Type ----------
|
||||
// ---------- func ChanOf(ChanDir, Type) Type ----------
|
||||
|
||||
// result = TypeOf(v)
|
||||
type reflectTypeOfConstraint struct {
|
||||
// result = ChanOf(_, t)
|
||||
type reflectChanOfConstraint struct {
|
||||
cgn *cgnode
|
||||
t nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectChanOfConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *reflectChanOfConstraint) ptr() nodeid {
|
||||
return c.t
|
||||
}
|
||||
|
||||
func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
T := a.rtypeTaggedValue(tObj)
|
||||
// TODO(adonovan): use only the channel direction
|
||||
// provided at the callsite, if constant.
|
||||
for _, dir := range []ast.ChanDir{1, 2, 3} {
|
||||
if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) {
|
||||
params := a.funcParams(cgn.obj)
|
||||
a.addConstraint(&reflectChanOfConstraint{
|
||||
cgn: cgn,
|
||||
t: params + 1,
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- func Indirect(v Value) Value ----------
|
||||
|
||||
// result = Indirect(v)
|
||||
type reflectIndirectConstraint struct {
|
||||
cgn *cgnode
|
||||
v nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.v)
|
||||
func (c *reflectIndirectConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) ptr() nodeid {
|
||||
func (c *reflectIndirectConstraint) ptr() nodeid {
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
tDyn, _, _ := a.taggedValue(obj)
|
||||
for vObj := range delta {
|
||||
tDyn, _, _ := a.taggedValue(vObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
|
||||
if a.nodes[c.result].pts.add(a.makeRtype(tDyn)) {
|
||||
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, a.sizeof(tPtr.Elem()))
|
||||
} else {
|
||||
res = vObj
|
||||
}
|
||||
|
||||
if a.addLabel(c.result, res) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&reflectIndirectConstraint{
|
||||
cgn: cgn,
|
||||
v: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- func MakeChan(Type) Value ----------
|
||||
|
||||
// result = MakeChan(typ)
|
||||
type reflectMakeChanConstraint struct {
|
||||
cgn *cgnode
|
||||
typ nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectMakeChanConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectMakeChanConstraint) ptr() nodeid {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
tChan, ok := T.Underlying().(*types.Chan)
|
||||
if !ok || tChan.Dir() != ast.SEND|ast.RECV {
|
||||
continue // not a bidirectional channel type
|
||||
}
|
||||
|
||||
obj := a.nextNode()
|
||||
a.addNodes(tChan.Elem(), "reflect.MakeChan.value")
|
||||
a.endObject(obj, c.cgn, nil)
|
||||
|
||||
// put its address in a new T-tagged object
|
||||
id := a.makeTagged(T, c.cgn, nil)
|
||||
a.addLabel(id+1, obj)
|
||||
|
||||
// flow the T-tagged object to the result
|
||||
if a.addLabel(c.result, id) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&reflectMakeChanConstraint{
|
||||
cgn: cgn,
|
||||
typ: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func MakeMap(Type) Value ----------
|
||||
|
||||
// result = MakeMap(typ)
|
||||
type reflectMakeMapConstraint struct {
|
||||
cgn *cgnode
|
||||
typ nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectMakeMapConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectMakeMapConstraint) ptr() nodeid {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *reflectMakeMapConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
tMap, ok := T.Underlying().(*types.Map)
|
||||
if !ok {
|
||||
continue // not a map type
|
||||
}
|
||||
|
||||
mapObj := a.nextNode()
|
||||
a.addNodes(tMap.Key(), "reflect.MakeMap.key")
|
||||
a.addNodes(tMap.Elem(), "reflect.MakeMap.value")
|
||||
a.endObject(mapObj, c.cgn, nil)
|
||||
|
||||
// put its address in a new T-tagged object
|
||||
id := a.makeTagged(T, c.cgn, nil)
|
||||
a.addLabel(id+1, mapObj)
|
||||
|
||||
// flow the T-tagged object to the result
|
||||
if a.addLabel(c.result, id) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&reflectMakeMapConstraint{
|
||||
cgn: cgn,
|
||||
typ: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func New(Type) Value ----------
|
||||
|
||||
// result = New(typ)
|
||||
type reflectNewConstraint struct {
|
||||
cgn *cgnode
|
||||
typ nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectNewConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectNewConstraint) ptr() nodeid {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *reflectNewConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
|
||||
// allocate new T object
|
||||
newObj := a.nextNode()
|
||||
a.addNodes(T, "reflect.New")
|
||||
a.endObject(newObj, c.cgn, nil)
|
||||
|
||||
// put its address in a new *T-tagged object
|
||||
id := a.makeTagged(types.NewPointer(T), c.cgn, nil)
|
||||
a.addLabel(id+1, newObj)
|
||||
|
||||
// flow the pointer to the result
|
||||
if a.addLabel(c.result, id) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰New(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&reflectNewConstraint{
|
||||
cgn: cgn,
|
||||
typ: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
|
||||
func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) {
|
||||
ext۰reflect۰New(a, cgn)
|
||||
|
||||
// TODO(adonovan): make it easier to report errors of this form,
|
||||
// which includes the callsite:
|
||||
// a.warnf("unsound: main.reflectNewAt contains a reflect.NewAt() call")
|
||||
a.warnf(cgn.Func().Pos(), "unsound: reflect.NewAt() call")
|
||||
}
|
||||
|
||||
func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰Select(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func TypeOf(v Value) Type ----------
|
||||
|
||||
// result = TypeOf(i)
|
||||
type reflectTypeOfConstraint struct {
|
||||
cgn *cgnode
|
||||
i nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) ptr() nodeid {
|
||||
return c.i
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for iObj := range delta {
|
||||
tDyn, _, _ := a.taggedValue(iObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
|
||||
if a.addLabel(c.result, a.makeRtype(tDyn)) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -307,7 +664,7 @@ func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&reflectTypeOfConstraint{
|
||||
cgn: cgn,
|
||||
v: a.funcParams(cgn.obj),
|
||||
i: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
@ -323,29 +680,25 @@ func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) {
|
||||
|
||||
// ---------- func Zero(Type) Value ----------
|
||||
|
||||
// result = Zero(t)
|
||||
// result = Zero(typ)
|
||||
type reflectZeroConstraint struct {
|
||||
cgn *cgnode
|
||||
t nodeid // (ptr)
|
||||
typ nodeid // (ptr)
|
||||
result nodeid
|
||||
}
|
||||
|
||||
func (c *reflectZeroConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.t)
|
||||
return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectZeroConstraint) ptr() nodeid {
|
||||
return c.t
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
tDyn, v, _ := a.taggedValue(obj)
|
||||
if tDyn != a.reflectRtype {
|
||||
panic("not a *reflect.rtype-tagged value")
|
||||
}
|
||||
T := a.nodes[v].typ
|
||||
for typObj := range delta {
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
|
||||
// memoize using a.reflectZeros[T]
|
||||
var id nodeid
|
||||
@ -355,7 +708,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
id = a.makeTagged(T, c.cgn, nil)
|
||||
a.reflectZeros.Set(T, id)
|
||||
}
|
||||
if a.nodes[c.result].pts.add(id) {
|
||||
if a.addLabel(c.result, id) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -367,7 +720,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
|
||||
a.addConstraint(&reflectZeroConstraint{
|
||||
cgn: cgn,
|
||||
t: a.funcParams(cgn.obj),
|
||||
typ: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
})
|
||||
}
|
||||
@ -392,15 +745,15 @@ func (c *rtypeElemConstraint) ptr() nodeid {
|
||||
}
|
||||
|
||||
func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
// Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
|
||||
type hasElem interface {
|
||||
Elem() types.Type
|
||||
}
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
T := a.nodes[obj].typ // assume obj is an *rtype
|
||||
|
||||
// Works for *types.{Map,Chan,Array,Slice,Pointer}.
|
||||
if T, ok := T.Underlying().(interface {
|
||||
Elem() types.Type
|
||||
}); ok {
|
||||
if a.nodes[c.result].pts.add(a.makeRtype(T.Elem())) {
|
||||
for tObj := range delta {
|
||||
T := a.nodes[tObj].obj.rtype
|
||||
if tHasElem, ok := T.Underlying().(hasElem); ok {
|
||||
if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -422,7 +775,71 @@ func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func (*rtype) In/Out() Type ----------
|
||||
|
||||
// result = In/Out(t)
|
||||
type rtypeInOutConstraint struct {
|
||||
cgn *cgnode
|
||||
t nodeid // (ptr)
|
||||
result nodeid
|
||||
out bool
|
||||
}
|
||||
|
||||
func (c *rtypeInOutConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *rtypeInOutConstraint) ptr() nodeid {
|
||||
return c.t
|
||||
}
|
||||
|
||||
func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
T := a.nodes[tObj].obj.rtype
|
||||
sig, ok := T.Underlying().(*types.Signature)
|
||||
if !ok {
|
||||
continue // not a func type
|
||||
}
|
||||
|
||||
tuple := sig.Params()
|
||||
if c.out {
|
||||
tuple = sig.Results()
|
||||
}
|
||||
// TODO(adonovan): when a function is analyzed
|
||||
// context-sensitively, we should be able to see its
|
||||
// caller's actual parameter's ssa.Values. Refactor
|
||||
// the intrinsic mechanism to allow this. Then if the
|
||||
// value is an int const K, skip the loop and use
|
||||
// tuple.At(K).
|
||||
for i, n := 0, tuple.Len(); i < n; i++ {
|
||||
if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) {
|
||||
a.addConstraint(&rtypeInOutConstraint{
|
||||
cgn: cgn,
|
||||
t: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
out: out,
|
||||
})
|
||||
}
|
||||
|
||||
func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) {
|
||||
ext۰reflect۰rtype۰InOut(a, cgn, false)
|
||||
}
|
||||
|
||||
func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) {
|
||||
ext۰reflect۰rtype۰InOut(a, cgn, true)
|
||||
}
|
||||
|
||||
// ---------- func (*rtype) Key() Type ----------
|
||||
|
||||
@ -443,11 +860,10 @@ func (c *rtypeKeyConstraint) ptr() nodeid {
|
||||
|
||||
func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for obj := range delta {
|
||||
T := a.nodes[obj].typ // assume obj is an *rtype
|
||||
|
||||
for tObj := range delta {
|
||||
T := a.nodes[tObj].obj.rtype
|
||||
if tMap, ok := T.Underlying().(*types.Map); ok {
|
||||
if a.nodes[c.result].pts.add(a.makeRtype(tMap.Key())) {
|
||||
if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@ -467,4 +883,3 @@ func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) {
|
||||
|
||||
func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) {}
|
||||
|
@ -14,27 +14,21 @@ import (
|
||||
)
|
||||
|
||||
func (a *analysis) solve() {
|
||||
a.work.swap()
|
||||
|
||||
// Solver main loop.
|
||||
for round := 1; ; round++ {
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "Solving, round %d\n", round)
|
||||
}
|
||||
|
||||
// Add new constraints to the graph:
|
||||
// static constraints from SSA on round 1,
|
||||
// dynamic constraints from reflection thereafter.
|
||||
a.processNewConstraints()
|
||||
|
||||
if a.work.swap() {
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "Solving, round %d\n", round)
|
||||
}
|
||||
|
||||
// Next iteration.
|
||||
if a.work.empty() {
|
||||
break // done
|
||||
}
|
||||
}
|
||||
|
||||
id := a.work.take()
|
||||
if id == empty {
|
||||
break
|
||||
}
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "\tnode n%d\n", id)
|
||||
}
|
||||
@ -110,9 +104,6 @@ func (a *analysis) processNewConstraints() {
|
||||
if len(n.prevPts) > 0 {
|
||||
stale.add(id)
|
||||
}
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "Adding to worklist n%d\n", id)
|
||||
}
|
||||
a.addWork(id)
|
||||
}
|
||||
}
|
||||
@ -152,6 +143,11 @@ func (a *analysis) solveConstraints(n *node, delta nodeset) {
|
||||
}
|
||||
}
|
||||
|
||||
// addLabel adds label to the points-to set of ptr and reports whether the set grew.
|
||||
func (a *analysis) addLabel(ptr, label nodeid) bool {
|
||||
return a.nodes[ptr].pts.add(label)
|
||||
}
|
||||
|
||||
func (a *analysis) addWork(id nodeid) {
|
||||
a.work.add(id)
|
||||
if a.log != nil {
|
||||
@ -205,6 +201,10 @@ func (a *analysis) onlineCopy(dst, src nodeid) bool {
|
||||
|
||||
// Returns sizeof.
|
||||
// Implicitly adds nodes to worklist.
|
||||
//
|
||||
// TODO(adonovan): now that we support a.copy() during solving, we
|
||||
// could eliminate onlineCopyN, but it's much slower. Investigate.
|
||||
//
|
||||
func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
|
||||
for i := uint32(0); i < sizeof; i++ {
|
||||
if a.onlineCopy(dst, src) {
|
||||
@ -263,7 +263,7 @@ func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
|
||||
if tIface != nil {
|
||||
if types.IsAssignableTo(tDyn, tIface) {
|
||||
if a.nodes[c.dst].pts.add(ifaceObj) {
|
||||
if a.addLabel(c.dst, ifaceObj) {
|
||||
a.addWork(c.dst)
|
||||
}
|
||||
}
|
||||
@ -316,7 +316,7 @@ func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
// Make callsite's fn variable point to identity of
|
||||
// concrete method. (There's no need to add it to
|
||||
// worklist since it never has attached constraints.)
|
||||
a.nodes[c.params].pts.add(fnObj)
|
||||
a.addLabel(c.params, fnObj)
|
||||
|
||||
// Extract value and connect to method's receiver.
|
||||
// Copy payload to method's receiver param (arg0).
|
||||
@ -324,7 +324,6 @@ func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
recvSize := a.sizeof(sig.Recv().Type())
|
||||
a.onlineCopyN(arg0, v, recvSize)
|
||||
|
||||
// Copy iface object payload to method receiver.
|
||||
src := c.params + 1 // skip past identity
|
||||
dst := arg0 + nodeid(recvSize)
|
||||
|
||||
|
21
pointer/testdata/chanreflect.go
vendored
21
pointer/testdata/chanreflect.go
vendored
@ -4,19 +4,16 @@ package main
|
||||
|
||||
import "reflect"
|
||||
|
||||
//
|
||||
// This test is very sensitive to line-number perturbations!
|
||||
|
||||
// Test of channels with reflection.
|
||||
|
||||
var a, b int
|
||||
|
||||
func chanreflect1() {
|
||||
ch := make(chan *int, 0)
|
||||
ch := make(chan *int, 0) // @line cr1make
|
||||
crv := reflect.ValueOf(ch)
|
||||
crv.Send(reflect.ValueOf(&a))
|
||||
print(crv.Interface()) // @types chan *int
|
||||
print(crv.Interface().(chan *int)) // @pointsto makechan@testdata/chanreflect.go:15:12
|
||||
print(crv.Interface().(chan *int)) // @pointsto makechan@cr1make:12
|
||||
print(<-ch) // @pointsto main.a
|
||||
}
|
||||
|
||||
@ -29,25 +26,31 @@ func chanreflect2() {
|
||||
print(r.Interface().(*int)) // @pointsto main.b
|
||||
}
|
||||
|
||||
// TODO(adonovan): the analysis can't yet take advantage of the
|
||||
// ChanOf(dir) parameter so the results are less precise than they
|
||||
// should be: all three directions are returned.
|
||||
|
||||
func chanOfRecv() {
|
||||
// MakeChan(<-chan) is a no-op.
|
||||
t := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(&a))
|
||||
print(reflect.Zero(t).Interface()) // @types <-chan *int
|
||||
print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int
|
||||
print(reflect.MakeChan(t, 0).Interface().(<-chan *int)) // @pointsto
|
||||
print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan>
|
||||
}
|
||||
|
||||
func chanOfSend() {
|
||||
// MakeChan(chan<-) is a no-op.
|
||||
t := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(&a))
|
||||
print(reflect.Zero(t).Interface()) // @types chan<- *int
|
||||
print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int
|
||||
print(reflect.MakeChan(t, 0).Interface().(chan<- *int)) // @pointsto
|
||||
print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan>
|
||||
}
|
||||
|
||||
func chanOfBoth() {
|
||||
t := reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&a))
|
||||
print(reflect.Zero(t).Interface()) // @types chan *int
|
||||
print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int
|
||||
ch := reflect.MakeChan(t, 0)
|
||||
print(ch.Interface().(chan *int)) // @pointsto reflectMakechan@testdata/chanreflect.go:49:24
|
||||
print(ch.Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan>
|
||||
ch.Send(reflect.ValueOf(&b))
|
||||
ch.Interface().(chan *int) <- &a
|
||||
r, _ := ch.Recv()
|
||||
|
52
pointer/testdata/funcreflect.go
vendored
52
pointer/testdata/funcreflect.go
vendored
@ -2,29 +2,43 @@
|
||||
|
||||
package main
|
||||
|
||||
//
|
||||
|
||||
import "reflect"
|
||||
|
||||
var a, b int
|
||||
var zero, a, b int
|
||||
|
||||
func f(p *int) *int {
|
||||
print(p) // @pointsto
|
||||
return &b
|
||||
// func f(p *int) *int {
|
||||
// print(p) // #@pointsto
|
||||
// return &b
|
||||
// }
|
||||
|
||||
// func g(p *bool) {
|
||||
// }
|
||||
|
||||
// func reflectValueCall() {
|
||||
// rvf := reflect.ValueOf(f)
|
||||
// res := rvf.Call([]reflect.Value{reflect.ValueOf(&a)})
|
||||
// print(res[0].Interface()) // #@types
|
||||
// print(res[0].Interface().(*int)) // #@pointsto
|
||||
// }
|
||||
|
||||
// #@calls main.reflectValueCall -> main.f
|
||||
|
||||
func reflectTypeInOut() {
|
||||
var f func(float64, bool) (string, int)
|
||||
// TODO(adonovan): when the In/Out argument is a valid index constant,
|
||||
// only include a single type in the result. Needs some work.
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(-1)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(zero)).Interface()) // @types float64 | bool
|
||||
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types
|
||||
}
|
||||
|
||||
func g(p *bool) {
|
||||
}
|
||||
|
||||
func funcreflect1() {
|
||||
rvf := reflect.ValueOf(f)
|
||||
res := rvf.Call([]reflect.Value{reflect.ValueOf(&a)})
|
||||
print(res[0].Interface()) // @types
|
||||
print(res[0].Interface().(*int)) // @pointsto
|
||||
}
|
||||
|
||||
// @calls main.funcreflect1 -> main.f
|
||||
|
||||
func main() {
|
||||
funcreflect1()
|
||||
//reflectValueCall()
|
||||
reflectTypeInOut()
|
||||
}
|
||||
|
19
pointer/testdata/mapreflect.go
vendored
19
pointer/testdata/mapreflect.go
vendored
@ -9,7 +9,7 @@ import "reflect"
|
||||
var a int
|
||||
var b bool
|
||||
|
||||
func mapreflect1() {
|
||||
func reflectMapKeysIndex() {
|
||||
m := make(map[*int]*bool) // @line mr1make
|
||||
m[&a] = &b
|
||||
|
||||
@ -33,7 +33,7 @@ func mapreflect1() {
|
||||
}
|
||||
}
|
||||
|
||||
func mapreflect2() {
|
||||
func reflectSetMapIndex() {
|
||||
m := make(map[*int]*bool)
|
||||
mrv := reflect.ValueOf(m)
|
||||
mrv.SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b))
|
||||
@ -64,7 +64,16 @@ func mapreflect2() {
|
||||
print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
mapreflect1()
|
||||
mapreflect2()
|
||||
func reflectMakeMap() {
|
||||
t := reflect.TypeOf(map[*int]*bool(nil))
|
||||
v := reflect.MakeMap(t)
|
||||
print(v) // @types map[*int]*bool
|
||||
print(v) // @pointsto <alloc in reflect.MakeMap>
|
||||
}
|
||||
|
||||
func main() {
|
||||
reflectMapKeysIndex()
|
||||
reflectSetMapIndex()
|
||||
reflectMakeMap()
|
||||
// TODO(adonovan): reflect.MapOf(Type)
|
||||
}
|
||||
|
29
pointer/testdata/reflect.go
vendored
29
pointer/testdata/reflect.go
vendored
@ -6,6 +6,7 @@ import "reflect"
|
||||
import "unsafe"
|
||||
|
||||
var a, b int
|
||||
var unknown bool
|
||||
|
||||
func reflectIndirect() {
|
||||
ptr := &a
|
||||
@ -20,19 +21,22 @@ func reflectNewAt() {
|
||||
print(reflect.NewAt(reflect.TypeOf(3), unsafe.Pointer(&x)).Interface()) // @types *int
|
||||
}
|
||||
|
||||
// @warning "unsound: main.reflectNewAt contains a reflect.NewAt.. call"
|
||||
// TODO(adonovan): report the location of the caller, not NewAt.
|
||||
// #warning "unsound: main.reflectNewAt contains a reflect.NewAt.. call"
|
||||
// @warning "unsound: reflect.NewAt.. call"
|
||||
|
||||
func reflectTypeOf() {
|
||||
t := reflect.TypeOf(3)
|
||||
if unknown {
|
||||
t = reflect.TypeOf("foo")
|
||||
}
|
||||
print(t) // @types *reflect.rtype
|
||||
// TODO(adonovan): make types.Eval let us refer to unexported types.
|
||||
print(t) // #@types *reflect.rtype
|
||||
print(reflect.Zero(t).Interface()) // @types int | string
|
||||
newint := reflect.New(t).Interface() // @line rtonew
|
||||
print(newint) // @types *int | *string
|
||||
print(newint.(*int)) // @pointsto reflectAlloc@rtonew:23
|
||||
print(newint.(*string)) // @pointsto reflectAlloc@rtonew:23
|
||||
print(newint.(*int)) // @pointsto <alloc in reflect.New>
|
||||
print(newint.(*string)) // @pointsto <alloc in reflect.New>
|
||||
}
|
||||
|
||||
func reflectTypeElem() {
|
||||
@ -44,26 +48,9 @@ func reflectTypeElem() {
|
||||
print(reflect.Zero(reflect.TypeOf(3).Elem()).Interface()) // @types
|
||||
}
|
||||
|
||||
func reflectTypeInOut() {
|
||||
var f func(float64, bool) (string, int)
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface()) // @types float64
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface()) // @types bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(-1)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(zero)).Interface()) // @types float64 | bool
|
||||
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types
|
||||
}
|
||||
|
||||
func main() {
|
||||
reflectIndirect()
|
||||
reflectNewAt()
|
||||
reflectTypeOf()
|
||||
reflectTypeElem()
|
||||
reflectTypeInOut()
|
||||
}
|
||||
|
||||
var unknown bool
|
||||
var zero int
|
||||
|
@ -278,47 +278,30 @@ func (cs *constraintset) add(c constraint) bool {
|
||||
|
||||
// Worklist -------------------------------------------------------------------
|
||||
|
||||
// TODO(adonovan): interface may not be general enough for certain
|
||||
// implementations, e.g. priority queue
|
||||
//
|
||||
// Uses double-buffering so nodes can be added during iteration.
|
||||
const empty nodeid = 1<<32 - 1
|
||||
|
||||
type worklist interface {
|
||||
empty() bool // Reports whether active buffer is empty.
|
||||
swap() bool // Switches to the shadow buffer if empty().
|
||||
add(nodeid) // Adds a node to the shadow buffer.
|
||||
take() nodeid // Takes a node from the active buffer. Precondition: !empty().
|
||||
add(nodeid) // Adds a node to the set
|
||||
take() nodeid // Takes a node from the set and returns it, or empty
|
||||
}
|
||||
|
||||
// Horribly naive (and nondeterministic) worklist
|
||||
// based on two hash-sets.
|
||||
// Simple nondeterministic worklist based on a built-in map.
|
||||
type mapWorklist struct {
|
||||
active, shadow nodeset
|
||||
}
|
||||
|
||||
func (w *mapWorklist) empty() bool {
|
||||
return len(w.active) == 0
|
||||
}
|
||||
|
||||
func (w *mapWorklist) swap() bool {
|
||||
if w.empty() {
|
||||
w.shadow, w.active = w.active, w.shadow
|
||||
return true
|
||||
}
|
||||
return false
|
||||
set nodeset
|
||||
}
|
||||
|
||||
func (w *mapWorklist) add(n nodeid) {
|
||||
w.shadow[n] = struct{}{}
|
||||
w.set[n] = struct{}{}
|
||||
}
|
||||
|
||||
func (w *mapWorklist) take() nodeid {
|
||||
for k := range w.active {
|
||||
delete(w.active, k)
|
||||
for k := range w.set {
|
||||
delete(w.set, k)
|
||||
return k
|
||||
}
|
||||
panic("worklist.take(): empty active buffer")
|
||||
return empty
|
||||
}
|
||||
|
||||
func makeMapWorklist() worklist {
|
||||
return &mapWorklist{make(nodeset), make(nodeset)}
|
||||
return &mapWorklist{make(nodeset)}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user