func ExampleMap() { const source = `package P var X []string var Y []string const p, q = 1.0, 2.0 func f(offset int32) (value byte, ok bool) func g(rune) (uint8, bool) ` // Parse and type-check the package. fset := token.NewFileSet() f, err := parser.ParseFile(fset, "P.go", source, 0) if err != nil { panic(err) } pkg, err := new(types.Config).Check("P", fset, []*ast.File{f}, nil) if err != nil { panic(err) } scope := pkg.Scope() // Group names of package-level objects by their type. var namesByType typeutil.Map // value is []string for _, name := range scope.Names() { T := scope.Lookup(name).Type() names, _ := namesByType.At(T).([]string) names = append(names, name) namesByType.Set(T, names) } // Format, sort, and print the map entries. var lines []string namesByType.Iterate(func(T types.Type, names interface{}) { lines = append(lines, fmt.Sprintf("%s %s", names, T)) }) sort.Strings(lines) for _, line := range lines { fmt.Println(line) } // Output: // [X Y] []string // [f g] func(offset int32) (value byte, ok bool) // [p q] untyped float }
// If this PointsToSet came from a Pointer of interface kind // or a reflect.Value, DynamicTypes returns the set of dynamic // types that it may contain. (For an interface, they will // always be concrete types.) // // The result is a mapping whose keys are the dynamic types to which // it may point. For each pointer-like key type, the corresponding // map value is the PointsToSet for pointers of that type. // // The result is empty unless CanHaveDynamicTypes(T). // func (s PointsToSet) DynamicTypes() *typeutil.Map { var tmap typeutil.Map tmap.SetHasher(s.a.hasher) if s.pts != nil { var space [50]int for _, x := range s.pts.AppendTo(space[:0]) { ifaceObjId := nodeid(x) if !s.a.isTaggedObject(ifaceObjId) { continue // !CanHaveDynamicTypes(tDyn) } tDyn, v, indirect := s.a.taggedValue(ifaceObjId) if indirect { panic("indirect tagged object") // implement later } pts, ok := tmap.At(tDyn).(PointsToSet) if !ok { pts = PointsToSet{s.a, new(nodeset)} tmap.Set(tDyn, pts) } pts.pts.addAll(&s.a.nodes[v].solve.pts) } } return &tmap }
func checkTypesExpectation(e *expectation, pts pointer.PointsToSet, typ types.Type) bool { var expected typeutil.Map var surplus typeutil.Map exact := true for _, g := range e.types { if g == types.Typ[types.Invalid] { exact = false continue } expected.Set(g, struct{}{}) } 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 pts.DynamicTypes().Keys() { if expected.At(T) != nil { expected.Delete(T) } else if exact { surplus.Set(T, struct{}{}) } } // Report set difference: ok := true if expected.Len() > 0 { ok = false e.errorf("interface cannot contain these types: %s", expected.KeysString()) } if surplus.Len() > 0 { ok = false e.errorf("interface may additionally contain these types: %s", surplus.KeysString()) } return ok }
// CallGraph computes the call graph of the specified program using the // Class Hierarchy Analysis algorithm. // func CallGraph(prog *ssa.Program) *callgraph.Graph { cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph allFuncs := ssautil.AllFunctions(prog) // funcsBySig contains all functions, keyed by signature. It is // the effective set of address-taken functions used to resolve // a dynamic call of a particular signature. var funcsBySig typeutil.Map // value is []*ssa.Function // methodsByName contains all methods, // grouped by name for efficient lookup. methodsByName := make(map[string][]*ssa.Function) // methodsMemo records, for every abstract method call call I.f on // interface type I, the set of concrete methods C.f of all // types C that satisfy interface I. methodsMemo := make(map[*types.Func][]*ssa.Function) lookupMethods := func(m *types.Func) []*ssa.Function { methods, ok := methodsMemo[m] if !ok { I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface) for _, f := range methodsByName[m.Name()] { C := f.Signature.Recv().Type() // named or *named if types.Implements(C, I) { methods = append(methods, f) } } methodsMemo[m] = methods } return methods } for f := range allFuncs { if f.Signature.Recv() == nil { // Package initializers can never be address-taken. if f.Name() == "init" && f.Synthetic == "package initializer" { continue } funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function) funcs = append(funcs, f) funcsBySig.Set(f.Signature, funcs) } else { methodsByName[f.Name()] = append(methodsByName[f.Name()], f) } } addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) { gnode := cg.CreateNode(g) callgraph.AddEdge(fnode, site, gnode) } addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) { // Because every call to a highly polymorphic and // frequently used abstract method such as // (io.Writer).Write is assumed to call every concrete // Write method in the program, the call graph can // contain a lot of duplication. // // TODO(adonovan): opt: consider factoring the callgraph // API so that the Callers component of each edge is a // slice of nodes, not a singleton. for _, g := range callees { addEdge(fnode, site, g) } } for f := range allFuncs { fnode := cg.CreateNode(f) for _, b := range f.Blocks { for _, instr := range b.Instrs { if site, ok := instr.(ssa.CallInstruction); ok { call := site.Common() if call.IsInvoke() { addEdges(fnode, site, lookupMethods(call.Method)) } else if g := call.StaticCallee(); g != nil { addEdge(fnode, site, g) } else if _, ok := call.Value.(*ssa.Builtin); !ok { callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function) addEdges(fnode, site, callees) } } } } } return cg }
func TestMap(t *testing.T) { var tmap *typeutil.Map // All methods but Set are safe on on (*T)(nil). tmap.Len() tmap.At(tPStr1) tmap.Delete(tPStr1) tmap.KeysString() tmap.String() tmap = new(typeutil.Map) // Length of empty map. if l := tmap.Len(); l != 0 { t.Errorf("Len() on empty Map: got %d, want 0", l) } // At of missing key. if v := tmap.At(tPStr1); v != nil { t.Errorf("At() on empty Map: got %v, want nil", v) } // Deletion of missing key. if tmap.Delete(tPStr1) { t.Errorf("Delete() on empty Map: got true, want false") } // Set of new key. if prev := tmap.Set(tPStr1, "*string"); prev != nil { t.Errorf("Set() on empty Map returned non-nil previous value %s", prev) } // Now: {*string: "*string"} // Length of non-empty map. if l := tmap.Len(); l != 1 { t.Errorf("Len(): got %d, want 1", l) } // At via insertion key. if v := tmap.At(tPStr1); v != "*string" { t.Errorf("At(): got %q, want \"*string\"", v) } // At via equal key. if v := tmap.At(tPStr2); v != "*string" { t.Errorf("At(): got %q, want \"*string\"", v) } // Iteration over sole entry. tmap.Iterate(func(key types.Type, value interface{}) { if key != tPStr1 { t.Errorf("Iterate: key: got %s, want %s", key, tPStr1) } if want := "*string"; value != want { t.Errorf("Iterate: value: got %s, want %s", value, want) } }) // Setion with key equal to present one. if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" { t.Errorf("Set() previous value: got %s, want \"*string\"", prev) } // Setion of another association. if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil { t.Errorf("Set() previous value: got %s, want nil", prev) } // Now: {*string: "*string again", <-chan int: "<-chan int"} want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}" want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}" if s := tmap.String(); s != want1 && s != want2 { t.Errorf("String(): got %s, want %s", s, want1) } want1 = "{*string, <-chan int}" want2 = "{<-chan int, *string}" if s := tmap.KeysString(); s != want1 && s != want2 { t.Errorf("KeysString(): got %s, want %s", s, want1) } // Keys(). I := types.Identical switch k := tmap.Keys(); { case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok default: t.Errorf("Keys(): got %v, want %s", k, want2) } if l := tmap.Len(); l != 2 { t.Errorf("Len(): got %d, want 1", l) } // At via original key. if v := tmap.At(tPStr1); v != "*string again" { t.Errorf("At(): got %q, want \"*string again\"", v) } hamming := 1 tmap.Iterate(func(key types.Type, value interface{}) { switch { case I(key, tChanInt1): hamming *= 2 // ok case I(key, tPStr1): hamming *= 3 // ok } }) if hamming != 6 { t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6) } if v := tmap.At(tChanInt2); v != "<-chan int" { t.Errorf("At(): got %q, want \"<-chan int\"", v) } // Deletion with key equal to present one. if !tmap.Delete(tChanInt2) { t.Errorf("Delete() of existing key: got false, want true") } // Now: {*string: "*string again"} if l := tmap.Len(); l != 1 { t.Errorf("Len(): got %d, want 1", l) } // Deletion again. if !tmap.Delete(tPStr2) { t.Errorf("Delete() of existing key: got false, want true") } // Now: {} if l := tmap.Len(); l != 0 { t.Errorf("Len(): got %d, want %d", l, 0) } if s := tmap.String(); s != "{}" { t.Errorf("Len(): got %q, want %q", s, "") } }