1
0
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:
Alan Donovan 2013-12-10 09:15:39 -05:00
parent 05f8c7dc66
commit 5d70784aca
4 changed files with 47 additions and 58 deletions

View File

@ -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),
},
}

View File

@ -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.

View File

@ -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))
}

View File

@ -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 {