// genCall generates constraints for call instruction instr. func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) { call := instr.Common() // Intrinsic implementations of built-in functions. if _, ok := call.Value.(*ssa.Builtin); ok { a.genBuiltinCall(instr, caller) return } var result nodeid if v := instr.Value(); v != nil { result = a.valueNode(v) } site := &callsite{instr: instr} if call.StaticCallee() != nil { a.genStaticCall(caller, site, call, result) } else if call.IsInvoke() { a.genInvoke(caller, site, call, result) } else { a.genDynamicCall(caller, site, call, result) } caller.sites = append(caller.sites, site) if a.log != nil { // TODO(adonovan): debug: improve log message. fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller) } }
// genBuiltinCall generates contraints for a call to a built-in. func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) { call := instr.Common() switch call.Value.(*ssa.Builtin).Name() { case "append": // Safe cast: append cannot appear in a go or defer statement. a.genAppend(instr.(*ssa.Call), cgn) case "copy": tElem := call.Args[0].Type().Underlying().(*types.Slice).Elem() a.copyElems(cgn, tElem, call.Args[0], call.Args[1]) case "panic": a.copy(a.panicNode, a.valueNode(call.Args[0]), 1) case "recover": if v := instr.Value(); v != nil { a.copy(a.valueNode(v), a.panicNode, 1) } case "print": // In the tests, the probe might be the sole reference // to its arg, so make sure we create nodes for it. if len(call.Args) > 0 { a.valueNode(call.Args[0]) } case "ssa:wrapnilchk": a.copy(a.valueNode(instr.Value()), a.valueNode(call.Args[0]), 1) default: // No-ops: close len cap real imag complex print println delete. } }
// visitInvoke is called each time the algorithm encounters an "invoke"-mode call. func (r *rta) visitInvoke(site ssa.CallInstruction) { I := site.Common().Value.Type().Underlying().(*types.Interface) // Record the invoke site. sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction) r.invokeSites.Set(I, append(sites, site)) // Add callgraph edge for each existing // address-taken concrete type implementing I. for _, C := range r.implementations(I) { r.addInvokeEdge(site, C) } }
// visitDynCall is called each time we encounter a dynamic "call"-mode call. func (r *rta) visitDynCall(site ssa.CallInstruction) { S := site.Common().Signature() // Record the call site. sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction) r.dynCallSites.Set(S, append(sites, site)) // For each function of signature S that we know is address-taken, // mark it reachable. We'll add the callgraph edges later. funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool) for g := range funcs { r.addEdge(site, g, true) } }
// addInvokeEdge is called for each new pair (site, C) in the matrix. func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) { // Ascertain the concrete method of C to be called. imethod := site.Common().Method cmethod := r.prog.MethodValue(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name())) r.addEdge(site, cmethod, true) }