mirror of
https://github.com/golang/go
synced 2024-11-18 21:05:02 -07:00
go.tools/pointer: replace Config.Print (callback) with a Result.PrintCalls (map).
This avoids leaking nodeids into client code before the analysis has had a chance to run the (forthcoming) constraint optimizer, which renumbers them. R=crawshaw CC=golang-dev https://golang.org/cl/39410043
This commit is contained in:
parent
05f8c7dc66
commit
5d70784aca
@ -259,11 +259,11 @@ func Analyze(config *Config) *Result {
|
||||
flattenMemo: make(map[types.Type][]*fieldInfo),
|
||||
hasher: typemap.MakeHasher(),
|
||||
intrinsics: make(map[*ssa.Function]intrinsic),
|
||||
probes: make(map[*ssa.CallCommon]nodeid),
|
||||
work: makeMapWorklist(),
|
||||
result: &Result{
|
||||
Queries: make(map[ssa.Value][]Pointer),
|
||||
IndirectQueries: make(map[ssa.Value][]Pointer),
|
||||
PrintCalls: make(map[*ssa.CallCommon]Pointer),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -33,19 +33,13 @@ type Config struct {
|
||||
// If enabled, the graph will be available in Result.CallGraph.
|
||||
BuildCallGraph bool
|
||||
|
||||
// Print is invoked during the analysis for each discovered
|
||||
// call to the built-in print(x), providing a convenient way
|
||||
// to identify arbitrary expressions of interest in the tests.
|
||||
// QueryPrintCalls causes the analysis to record (in
|
||||
// Result.PrintCalls) the points-to set of the first operand
|
||||
// of each discovered call to the built-in print(x), providing
|
||||
// a convenient way to identify arbitrary expressions of
|
||||
// interest in the tests.
|
||||
//
|
||||
// Pointer p may be saved until the analysis is complete, at
|
||||
// which point its methods provide access to the analysis
|
||||
// (The result of callings its methods within the Print
|
||||
// callback is undefined.)
|
||||
//
|
||||
// CanPoint(site.Args[0].Type()) reports whether p is
|
||||
// pointerlike.
|
||||
//
|
||||
Print func(site *ssa.CallCommon, p Pointer)
|
||||
QueryPrintCalls bool
|
||||
|
||||
// The client populates Queries[v] or IndirectQueries[v]
|
||||
// for each ssa.Value v of interest, to request that the
|
||||
@ -57,12 +51,12 @@ type Config struct {
|
||||
// to source-level lvalues, e.g. an *ssa.Global.)
|
||||
//
|
||||
// The analysis populates the corresponding
|
||||
// Results.{Indirect,}Queries map when it creates the pointer
|
||||
// Result.{Indirect,}Queries map when it creates the pointer
|
||||
// variable for v or *v. Upon completion the client can
|
||||
// inspect that map for the results.
|
||||
//
|
||||
// If a Value belongs to a function that the analysis treats
|
||||
// context-sensitively, the corresponding Results.{Indirect,}Queries
|
||||
// context-sensitively, the corresponding Result.{Indirect,}Queries
|
||||
// slice may have multiple Pointers, one per distinct context.
|
||||
// Use PointsToCombined to merge them.
|
||||
//
|
||||
@ -113,10 +107,11 @@ type Warning struct {
|
||||
// See Config for how to request the various Result components.
|
||||
//
|
||||
type Result struct {
|
||||
CallGraph call.Graph // discovered call graph
|
||||
Queries map[ssa.Value][]Pointer // pts(v) for each v in Config.Queries.
|
||||
IndirectQueries map[ssa.Value][]Pointer // pts(*v) for each v in Config.IndirectQueries.
|
||||
Warnings []Warning // warnings of unsoundness
|
||||
CallGraph call.Graph // discovered call graph
|
||||
Queries map[ssa.Value][]Pointer // pts(v) for each v in Config.Queries.
|
||||
IndirectQueries map[ssa.Value][]Pointer // pts(*v) for each v in Config.IndirectQueries.
|
||||
Warnings []Warning // warnings of unsoundness
|
||||
PrintCalls map[*ssa.CallCommon]Pointer // pts(x) for each call to print(x)
|
||||
}
|
||||
|
||||
// A Pointer is an equivalence class of pointerlike values.
|
||||
|
@ -528,20 +528,20 @@ func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) {
|
||||
// Analytically print is a no-op, but it's a convenient hook
|
||||
// for testing the pts of an expression, so we notify the client.
|
||||
// Existing uses in Go core libraries are few and harmless.
|
||||
if Print := a.config.Print; Print != nil {
|
||||
if a.config.QueryPrintCalls {
|
||||
// Due to context-sensitivity, we may encounter
|
||||
// the same print() call in many contexts, so
|
||||
// we merge them to a canonical node.
|
||||
probe := a.probes[call]
|
||||
|
||||
t := call.Args[0].Type()
|
||||
|
||||
// First time? Create the canonical probe node.
|
||||
if probe == 0 {
|
||||
probe = a.addNodes(t, "print")
|
||||
a.probes[call] = probe
|
||||
Print(call, Pointer{a, nil, probe}) // notify client
|
||||
ptr, ok := a.result.PrintCalls[call]
|
||||
if !ok {
|
||||
// First time? Create the canonical probe node.
|
||||
ptr = Pointer{a, nil, a.addNodes(t, "print")}
|
||||
a.result.PrintCalls[call] = ptr
|
||||
}
|
||||
|
||||
probe := ptr.n
|
||||
a.copy(probe, a.valueNode(call.Args[0]), a.sizeof(t))
|
||||
}
|
||||
|
||||
|
@ -136,25 +136,19 @@ func (e *expectation) needsProbe() bool {
|
||||
return e.kind == "pointsto" || e.kind == "types"
|
||||
}
|
||||
|
||||
// A record of a call to the built-in print() function. Used for testing.
|
||||
type probe struct {
|
||||
instr *ssa.CallCommon
|
||||
arg0 pointer.Pointer // first argument to print
|
||||
}
|
||||
|
||||
// Find probe (call to print(x)) of same source
|
||||
// file/line as expectation.
|
||||
func findProbe(prog *ssa.Program, probes []probe, e *expectation) *probe {
|
||||
for _, p := range probes {
|
||||
pos := prog.Fset.Position(p.instr.Pos())
|
||||
func findProbe(prog *ssa.Program, probes map[*ssa.CallCommon]pointer.Pointer, e *expectation) (site *ssa.CallCommon, ptr pointer.Pointer) {
|
||||
for call, ptr := range probes {
|
||||
pos := prog.Fset.Position(call.Pos())
|
||||
if pos.Line == e.linenum && pos.Filename == e.filename {
|
||||
// TODO(adonovan): send this to test log (display only on failure).
|
||||
// fmt.Printf("%s:%d: info: found probe for %s: %s\n",
|
||||
// e.filename, e.linenum, e, p.arg0) // debugging
|
||||
return &p
|
||||
return call, ptr
|
||||
}
|
||||
}
|
||||
return nil // e.g. analysis didn't reach this call
|
||||
return // e.g. analysis didn't reach this call
|
||||
}
|
||||
|
||||
func doOneInput(input, filename string) bool {
|
||||
@ -276,18 +270,15 @@ func doOneInput(input, filename string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
var probes []probe
|
||||
var log bytes.Buffer
|
||||
|
||||
// Run the analysis.
|
||||
config := &pointer.Config{
|
||||
Reflection: true,
|
||||
BuildCallGraph: true,
|
||||
Mains: []*ssa.Package{ptrmain},
|
||||
Log: &log,
|
||||
Print: func(site *ssa.CallCommon, p pointer.Pointer) {
|
||||
probes = append(probes, probe{site, p})
|
||||
},
|
||||
Reflection: true,
|
||||
BuildCallGraph: true,
|
||||
QueryPrintCalls: true,
|
||||
Mains: []*ssa.Package{ptrmain},
|
||||
Log: &log,
|
||||
}
|
||||
|
||||
// Print the log is there was an error or a panic.
|
||||
@ -302,28 +293,31 @@ func doOneInput(input, filename string) bool {
|
||||
|
||||
// Check the expectations.
|
||||
for _, e := range exps {
|
||||
var pr *probe
|
||||
var call *ssa.CallCommon
|
||||
var ptr pointer.Pointer
|
||||
var tProbe types.Type
|
||||
if e.needsProbe() {
|
||||
if pr = findProbe(prog, probes, e); pr == nil {
|
||||
if call, ptr = findProbe(prog, result.PrintCalls, e); call == nil {
|
||||
ok = false
|
||||
e.errorf("unreachable print() statement has expectation %s", e)
|
||||
continue
|
||||
}
|
||||
if tArg := pr.instr.Args[0].Type(); !pointer.CanPoint(tArg) {
|
||||
tProbe = call.Args[0].Type()
|
||||
if !pointer.CanPoint(tProbe) {
|
||||
ok = false
|
||||
e.errorf("expectation on non-pointerlike operand: %s", tArg)
|
||||
e.errorf("expectation on non-pointerlike operand: %s", tProbe)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
switch e.kind {
|
||||
case "pointsto":
|
||||
if !checkPointsToExpectation(e, pr, lineMapping, prog) {
|
||||
if !checkPointsToExpectation(e, ptr, lineMapping, prog) {
|
||||
ok = false
|
||||
}
|
||||
|
||||
case "types":
|
||||
if !checkTypesExpectation(e, pr) {
|
||||
if !checkTypesExpectation(e, ptr, tProbe) {
|
||||
ok = false
|
||||
}
|
||||
|
||||
@ -368,7 +362,7 @@ func labelString(l *pointer.Label, lineMapping map[string]string, prog *ssa.Prog
|
||||
return str
|
||||
}
|
||||
|
||||
func checkPointsToExpectation(e *expectation, pr *probe, lineMapping map[string]string, prog *ssa.Program) bool {
|
||||
func checkPointsToExpectation(e *expectation, ptr pointer.Pointer, lineMapping map[string]string, prog *ssa.Program) bool {
|
||||
expected := make(map[string]int)
|
||||
surplus := make(map[string]int)
|
||||
exact := true
|
||||
@ -381,7 +375,7 @@ func checkPointsToExpectation(e *expectation, pr *probe, lineMapping map[string]
|
||||
}
|
||||
// Find the set of labels that the probe's
|
||||
// argument (x in print(x)) may point to.
|
||||
for _, label := range pr.arg0.PointsTo().Labels() {
|
||||
for _, label := range ptr.PointsTo().Labels() {
|
||||
name := labelString(label, lineMapping, prog)
|
||||
if expected[name] > 0 {
|
||||
expected[name]--
|
||||
@ -419,7 +413,7 @@ func underlyingType(typ types.Type) types.Type {
|
||||
return typ
|
||||
}
|
||||
|
||||
func checkTypesExpectation(e *expectation, pr *probe) bool {
|
||||
func checkTypesExpectation(e *expectation, ptr pointer.Pointer, typ types.Type) bool {
|
||||
var expected typemap.M
|
||||
var surplus typemap.M
|
||||
exact := true
|
||||
@ -431,14 +425,14 @@ func checkTypesExpectation(e *expectation, pr *probe) bool {
|
||||
expected.Set(g, struct{}{})
|
||||
}
|
||||
|
||||
if t := pr.instr.Args[0].Type(); !pointer.CanHaveDynamicTypes(t) {
|
||||
e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", t)
|
||||
if !pointer.CanHaveDynamicTypes(typ) {
|
||||
e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", typ)
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the set of types that the probe's
|
||||
// argument (x in print(x)) may contain.
|
||||
for _, T := range pr.arg0.PointsTo().DynamicTypes().Keys() {
|
||||
for _, T := range ptr.PointsTo().DynamicTypes().Keys() {
|
||||
if expected.At(T) != nil {
|
||||
expected.Delete(T)
|
||||
} else if exact {
|
||||
|
Loading…
Reference in New Issue
Block a user