diff --git a/go/callgraph/callgraph.go b/go/callgraph/callgraph.go index 7d78a05ea7..68ed23025e 100644 --- a/go/callgraph/callgraph.go +++ b/go/callgraph/callgraph.go @@ -41,6 +41,7 @@ package callgraph import ( "fmt" + "go/token" "code.google.com/p/go.tools/go/ssa" ) @@ -99,6 +100,20 @@ func (e Edge) String() string { return fmt.Sprintf("%s --> %s", e.Caller, e.Callee) } +func (e Edge) Description() string { + if e.Site == nil { + return "synthetic call" + } + return e.Site.Common().Description() +} + +func (e Edge) Pos() token.Pos { + if e.Site == nil { + return token.NoPos + } + return e.Site.Pos() +} + // AddEdge adds the edge (caller, site, callee) to the call graph. // Elimination of duplicate edges is the caller's responsibility. func AddEdge(caller *Node, site ssa.CallInstruction, callee *Node) { diff --git a/go/pointer/intrinsics.go b/go/pointer/intrinsics.go index 5a11ea1178..92d0730c3a 100644 --- a/go/pointer/intrinsics.go +++ b/go/pointer/intrinsics.go @@ -174,7 +174,7 @@ func init() { "syscall.setenv_c": ext۰NoEffect, "time.Sleep": ext۰NoEffect, "time.now": ext۰NoEffect, - "time.startTimer": ext۰NoEffect, + "time.startTimer": ext۰time۰startTimer, "time.stopTimer": ext۰NoEffect, } { intrinsicsByName[name] = fn @@ -243,12 +243,8 @@ func (a *analysis) isReflect(fn *ssa.Function) bool { func ext۰NoEffect(a *analysis, cgn *cgnode) {} func ext۰NotYetImplemented(a *analysis, cgn *cgnode) { - // TODO(adonovan): enable this warning when we've implemented - // enough that it's not unbearably annoying. - if true { - fn := cgn.fn - a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn) - } + fn := cgn.fn + a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn) } // ---------- func runtime.SetFinalizer(x, f interface{}) ---------- @@ -320,3 +316,65 @@ func ext۰runtime۰SetFinalizer(a *analysis, cgn *cgnode) { f: params + 1, }) } + +// ---------- func time.startTimer(t *runtimeTimer) ---------- + +// time.StartTimer(t) +type timeStartTimerConstraint struct { + targets nodeid // (indirect) + t nodeid // (ptr) +} + +func (c *timeStartTimerConstraint) ptr() nodeid { return c.t } +func (c *timeStartTimerConstraint) presolve(h *hvn) { + h.markIndirect(onodeid(c.targets), "StartTimer.targets") +} +func (c *timeStartTimerConstraint) renumber(mapping []nodeid) { + c.targets = mapping[c.targets] + c.t = mapping[c.t] +} + +func (c *timeStartTimerConstraint) String() string { + return fmt.Sprintf("time.startTimer(n%d)", c.t) +} + +func (c *timeStartTimerConstraint) solve(a *analysis, delta *nodeset) { + for _, tObj := range delta.AppendTo(a.deltaSpace) { + t := nodeid(tObj) + + // We model startTimer as if it was defined thus: + // func startTimer(t *runtimeTimer) { t.f(0, t.arg) } + + // We hard-code the field offsets of time.runtimeTimer: + // type runtimeTimer struct { + // 0 __identity__ + // 1 i int32 + // 2 when int64 + // 3 period int64 + // 4 f func(int64, interface{}) + // 5 arg interface{} + // } + f := t + 4 + arg := t + 5 + + // store t.arg to t.f.params[1] + // (offset 2 => skip identity and int64 param) + a.store(f, arg, 2, 1) + + // Add dynamic call target. + if a.onlineCopy(c.targets, f) { + a.addWork(c.targets) + } + } +} + +func ext۰time۰startTimer(a *analysis, cgn *cgnode) { + // This is the shared contour, used for dynamic calls. + targets := a.addOneNode(tInvalid, "startTimer.targets", nil) + cgn.sites = append(cgn.sites, &callsite{targets: targets}) + params := a.funcParams(cgn.obj) + a.addConstraint(&timeStartTimerConstraint{ + targets: targets, + t: params, + }) +} diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go index 112c1e2d01..ae521d1e97 100644 --- a/go/pointer/pointer_test.go +++ b/go/pointer/pointer_test.go @@ -54,6 +54,7 @@ var inputs = []string{ "testdata/rtti.go", "testdata/structreflect.go", "testdata/structs.go", + "testdata/timer.go", } // Expectation grammar: diff --git a/godoc/analysis/callgraph.go b/godoc/analysis/callgraph.go index a6f1dba20d..2442da7328 100644 --- a/godoc/analysis/callgraph.go +++ b/godoc/analysis/callgraph.go @@ -43,7 +43,7 @@ func (a *analysis) doCallgraph(cg *callgraph.Graph) { for _, n := range cg.Nodes { for _, e := range n.Out { if e.Site == nil { - continue // a call from the node + continue // a call from a synthetic node such as } // Add (site pos, callee) to calledFuncs. diff --git a/oracle/callers.go b/oracle/callers.go index ac3d37b461..f970eb5ef4 100644 --- a/oracle/callers.go +++ b/oracle/callers.go @@ -63,25 +63,20 @@ func (r *callersResult) display(printf printfFunc) { if edge.Caller == root { printf(r.target, "the root of the call graph") } else { - printf(edge.Site, "\t%s from %s", edge.Site.Common().Description(), edge.Caller.Func) + printf(edge, "\t%s from %s", edge.Description(), edge.Caller.Func) } } } } func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) { - root := r.callgraph.Root var callers []serial.Caller for _, edge := range r.edges { - var c serial.Caller - c.Caller = edge.Caller.Func.String() - if edge.Caller == root { - c.Desc = "synthetic call" - } else { - c.Pos = fset.Position(edge.Site.Pos()).String() - c.Desc = edge.Site.Common().Description() - } - callers = append(callers, c) + callers = append(callers, serial.Caller{ + Caller: edge.Caller.Func.String(), + Pos: fset.Position(edge.Pos()).String(), + Desc: edge.Description(), + }) } res.Callers = callers } diff --git a/oracle/callstack.go b/oracle/callstack.go index e71e09ec43..3269fd2752 100644 --- a/oracle/callstack.go +++ b/oracle/callstack.go @@ -71,7 +71,7 @@ func (r *callstackResult) display(printf printfFunc) { printf(r.target, "%s", r.target) for i := len(r.callpath) - 1; i >= 0; i-- { edge := r.callpath[i] - printf(edge.Site, "%s from %s", edge.Site.Common().Description(), edge.Caller.Func) + printf(edge, "%s from %s", edge.Description(), edge.Caller.Func) } } else { printf(r.target, "%s is unreachable in this analysis scope", r.target) @@ -83,9 +83,9 @@ func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) { for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first) edge := r.callpath[i] callers = append(callers, serial.Caller{ - Pos: fset.Position(edge.Site.Pos()).String(), + Pos: fset.Position(edge.Pos()).String(), Caller: edge.Caller.Func.String(), - Desc: edge.Site.Common().Description(), + Desc: edge.Description(), }) } res.Callstack = &serial.CallStack{