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'.")
|
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 useHelp = "Run 'oracle -help' for more information.\n"
|
||||||
|
|
||||||
const helpMessage = `Go source code oracle.
|
const helpMessage = `Go source code oracle.
|
||||||
|
@ -68,8 +71,8 @@ The user manual is available here: http://golang.org/s/oracle-user-manual
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
Describe the syntax at offset 670 in this file (an import spec):
|
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:#670 \
|
% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#530 \
|
||||||
code.google.com/p/go.tools/cmd/oracle
|
code.google.com/p/go.tools/cmd/oracle
|
||||||
|
|
||||||
Print the callgraph of the trivial web-server in JSON format:
|
Print the callgraph of the trivial web-server in JSON format:
|
||||||
|
@ -149,7 +152,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the oracle.
|
// 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 {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s\n"+useHelp, err)
|
fmt.Fprintf(os.Stderr, "%s\n"+useHelp, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -43,7 +43,7 @@ import (
|
||||||
type Oracle struct {
|
type Oracle struct {
|
||||||
out io.Writer // standard output
|
out io.Writer // standard output
|
||||||
prog *ssa.Program // the SSA program [only populated if need&SSA]
|
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
|
// need&AllTypeInfo
|
||||||
typeInfo map[*types.Package]*importer.PackageInfo // type info for all ASTs in the program
|
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).
|
// mode is the query mode ("callers", etc).
|
||||||
// ptalog is the (optional) pointer-analysis log file.
|
// ptalog is the (optional) pointer-analysis log file.
|
||||||
// buildContext is the go/build configuration for locating packages.
|
// 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
|
// Clients that intend to perform multiple queries against the same
|
||||||
// analysis scope should use this pattern instead:
|
// 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
|
// TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos
|
||||||
// depends on the query mode; how should we expose this?
|
// 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)
|
minfo := findMode(mode)
|
||||||
if minfo == nil {
|
if minfo == nil {
|
||||||
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
imp := importer.New(&importer.Config{Build: buildContext})
|
imp := importer.New(&importer.Config{Build: buildContext})
|
||||||
o, err := New(imp, args, ptalog)
|
o, err := New(imp, args, ptalog, reflection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// args specify the main package in importer.CreatePackageFromArgs syntax.
|
||||||
//
|
//
|
||||||
// ptalog is the (optional) pointer-analysis log file.
|
// 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) {
|
func New(imp *importer.Importer, args []string, ptalog io.Writer, reflection bool) (*Oracle, error) {
|
||||||
return newOracle(imp, args, ptalog, needAll)
|
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{
|
o := &Oracle{
|
||||||
prog: ssa.NewProgram(imp.Fset, 0),
|
prog: ssa.NewProgram(imp.Fset, 0),
|
||||||
timers: make(map[string]time.Duration),
|
timers: make(map[string]time.Duration),
|
||||||
}
|
}
|
||||||
o.config.Log = ptalog
|
o.config.Log = ptalog
|
||||||
|
o.config.Reflection = reflection
|
||||||
|
|
||||||
// Load/parse/type-check program from args.
|
// Load/parse/type-check program from args.
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
|
@ -165,7 +165,9 @@ func doQuery(out io.Writer, q *query, useJson bool) {
|
||||||
res, err := oracle.Query([]string{q.filename},
|
res, err := oracle.Query([]string{q.filename},
|
||||||
q.verb,
|
q.verb,
|
||||||
fmt.Sprintf("%s:#%d,#%d", q.filename, q.start, q.end),
|
fmt.Sprintf("%s:#%d,#%d", q.filename, q.start, q.end),
|
||||||
/*PTA-log=*/ nil, &buildContext)
|
nil, // ptalog,
|
||||||
|
&buildContext,
|
||||||
|
true) // reflection
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "\nError: %s\n", stripLocation(err.Error()))
|
fmt.Fprintf(out, "\nError: %s\n", stripLocation(err.Error()))
|
||||||
return
|
return
|
||||||
|
@ -259,7 +261,7 @@ func TestMultipleQueries(t *testing.T) {
|
||||||
|
|
||||||
// Oracle
|
// Oracle
|
||||||
filename := "testdata/src/main/multi.go"
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("oracle.New failed: %s", err)
|
t.Fatalf("oracle.New failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ CONSTRAINT GENERATION:
|
||||||
|
|
||||||
PRESOLVER OPTIMISATIONS
|
PRESOLVER OPTIMISATIONS
|
||||||
- use HVN, HRU, LE, PE, HCD, LCD.
|
- use HVN, HRU, LE, PE, HCD, LCD.
|
||||||
But: LE would lose the precise detail we currently enjoy in each label.
|
|
||||||
|
|
||||||
SOLVER:
|
SOLVER:
|
||||||
- use BDDs and/or sparse bitvectors for ptsets
|
- use BDDs and/or sparse bitvectors for ptsets
|
||||||
|
|
|
@ -185,7 +185,8 @@ type analysis struct {
|
||||||
hasher typemap.Hasher // cache of type hashes
|
hasher typemap.Hasher // cache of type hashes
|
||||||
reflectValueObj types.Object // type symbol for reflect.Value (if present)
|
reflectValueObj types.Object // type symbol for reflect.Value (if present)
|
||||||
reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (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
|
rtypes typemap.M // nodeid of canonical *rtype-tagged object for type T
|
||||||
reflectZeros typemap.M // nodeid of canonical T-tagged object for zero value
|
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 {
|
if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
|
||||||
a.reflectValueObj = reflect.Object.Scope().Lookup("Value")
|
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.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.
|
// Override flattening of reflect.Value, treating it like a basic type.
|
||||||
tReflectValue := a.reflectValueObj.Type()
|
tReflectValue := a.reflectValueObj.Type()
|
||||||
|
@ -265,11 +267,7 @@ func Analyze(config *Config) CallGraphNode {
|
||||||
|
|
||||||
root := a.generate()
|
root := a.generate()
|
||||||
|
|
||||||
// ---------- Presolver ----------
|
//a.optimize()
|
||||||
|
|
||||||
// TODO(adonovan): opt: presolver optimisations.
|
|
||||||
|
|
||||||
// ---------- Solver ----------
|
|
||||||
|
|
||||||
a.solve()
|
a.solve()
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,13 @@ type Config struct {
|
||||||
Mains []*ssa.Package // set of 'main' packages to analyze
|
Mains []*ssa.Package // set of 'main' packages to analyze
|
||||||
root *ssa.Function // synthetic analysis root
|
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 --------
|
// -------- Optional callbacks invoked by the analysis --------
|
||||||
|
|
||||||
// Call is invoked for each discovered call-graph edge. The
|
// 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,
|
The implementation is similar to that described in (Pearce et al,
|
||||||
PASTE'04). Unlike many algorithms which interleave constraint
|
PASTE'04). Unlike many algorithms which interleave constraint
|
||||||
generation and solving, constructing the callgraph as they go, this
|
generation and solving, constructing the callgraph as they go, this
|
||||||
implementation has a strict phase ordering: generation before solving.
|
implementation for the most part observes a phase ordering (generation
|
||||||
Only simple (copy) constraints may be generated during solving. This
|
before solving), with only simple (copy) constraints being generated
|
||||||
improves the traction of presolver optimisations, but imposes certain
|
during solving. (The exception is reflection, which creates various
|
||||||
restrictions, e.g. potential context sensitivity is limited since all
|
constraints during solving as new types flow to reflect.Value
|
||||||
variants must be created a priori.
|
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
|
We intend to add various presolving optimisations such as Pointer and
|
||||||
Location Equivalence from (Hardekopf & Lin, SAS '07) and solver
|
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.addOneNode(T, "reflect.rtype", nil)
|
||||||
a.endObject(obj, nil, nil).rtype = T
|
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].obj.rtype = T
|
||||||
a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
|
a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
|
||||||
a.addressOf(id+1, obj)
|
a.addressOf(id+1, obj)
|
||||||
|
@ -244,6 +244,15 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
|
||||||
return id
|
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
|
// valueNode returns the id of the value node for v, creating it (and
|
||||||
// the association) as needed. It may return zero for uninteresting
|
// the association) as needed. It may return zero for uninteresting
|
||||||
// values containing no pointers.
|
// 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.
|
// addConstraint adds c to the constraint set.
|
||||||
func (a *analysis) addConstraint(c constraint) {
|
func (a *analysis) addConstraint(c constraint) {
|
||||||
a.constraints = append(a.constraints, c)
|
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.
|
// It returns a node whose pts() will be the set of possible call targets.
|
||||||
//
|
//
|
||||||
func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
|
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
|
sig := call.Signature()
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Allocate a contiguous targets/params/results block for this call.
|
// Allocate a contiguous targets/params/results block for this call.
|
||||||
block := a.nextNode()
|
block := a.nextNode()
|
||||||
|
@ -753,6 +765,63 @@ func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
|
||||||
return targets
|
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.
|
// genCall generates contraints for call instruction instr.
|
||||||
func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
|
func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
|
||||||
call := instr.Common()
|
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)
|
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
|
||||||
|
|
||||||
case *ssa.TypeAssert:
|
case *ssa.TypeAssert:
|
||||||
dst, src := a.valueNode(instr), a.valueNode(instr.X)
|
a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X))
|
||||||
a.addConstraint(&typeAssertConstraint{instr.AssertedType, dst, src})
|
|
||||||
|
|
||||||
case *ssa.Slice:
|
case *ssa.Slice:
|
||||||
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
|
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")
|
a.panicNode = a.addNodes(tEface, "panic")
|
||||||
|
|
||||||
// Create nodes and constraints for all methods of reflect.rtype.
|
// 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()
|
mset := rtype.MethodSet()
|
||||||
for i, n := 0, mset.Len(); i < n; i++ {
|
for i, n := 0, mset.Len(); i < n; i++ {
|
||||||
a.valueNode(a.prog.Method(mset.At(i)))
|
a.valueNode(a.prog.Method(mset.At(i)))
|
||||||
|
@ -1135,9 +1205,5 @@ func (a *analysis) generate() *cgnode {
|
||||||
a.genFunc(cgn)
|
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
|
return root
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,8 @@ func init() {
|
||||||
"(reflect.Value).OverflowInt": ext۰NoEffect,
|
"(reflect.Value).OverflowInt": ext۰NoEffect,
|
||||||
"(reflect.Value).OverflowUint": ext۰NoEffect,
|
"(reflect.Value).OverflowUint": ext۰NoEffect,
|
||||||
"(reflect.Value).Pointer": 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).Set": ext۰reflect۰Value۰Set,
|
||||||
"(reflect.Value).SetBool": ext۰NoEffect,
|
"(reflect.Value).SetBool": ext۰NoEffect,
|
||||||
"(reflect.Value).SetBytes": ext۰reflect۰Value۰SetBytes,
|
"(reflect.Value).SetBytes": ext۰reflect۰Value۰SetBytes,
|
||||||
|
@ -83,6 +85,8 @@ func init() {
|
||||||
"(reflect.Value).SetUint": ext۰NoEffect,
|
"(reflect.Value).SetUint": ext۰NoEffect,
|
||||||
"(reflect.Value).Slice": ext۰reflect۰Value۰Slice,
|
"(reflect.Value).Slice": ext۰reflect۰Value۰Slice,
|
||||||
"(reflect.Value).String": ext۰NoEffect,
|
"(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).Type": ext۰NoEffect,
|
||||||
"(reflect.Value).Uint": ext۰NoEffect,
|
"(reflect.Value).Uint": ext۰NoEffect,
|
||||||
"(reflect.Value).UnsafeAddr": ext۰NoEffect,
|
"(reflect.Value).UnsafeAddr": ext۰NoEffect,
|
||||||
|
@ -275,9 +279,13 @@ func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
|
||||||
if !ok {
|
if !ok {
|
||||||
impl = intrinsicsByName[fn.String()] // may be nil
|
impl = intrinsicsByName[fn.String()] // may be nil
|
||||||
|
|
||||||
// Ensure all "reflect" code is treated intrinsically.
|
if fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object {
|
||||||
if impl == nil && fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object {
|
if !a.config.Reflection {
|
||||||
impl = ext۰NotYetImplemented
|
impl = ext۰NoEffect // reflection disabled
|
||||||
|
} else if impl == nil {
|
||||||
|
// Ensure all "reflect" code is treated intrinsically.
|
||||||
|
impl = ext۰NotYetImplemented
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.intrinsics[fn] = impl
|
a.intrinsics[fn] = impl
|
||||||
|
|
|
@ -33,9 +33,11 @@ var inputs = []string{
|
||||||
// "testdata/tmp.go",
|
// "testdata/tmp.go",
|
||||||
|
|
||||||
// Working:
|
// Working:
|
||||||
|
"testdata/a_test.go",
|
||||||
"testdata/another.go",
|
"testdata/another.go",
|
||||||
"testdata/arrays.go",
|
"testdata/arrays.go",
|
||||||
"testdata/channels.go",
|
"testdata/channels.go",
|
||||||
|
"testdata/chanreflect.go",
|
||||||
"testdata/context.go",
|
"testdata/context.go",
|
||||||
"testdata/conv.go",
|
"testdata/conv.go",
|
||||||
"testdata/flow.go",
|
"testdata/flow.go",
|
||||||
|
@ -43,21 +45,19 @@ var inputs = []string{
|
||||||
"testdata/func.go",
|
"testdata/func.go",
|
||||||
"testdata/hello.go",
|
"testdata/hello.go",
|
||||||
"testdata/interfaces.go",
|
"testdata/interfaces.go",
|
||||||
|
"testdata/funcreflect.go",
|
||||||
|
"testdata/mapreflect.go",
|
||||||
"testdata/maps.go",
|
"testdata/maps.go",
|
||||||
"testdata/panic.go",
|
"testdata/panic.go",
|
||||||
"testdata/recur.go",
|
"testdata/recur.go",
|
||||||
|
"testdata/reflect.go",
|
||||||
"testdata/structs.go",
|
"testdata/structs.go",
|
||||||
"testdata/a_test.go",
|
|
||||||
"testdata/mapreflect.go",
|
|
||||||
|
|
||||||
// TODO(adonovan): get these tests (of reflection) passing.
|
// TODO(adonovan): get these tests (of reflection) passing.
|
||||||
// (The tests are mostly sound since they were used for a
|
// (The tests are mostly sound since they were used for a
|
||||||
// previous implementation.)
|
// previous implementation.)
|
||||||
// "testdata/funcreflect.go",
|
|
||||||
// "testdata/arrayreflect.go",
|
// "testdata/arrayreflect.go",
|
||||||
// "testdata/chanreflect.go",
|
|
||||||
// "testdata/finalizer.go",
|
// "testdata/finalizer.go",
|
||||||
// "testdata/reflect.go",
|
|
||||||
// "testdata/structreflect.go",
|
// "testdata/structreflect.go",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,8 +290,9 @@ func doOneInput(input, filename string) bool {
|
||||||
|
|
||||||
// Run the analysis.
|
// Run the analysis.
|
||||||
config := &pointer.Config{
|
config := &pointer.Config{
|
||||||
Mains: []*ssa.Package{ptrmain},
|
Reflection: true,
|
||||||
Log: &log,
|
Mains: []*ssa.Package{ptrmain},
|
||||||
|
Log: &log,
|
||||||
Print: func(site *ssa.CallCommon, p pointer.Pointer) {
|
Print: func(site *ssa.CallCommon, p pointer.Pointer) {
|
||||||
probes = append(probes, probe{site, p})
|
probes = append(probes, probe{site, p})
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,10 +4,14 @@ package pointer
|
||||||
// constraints arising from the use of reflection in the target
|
// constraints arising from the use of reflection in the target
|
||||||
// program. See doc.go for explanation of the representation.
|
// 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
|
// TODO(adonovan): fix: most of the reflect API permits implicit
|
||||||
// conversions due to assignability, e.g. m.MapIndex(k) is ok if T(k)
|
// 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
|
// 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
|
// To avoid proliferation of equivalent labels, instrinsics should
|
||||||
// memoize as much as possible, like TypeOf and Zero do for their
|
// memoize as much as possible, like TypeOf and Zero do for their
|
||||||
|
@ -17,6 +21,7 @@ package pointer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
|
||||||
"code.google.com/p/go.tools/go/types"
|
"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 ----------
|
// ---------- func (Value).Interface() Value ----------
|
||||||
|
|
||||||
// result = rv.Interface()
|
// result = v.Interface()
|
||||||
type rVInterfaceConstraint struct {
|
type rVInterfaceConstraint struct {
|
||||||
rv nodeid // (ptr)
|
v nodeid // (ptr)
|
||||||
result nodeid
|
result nodeid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVInterfaceConstraint) String() string {
|
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 {
|
func (c *rVInterfaceConstraint) ptr() nodeid {
|
||||||
return c.rv
|
return c.v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
resultPts := &a.nodes[c.result].pts
|
resultPts := &a.nodes[c.result].pts
|
||||||
changed := false
|
changed := false
|
||||||
for obj := range delta {
|
for vObj := range delta {
|
||||||
tDyn, _, indirect := a.taggedValue(obj)
|
tDyn, _, indirect := a.taggedValue(vObj)
|
||||||
if tDyn == nil {
|
if tDyn == nil {
|
||||||
panic("not a tagged object")
|
panic("not a tagged object")
|
||||||
}
|
}
|
||||||
|
@ -65,7 +70,7 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
panic("indirect tagged object")
|
panic("indirect tagged object")
|
||||||
}
|
}
|
||||||
|
|
||||||
if resultPts.add(obj) {
|
if resultPts.add(vObj) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,33 +81,33 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
|
|
||||||
func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
|
func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
|
||||||
a.addConstraint(&rVInterfaceConstraint{
|
a.addConstraint(&rVInterfaceConstraint{
|
||||||
rv: a.funcParams(cgn.obj),
|
v: a.funcParams(cgn.obj),
|
||||||
result: a.funcResults(cgn.obj),
|
result: a.funcResults(cgn.obj),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- func (Value).MapIndex(Value) Value ----------
|
// ---------- func (Value).MapIndex(Value) Value ----------
|
||||||
|
|
||||||
// result = rv.MapIndex(key)
|
// result = v.MapIndex(_)
|
||||||
type rVMapIndexConstraint struct {
|
type rVMapIndexConstraint struct {
|
||||||
cgn *cgnode
|
cgn *cgnode
|
||||||
rv nodeid // (ptr)
|
v nodeid // (ptr)
|
||||||
result nodeid
|
result nodeid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVMapIndexConstraint) String() string {
|
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 {
|
func (c *rVMapIndexConstraint) ptr() nodeid {
|
||||||
return c.rv
|
return c.v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
changed := false
|
changed := false
|
||||||
for obj := range delta {
|
for vObj := range delta {
|
||||||
tDyn, m, indirect := a.taggedValue(obj)
|
tDyn, m, indirect := a.taggedValue(vObj)
|
||||||
tMap, _ := tDyn.(*types.Map)
|
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||||
if tMap == nil {
|
if tMap == nil {
|
||||||
continue // not a map
|
continue // not a map
|
||||||
}
|
}
|
||||||
|
@ -112,9 +117,9 @@ func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
panic("indirect tagged object")
|
panic("indirect tagged object")
|
||||||
}
|
}
|
||||||
|
|
||||||
vObj := a.makeTagged(tMap.Elem(), c.cgn, nil)
|
obj := a.makeTagged(tMap.Elem(), c.cgn, nil)
|
||||||
a.loadOffset(vObj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
|
a.loadOffset(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
|
||||||
if a.nodes[c.result].pts.add(vObj) {
|
if a.addLabel(c.result, obj) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,33 +131,33 @@ func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
|
func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
|
||||||
a.addConstraint(&rVMapIndexConstraint{
|
a.addConstraint(&rVMapIndexConstraint{
|
||||||
cgn: cgn,
|
cgn: cgn,
|
||||||
rv: a.funcParams(cgn.obj),
|
v: a.funcParams(cgn.obj),
|
||||||
result: a.funcResults(cgn.obj),
|
result: a.funcResults(cgn.obj),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- func (Value).MapKeys() []Value ----------
|
// ---------- func (Value).MapKeys() []Value ----------
|
||||||
|
|
||||||
// result = rv.MapKeys()
|
// result = v.MapKeys()
|
||||||
type rVMapKeysConstraint struct {
|
type rVMapKeysConstraint struct {
|
||||||
cgn *cgnode
|
cgn *cgnode
|
||||||
rv nodeid // (ptr)
|
v nodeid // (ptr)
|
||||||
result nodeid
|
result nodeid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVMapKeysConstraint) String() string {
|
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 {
|
func (c *rVMapKeysConstraint) ptr() nodeid {
|
||||||
return c.rv
|
return c.v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
changed := false
|
changed := false
|
||||||
for obj := range delta {
|
for vObj := range delta {
|
||||||
tDyn, m, indirect := a.taggedValue(obj)
|
tDyn, m, indirect := a.taggedValue(vObj)
|
||||||
tMap, _ := tDyn.(*types.Map)
|
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||||
if tMap == nil {
|
if tMap == nil {
|
||||||
continue // not a map
|
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)
|
kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
|
||||||
a.load(kObj+1, m, a.sizeof(tMap.Key()))
|
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
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,41 +185,139 @@ func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) {
|
||||||
a.endObject(obj, cgn, nil)
|
a.endObject(obj, cgn, nil)
|
||||||
a.addressOf(a.funcResults(cgn.obj), obj)
|
a.addressOf(a.funcResults(cgn.obj), obj)
|
||||||
|
|
||||||
// resolution rule attached to rv
|
|
||||||
a.addConstraint(&rVMapKeysConstraint{
|
a.addConstraint(&rVMapKeysConstraint{
|
||||||
cgn: cgn,
|
cgn: cgn,
|
||||||
rv: a.funcParams(cgn.obj),
|
v: a.funcParams(cgn.obj),
|
||||||
result: obj + 1, // result is stored in array elems
|
result: obj + 1, // result is stored in array elems
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode) {}
|
func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode) {}
|
||||||
func ext۰reflect۰Value۰MethodByName(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) ----------
|
// ---------- func (Value).SetMapIndex(k Value, v Value) ----------
|
||||||
|
|
||||||
// rv.SetMapIndex(k, v)
|
// v.SetMapIndex(key, val)
|
||||||
type rVSetMapIndexConstraint struct {
|
type rVSetMapIndexConstraint struct {
|
||||||
cgn *cgnode
|
cgn *cgnode
|
||||||
rv nodeid // (ptr)
|
v nodeid // (ptr)
|
||||||
k nodeid
|
key nodeid
|
||||||
v nodeid
|
val nodeid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVSetMapIndexConstraint) String() string {
|
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 {
|
func (c *rVSetMapIndexConstraint) ptr() nodeid {
|
||||||
return c.rv
|
return c.v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
for obj := range delta {
|
for vObj := range delta {
|
||||||
tDyn, m, indirect := a.taggedValue(obj)
|
tDyn, m, indirect := a.taggedValue(vObj)
|
||||||
tMap, _ := tDyn.(*types.Map)
|
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||||
if tMap == nil {
|
if tMap == nil {
|
||||||
continue // not a map
|
continue // not a map
|
||||||
}
|
}
|
||||||
|
@ -224,28 +327,27 @@ func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
panic("indirect tagged object")
|
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.
|
// Extract key's payload to keytmp, then store to map key.
|
||||||
ktmp := a.addNodes(tMap.Key(), "SetMapIndex.ktmp")
|
keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
|
||||||
a.addConstraint(&typeAssertConstraint{tMap.Key(), ktmp, c.k})
|
a.typeAssert(tMap.Key(), keytmp, c.key)
|
||||||
a.store(m, ktmp, ksize)
|
a.store(m, keytmp, keysize)
|
||||||
|
|
||||||
// Extract v Value's payload to vtmp, then store to map value.
|
// Extract val's payload to vtmp, then store to map value.
|
||||||
vtmp := a.addNodes(tMap.Elem(), "SetMapIndex.vtmp")
|
valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp")
|
||||||
a.addConstraint(&typeAssertConstraint{tMap.Elem(), vtmp, c.v})
|
a.typeAssert(tMap.Elem(), valtmp, c.val)
|
||||||
a.storeOffset(m, vtmp, ksize, a.sizeof(tMap.Elem()))
|
a.storeOffset(m, valtmp, keysize, a.sizeof(tMap.Elem()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
|
func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
|
||||||
// resolution rule attached to rv
|
params := a.funcParams(cgn.obj)
|
||||||
rv := a.funcParams(cgn.obj)
|
|
||||||
a.addConstraint(&rVSetMapIndexConstraint{
|
a.addConstraint(&rVSetMapIndexConstraint{
|
||||||
cgn: cgn,
|
cgn: cgn,
|
||||||
rv: rv,
|
v: params,
|
||||||
k: rv + 1,
|
key: params + 1,
|
||||||
v: rv + 2,
|
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۰Append(a *analysis, cgn *cgnode) {}
|
||||||
func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {}
|
func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {}
|
||||||
func ext۰reflect۰Copy(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)
|
// result = ChanOf(_, t)
|
||||||
type reflectTypeOfConstraint struct {
|
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
|
cgn *cgnode
|
||||||
v nodeid // (ptr)
|
v nodeid // (ptr)
|
||||||
result nodeid
|
result nodeid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *reflectTypeOfConstraint) String() string {
|
func (c *reflectIndirectConstraint) String() string {
|
||||||
return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.v)
|
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
|
return c.v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
changed := false
|
changed := false
|
||||||
for obj := range delta {
|
for vObj := range delta {
|
||||||
tDyn, _, _ := a.taggedValue(obj)
|
tDyn, _, _ := a.taggedValue(vObj)
|
||||||
if tDyn == nil {
|
if tDyn == nil {
|
||||||
panic("not a tagged value")
|
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
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,7 +664,7 @@ func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
|
func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
|
||||||
a.addConstraint(&reflectTypeOfConstraint{
|
a.addConstraint(&reflectTypeOfConstraint{
|
||||||
cgn: cgn,
|
cgn: cgn,
|
||||||
v: a.funcParams(cgn.obj),
|
i: a.funcParams(cgn.obj),
|
||||||
result: a.funcResults(cgn.obj),
|
result: a.funcResults(cgn.obj),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -323,29 +680,25 @@ func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) {
|
||||||
|
|
||||||
// ---------- func Zero(Type) Value ----------
|
// ---------- func Zero(Type) Value ----------
|
||||||
|
|
||||||
// result = Zero(t)
|
// result = Zero(typ)
|
||||||
type reflectZeroConstraint struct {
|
type reflectZeroConstraint struct {
|
||||||
cgn *cgnode
|
cgn *cgnode
|
||||||
t nodeid // (ptr)
|
typ nodeid // (ptr)
|
||||||
result nodeid
|
result nodeid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *reflectZeroConstraint) String() string {
|
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 {
|
func (c *reflectZeroConstraint) ptr() nodeid {
|
||||||
return c.t
|
return c.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
changed := false
|
changed := false
|
||||||
for obj := range delta {
|
for typObj := range delta {
|
||||||
tDyn, v, _ := a.taggedValue(obj)
|
T := a.rtypeTaggedValue(typObj)
|
||||||
if tDyn != a.reflectRtype {
|
|
||||||
panic("not a *reflect.rtype-tagged value")
|
|
||||||
}
|
|
||||||
T := a.nodes[v].typ
|
|
||||||
|
|
||||||
// memoize using a.reflectZeros[T]
|
// memoize using a.reflectZeros[T]
|
||||||
var id nodeid
|
var id nodeid
|
||||||
|
@ -355,7 +708,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
id = a.makeTagged(T, c.cgn, nil)
|
id = a.makeTagged(T, c.cgn, nil)
|
||||||
a.reflectZeros.Set(T, id)
|
a.reflectZeros.Set(T, id)
|
||||||
}
|
}
|
||||||
if a.nodes[c.result].pts.add(id) {
|
if a.addLabel(c.result, id) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,7 +720,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
|
func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
|
||||||
a.addConstraint(&reflectZeroConstraint{
|
a.addConstraint(&reflectZeroConstraint{
|
||||||
cgn: cgn,
|
cgn: cgn,
|
||||||
t: a.funcParams(cgn.obj),
|
typ: a.funcParams(cgn.obj),
|
||||||
result: a.funcResults(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) {
|
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
|
changed := false
|
||||||
for obj := range delta {
|
for tObj := range delta {
|
||||||
T := a.nodes[obj].typ // assume obj is an *rtype
|
T := a.nodes[tObj].obj.rtype
|
||||||
|
if tHasElem, ok := T.Underlying().(hasElem); ok {
|
||||||
// Works for *types.{Map,Chan,Array,Slice,Pointer}.
|
if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
|
||||||
if T, ok := T.Underlying().(interface {
|
|
||||||
Elem() types.Type
|
|
||||||
}); ok {
|
|
||||||
if a.nodes[c.result].pts.add(a.makeRtype(T.Elem())) {
|
|
||||||
changed = true
|
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۰FieldByIndex(a *analysis, cgn *cgnode) {}
|
||||||
func ext۰reflect۰rtype۰FieldByName(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۰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 ----------
|
// ---------- func (*rtype) Key() Type ----------
|
||||||
|
|
||||||
|
@ -443,11 +860,10 @@ func (c *rtypeKeyConstraint) ptr() nodeid {
|
||||||
|
|
||||||
func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
changed := false
|
changed := false
|
||||||
for obj := range delta {
|
for tObj := range delta {
|
||||||
T := a.nodes[obj].typ // assume obj is an *rtype
|
T := a.nodes[tObj].obj.rtype
|
||||||
|
|
||||||
if tMap, ok := T.Underlying().(*types.Map); ok {
|
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
|
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۰Method(a *analysis, cgn *cgnode) {}
|
||||||
func ext۰reflect۰rtype۰MethodByName(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() {
|
func (a *analysis) solve() {
|
||||||
a.work.swap()
|
|
||||||
|
|
||||||
// Solver main loop.
|
// Solver main loop.
|
||||||
for round := 1; ; round++ {
|
for round := 1; ; round++ {
|
||||||
|
if a.log != nil {
|
||||||
|
fmt.Fprintf(a.log, "Solving, round %d\n", round)
|
||||||
|
}
|
||||||
|
|
||||||
// Add new constraints to the graph:
|
// Add new constraints to the graph:
|
||||||
// static constraints from SSA on round 1,
|
// static constraints from SSA on round 1,
|
||||||
// dynamic constraints from reflection thereafter.
|
// dynamic constraints from reflection thereafter.
|
||||||
a.processNewConstraints()
|
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()
|
id := a.work.take()
|
||||||
|
if id == empty {
|
||||||
|
break
|
||||||
|
}
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
fmt.Fprintf(a.log, "\tnode n%d\n", id)
|
fmt.Fprintf(a.log, "\tnode n%d\n", id)
|
||||||
}
|
}
|
||||||
|
@ -110,9 +104,6 @@ func (a *analysis) processNewConstraints() {
|
||||||
if len(n.prevPts) > 0 {
|
if len(n.prevPts) > 0 {
|
||||||
stale.add(id)
|
stale.add(id)
|
||||||
}
|
}
|
||||||
if a.log != nil {
|
|
||||||
fmt.Fprintf(a.log, "Adding to worklist n%d\n", id)
|
|
||||||
}
|
|
||||||
a.addWork(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) {
|
func (a *analysis) addWork(id nodeid) {
|
||||||
a.work.add(id)
|
a.work.add(id)
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
|
@ -205,6 +201,10 @@ func (a *analysis) onlineCopy(dst, src nodeid) bool {
|
||||||
|
|
||||||
// Returns sizeof.
|
// Returns sizeof.
|
||||||
// Implicitly adds nodes to worklist.
|
// 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 {
|
func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
|
||||||
for i := uint32(0); i < sizeof; i++ {
|
for i := uint32(0); i < sizeof; i++ {
|
||||||
if a.onlineCopy(dst, src) {
|
if a.onlineCopy(dst, src) {
|
||||||
|
@ -263,7 +263,7 @@ func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||||
|
|
||||||
if tIface != nil {
|
if tIface != nil {
|
||||||
if types.IsAssignableTo(tDyn, tIface) {
|
if types.IsAssignableTo(tDyn, tIface) {
|
||||||
if a.nodes[c.dst].pts.add(ifaceObj) {
|
if a.addLabel(c.dst, ifaceObj) {
|
||||||
a.addWork(c.dst)
|
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
|
// Make callsite's fn variable point to identity of
|
||||||
// concrete method. (There's no need to add it to
|
// concrete method. (There's no need to add it to
|
||||||
// worklist since it never has attached constraints.)
|
// 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.
|
// Extract value and connect to method's receiver.
|
||||||
// Copy payload to method's receiver param (arg0).
|
// 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())
|
recvSize := a.sizeof(sig.Recv().Type())
|
||||||
a.onlineCopyN(arg0, v, recvSize)
|
a.onlineCopyN(arg0, v, recvSize)
|
||||||
|
|
||||||
// Copy iface object payload to method receiver.
|
|
||||||
src := c.params + 1 // skip past identity
|
src := c.params + 1 // skip past identity
|
||||||
dst := arg0 + nodeid(recvSize)
|
dst := arg0 + nodeid(recvSize)
|
||||||
|
|
||||||
|
|
|
@ -4,19 +4,16 @@ package main
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
//
|
|
||||||
// This test is very sensitive to line-number perturbations!
|
|
||||||
|
|
||||||
// Test of channels with reflection.
|
// Test of channels with reflection.
|
||||||
|
|
||||||
var a, b int
|
var a, b int
|
||||||
|
|
||||||
func chanreflect1() {
|
func chanreflect1() {
|
||||||
ch := make(chan *int, 0)
|
ch := make(chan *int, 0) // @line cr1make
|
||||||
crv := reflect.ValueOf(ch)
|
crv := reflect.ValueOf(ch)
|
||||||
crv.Send(reflect.ValueOf(&a))
|
crv.Send(reflect.ValueOf(&a))
|
||||||
print(crv.Interface()) // @types chan *int
|
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
|
print(<-ch) // @pointsto main.a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,25 +26,31 @@ func chanreflect2() {
|
||||||
print(r.Interface().(*int)) // @pointsto main.b
|
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() {
|
func chanOfRecv() {
|
||||||
// MakeChan(<-chan) is a no-op.
|
// MakeChan(<-chan) is a no-op.
|
||||||
t := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(&a))
|
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
|
||||||
|
print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan>
|
||||||
}
|
}
|
||||||
|
|
||||||
func chanOfSend() {
|
func chanOfSend() {
|
||||||
// MakeChan(chan<-) is a no-op.
|
// MakeChan(chan<-) is a no-op.
|
||||||
t := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(&a))
|
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
|
||||||
|
print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan>
|
||||||
}
|
}
|
||||||
|
|
||||||
func chanOfBoth() {
|
func chanOfBoth() {
|
||||||
t := reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&a))
|
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)
|
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.Send(reflect.ValueOf(&b))
|
||||||
ch.Interface().(chan *int) <- &a
|
ch.Interface().(chan *int) <- &a
|
||||||
r, _ := ch.Recv()
|
r, _ := ch.Recv()
|
||||||
|
|
|
@ -2,29 +2,43 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
var a, b int
|
var zero, a, b int
|
||||||
|
|
||||||
func f(p *int) *int {
|
// func f(p *int) *int {
|
||||||
print(p) // @pointsto
|
// print(p) // #@pointsto
|
||||||
return &b
|
// 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() {
|
func main() {
|
||||||
funcreflect1()
|
//reflectValueCall()
|
||||||
|
reflectTypeInOut()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import "reflect"
|
||||||
var a int
|
var a int
|
||||||
var b bool
|
var b bool
|
||||||
|
|
||||||
func mapreflect1() {
|
func reflectMapKeysIndex() {
|
||||||
m := make(map[*int]*bool) // @line mr1make
|
m := make(map[*int]*bool) // @line mr1make
|
||||||
m[&a] = &b
|
m[&a] = &b
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ func mapreflect1() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapreflect2() {
|
func reflectSetMapIndex() {
|
||||||
m := make(map[*int]*bool)
|
m := make(map[*int]*bool)
|
||||||
mrv := reflect.ValueOf(m)
|
mrv := reflect.ValueOf(m)
|
||||||
mrv.SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b))
|
mrv.SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b))
|
||||||
|
@ -64,7 +64,16 @@ func mapreflect2() {
|
||||||
print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool
|
print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func reflectMakeMap() {
|
||||||
mapreflect1()
|
t := reflect.TypeOf(map[*int]*bool(nil))
|
||||||
mapreflect2()
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import "reflect"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var a, b int
|
var a, b int
|
||||||
|
var unknown bool
|
||||||
|
|
||||||
func reflectIndirect() {
|
func reflectIndirect() {
|
||||||
ptr := &a
|
ptr := &a
|
||||||
|
@ -20,19 +21,22 @@ func reflectNewAt() {
|
||||||
print(reflect.NewAt(reflect.TypeOf(3), unsafe.Pointer(&x)).Interface()) // @types *int
|
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() {
|
func reflectTypeOf() {
|
||||||
t := reflect.TypeOf(3)
|
t := reflect.TypeOf(3)
|
||||||
if unknown {
|
if unknown {
|
||||||
t = reflect.TypeOf("foo")
|
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
|
print(reflect.Zero(t).Interface()) // @types int | string
|
||||||
newint := reflect.New(t).Interface() // @line rtonew
|
newint := reflect.New(t).Interface() // @line rtonew
|
||||||
print(newint) // @types *int | *string
|
print(newint) // @types *int | *string
|
||||||
print(newint.(*int)) // @pointsto reflectAlloc@rtonew:23
|
print(newint.(*int)) // @pointsto <alloc in reflect.New>
|
||||||
print(newint.(*string)) // @pointsto reflectAlloc@rtonew:23
|
print(newint.(*string)) // @pointsto <alloc in reflect.New>
|
||||||
}
|
}
|
||||||
|
|
||||||
func reflectTypeElem() {
|
func reflectTypeElem() {
|
||||||
|
@ -44,26 +48,9 @@ func reflectTypeElem() {
|
||||||
print(reflect.Zero(reflect.TypeOf(3).Elem()).Interface()) // @types
|
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() {
|
func main() {
|
||||||
reflectIndirect()
|
reflectIndirect()
|
||||||
reflectNewAt()
|
reflectNewAt()
|
||||||
reflectTypeOf()
|
reflectTypeOf()
|
||||||
reflectTypeElem()
|
reflectTypeElem()
|
||||||
reflectTypeInOut()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var unknown bool
|
|
||||||
var zero int
|
|
||||||
|
|
|
@ -278,47 +278,30 @@ func (cs *constraintset) add(c constraint) bool {
|
||||||
|
|
||||||
// Worklist -------------------------------------------------------------------
|
// Worklist -------------------------------------------------------------------
|
||||||
|
|
||||||
// TODO(adonovan): interface may not be general enough for certain
|
const empty nodeid = 1<<32 - 1
|
||||||
// implementations, e.g. priority queue
|
|
||||||
//
|
|
||||||
// Uses double-buffering so nodes can be added during iteration.
|
|
||||||
type worklist interface {
|
type worklist interface {
|
||||||
empty() bool // Reports whether active buffer is empty.
|
add(nodeid) // Adds a node to the set
|
||||||
swap() bool // Switches to the shadow buffer if empty().
|
take() nodeid // Takes a node from the set and returns it, or empty
|
||||||
add(nodeid) // Adds a node to the shadow buffer.
|
|
||||||
take() nodeid // Takes a node from the active buffer. Precondition: !empty().
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Horribly naive (and nondeterministic) worklist
|
// Simple nondeterministic worklist based on a built-in map.
|
||||||
// based on two hash-sets.
|
|
||||||
type mapWorklist struct {
|
type mapWorklist struct {
|
||||||
active, shadow nodeset
|
set 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *mapWorklist) add(n nodeid) {
|
func (w *mapWorklist) add(n nodeid) {
|
||||||
w.shadow[n] = struct{}{}
|
w.set[n] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *mapWorklist) take() nodeid {
|
func (w *mapWorklist) take() nodeid {
|
||||||
for k := range w.active {
|
for k := range w.set {
|
||||||
delete(w.active, k)
|
delete(w.set, k)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
panic("worklist.take(): empty active buffer")
|
return empty
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMapWorklist() worklist {
|
func makeMapWorklist() worklist {
|
||||||
return &mapWorklist{make(nodeset), make(nodeset)}
|
return &mapWorklist{make(nodeset)}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue