go.tools/pointer: more reflection.
Support for:
(*reflect.rtype).Field
(*reflect.rtype).FieldByName
reflect.MakeSlice
runtime.SetFinalizer
Details:
- analysis locates ssa.Functions for (reflect.Value).Call
and runtime.SetFinalizer during startup to that it can
special-case them during genCall. ('Call' is forthcoming.)
- The callsite.targets mechanism is only used for dynamic
calls now. For static calls we call callEdge during constraint
generation; this is a minor optimisation.
- Static calls to SetFinalizer are inlined so that the call
appears to go direct to the finalizer. (We'll use the same
trick for (reflect.Value).Call.)
- runtime.FuncForPC: treat as a no-op.
- Fixed pointer_test to properly deal with expectations
that are multi-sets.
- Inlined rtypeMethodByNameConstraint.addMethod.
- More tests.
R=crawshaw
CC=golang-dev
https://golang.org/cl/14682045
diff --git a/pointer/analysis.go b/pointer/analysis.go
index e3677c6..0f21424 100644
--- a/pointer/analysis.go
+++ b/pointer/analysis.go
@@ -205,14 +205,16 @@
work worklist // solver's worklist
result *Result // results of the analysis
- // Reflection:
- hasher typemap.Hasher // cache of type hashes
- reflectValueObj types.Object // type symbol for reflect.Value (if present)
- reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
- reflectRtypePtr *types.Pointer // *reflect.rtype
- reflectType *types.Named // reflect.Type
- rtypes typemap.M // nodeid of canonical *rtype-tagged object for type T
- reflectZeros typemap.M // nodeid of canonical T-tagged object for zero value
+ // Reflection & intrinsics:
+ hasher typemap.Hasher // cache of type hashes
+ reflectValueObj types.Object // type symbol for reflect.Value (if present)
+ reflectValueCall *ssa.Function // (reflect.Value).Call
+ reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
+ reflectRtypePtr *types.Pointer // *reflect.rtype
+ reflectType *types.Named // reflect.Type
+ rtypes typemap.M // nodeid of canonical *rtype-tagged object for type T
+ reflectZeros typemap.M // nodeid of canonical T-tagged object for zero value
+ runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer
}
// enclosingObj returns the object (addressible memory object) that encloses node id.
@@ -273,7 +275,9 @@
}
if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
- a.reflectValueObj = reflect.Object.Scope().Lookup("Value")
+ rV := reflect.Object.Scope().Lookup("Value")
+ a.reflectValueObj = rV
+ a.reflectValueCall = a.prog.Method(rV.Type().MethodSet().Lookup(nil, "Call"))
a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named)
a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
@@ -285,6 +289,9 @@
a.rtypes.SetHasher(a.hasher)
a.reflectZeros.SetHasher(a.hasher)
}
+ if runtime := a.prog.ImportedPackage("runtime"); runtime != nil {
+ a.runtimeSetFinalizer = runtime.Func("SetFinalizer")
+ }
root := a.generate()
@@ -314,22 +321,11 @@
}
}
- // Visit discovered call graph.
+ // Add dynamic edges to call graph.
for _, caller := range a.cgnodes {
for _, site := range caller.sites {
- for nid := range a.nodes[site.targets].pts {
- callee := a.nodes[nid].obj.cgn
-
- if a.config.BuildCallGraph {
- site.callees = append(site.callees, callee)
- }
-
- // TODO(adonovan): de-dup these messages.
- // Warn about calls to non-intrinsic external functions.
- if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil {
- a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn)
- a.warnf(fn.Pos(), " (declared here)")
- }
+ for callee := range a.nodes[site.targets].pts {
+ a.callEdge(site, callee)
}
}
}
@@ -340,3 +336,29 @@
return a.result
}
+
+// callEdge is called for each edge in the callgraph.
+// calleeid is the callee's object node (has otFunction flag).
+//
+func (a *analysis) callEdge(site *callsite, calleeid nodeid) {
+ obj := a.nodes[calleeid].obj
+ if obj.flags&otFunction == 0 {
+ panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid))
+ }
+ callee := obj.cgn
+
+ if a.config.BuildCallGraph {
+ site.callees = append(site.callees, callee)
+ }
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee)
+ }
+
+ // Warn about calls to non-intrinsic external functions.
+ // TODO(adonovan): de-dup these messages.
+ if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil {
+ a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn)
+ a.warnf(fn.Pos(), " (declared here)")
+ }
+}