// Hash functions and equivalence relation: // hashString computes the FNV hash of s. func hashString(s string) int { var h uint32 for i := 0; i < len(s); i++ { h ^= uint32(s[i]) h *= 16777619 } return int(h) } var ( mu sync.Mutex hasher = typeutil.MakeHasher() ) // hashType returns a hash for t such that // types.Identical(x, y) => hashType(x) == hashType(y). func hashType(t types.Type) int { mu.Lock() h := int(hasher.Hash(t)) mu.Unlock() return h } // usesBuiltinMap returns true if the built-in hash function and // equivalence relation for type t are consistent with those of the // interpreter's representation of type t. Such types are: all basic // types (bool, numbers, string), pointers and channels.
// Analyze runs the pointer analysis with the scope and options // specified by config, and returns the (synthetic) root of the callgraph. // // Pointer analysis of a transitively closed well-typed program should // always succeed. An error can occur only due to an internal bug. // func Analyze(config *Config) (result *Result, err error) { defer func() { if p := recover(); p != nil { err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p) fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:") debug.PrintStack() } }() a := &analysis{ config: config, log: config.Log, prog: config.prog(), globalval: make(map[ssa.Value]nodeid), globalobj: make(map[ssa.Value]nodeid), flattenMemo: make(map[types.Type][]*fieldInfo), trackTypes: make(map[types.Type]bool), hasher: typeutil.MakeHasher(), intrinsics: make(map[*ssa.Function]intrinsic), work: makeMapWorklist(), result: &Result{ Queries: make(map[ssa.Value]Pointer), IndirectQueries: make(map[ssa.Value]Pointer), }, } if false { a.log = os.Stderr // for debugging crashes; extremely verbose } if a.log != nil { fmt.Fprintln(a.log, "======== NEW ANALYSIS ========") } // Pointer analysis requires a complete program for soundness. // Check to prevent accidental misconfiguration. for _, pkg := range a.prog.AllPackages() { // (This only checks that the package scope is complete, // not that func bodies exist, but it's a good signal.) if !pkg.Object.Complete() { return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete (set loader.Config.SourceImports during loading)`, pkg.Object.Path()) } } if reflect := a.prog.ImportedPackage("reflect"); reflect != nil { rV := reflect.Object.Scope().Lookup("Value") a.reflectValueObj = rV a.reflectValueCall = a.prog.LookupMethod(rV.Type(), 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()) // Override flattening of reflect.Value, treating it like a basic type. tReflectValue := a.reflectValueObj.Type() a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}} // Override shouldTrack of reflect.Value and *reflect.rtype. // Always track pointers of these types. a.trackTypes[tReflectValue] = true a.trackTypes[a.reflectRtypePtr] = true a.rtypes.SetHasher(a.hasher) a.reflectZeros.SetHasher(a.hasher) } if runtime := a.prog.ImportedPackage("runtime"); runtime != nil { a.runtimeSetFinalizer = runtime.Func("SetFinalizer") } a.computeTrackBits() a.generate() if a.log != nil { // Show size of constraint system. counts := make(map[reflect.Type]int) for _, c := range a.constraints { counts[reflect.TypeOf(c)]++ } fmt.Fprintf(a.log, "# constraints:\t%d\n", len(a.constraints)) for t, n := range counts { fmt.Fprintf(a.log, "\t%s:\t%d\n", t, n) } fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes)) } a.optimize() a.solve() // Create callgraph.Nodes in deterministic order. if cg := a.result.CallGraph; cg != nil { for _, caller := range a.cgnodes { cg.CreateNode(caller.fn) } } // Add dynamic edges to call graph. for _, caller := range a.cgnodes { for _, site := range caller.sites { for callee := range a.nodes[site.targets].pts { a.callEdge(caller, site, callee) } } } return a.result, nil }
// Analyze runs the pointer analysis with the scope and options // specified by config, and returns the (synthetic) root of the callgraph. // // Pointer analysis of a transitively closed well-typed program should // always succeed. An error can occur only due to an internal bug. // func Analyze(config *Config) (result *Result, err error) { if config.Mains == nil { return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)") } defer func() { if p := recover(); p != nil { err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p) fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:") debug.PrintStack() } }() a := &analysis{ config: config, log: config.Log, prog: config.prog(), globalval: make(map[ssa.Value]nodeid), globalobj: make(map[ssa.Value]nodeid), flattenMemo: make(map[types.Type][]*fieldInfo), trackTypes: make(map[types.Type]bool), atFuncs: make(map[*ssa.Function]bool), hasher: typeutil.MakeHasher(), intrinsics: make(map[*ssa.Function]intrinsic), result: &Result{ Queries: make(map[ssa.Value]Pointer), IndirectQueries: make(map[ssa.Value]Pointer), }, deltaSpace: make([]int, 0, 100), } if false { a.log = os.Stderr // for debugging crashes; extremely verbose } if a.log != nil { fmt.Fprintln(a.log, "==== Starting analysis") } // Pointer analysis requires a complete program for soundness. // Check to prevent accidental misconfiguration. for _, pkg := range a.prog.AllPackages() { // (This only checks that the package scope is complete, // not that func bodies exist, but it's a good signal.) if !pkg.Object.Complete() { return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete (set loader.Config.SourceImports during loading)`, pkg.Object.Path()) } } if reflect := a.prog.ImportedPackage("reflect"); reflect != nil { rV := reflect.Object.Scope().Lookup("Value") a.reflectValueObj = rV a.reflectValueCall = a.prog.LookupMethod(rV.Type(), 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()) // Override flattening of reflect.Value, treating it like a basic type. tReflectValue := a.reflectValueObj.Type() a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}} // Override shouldTrack of reflect.Value and *reflect.rtype. // Always track pointers of these types. a.trackTypes[tReflectValue] = true a.trackTypes[a.reflectRtypePtr] = true a.rtypes.SetHasher(a.hasher) a.reflectZeros.SetHasher(a.hasher) } if runtime := a.prog.ImportedPackage("runtime"); runtime != nil { a.runtimeSetFinalizer = runtime.Func("SetFinalizer") } a.computeTrackBits() a.generate() a.showCounts() if optRenumber { a.renumber() } N := len(a.nodes) // excludes solver-created nodes if optHVN { if debugHVNCrossCheck { // Cross-check: run the solver once without // optimization, once with, and compare the // solutions. savedConstraints := a.constraints a.solve() a.dumpSolution("A.pts", N) // Restore. a.constraints = savedConstraints for _, n := range a.nodes { n.solve = new(solverState) } a.nodes = a.nodes[:N] // rtypes is effectively part of the solver state. a.rtypes = typeutil.Map{} a.rtypes.SetHasher(a.hasher) } a.hvn() } if debugHVNCrossCheck { runtime.GC() runtime.GC() } a.solve() // Compare solutions. if optHVN && debugHVNCrossCheck { a.dumpSolution("B.pts", N) if !diff("A.pts", "B.pts") { return nil, fmt.Errorf("internal error: optimization changed solution") } } // Create callgraph.Nodes in deterministic order. if cg := a.result.CallGraph; cg != nil { for _, caller := range a.cgnodes { cg.CreateNode(caller.fn) } } // Add dynamic edges to call graph. var space [100]int for _, caller := range a.cgnodes { for _, site := range caller.sites { for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) { a.callEdge(caller, site, nodeid(callee)) } } } return a.result, nil }