// prepareCall determines the function value and argument values for a // function call in a Call, Go or Defer instruction, peforming // interface method lookup if needed. // func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { if call.Func != nil { // Function call. fn = fr.get(call.Func) } else { // Interface method invocation. recv := fr.get(call.Recv).(iface) if recv.t == nil { panic("method invoked on nil interface") } id := call.MethodId() m := findMethodSet(fr.i, recv.t)[id] if m == nil { // Unreachable in well-typed programs. panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id)) } _, aptr := recv.v.(*value) // actual pointerness _, fptr := m.Signature.Recv().Type().(*types.Pointer) // formal pointerness switch { case aptr == fptr: args = append(args, copyVal(recv.v)) case aptr: // Calling func(T) with a *T receiver: make a copy. args = append(args, copyVal(*recv.v.(*value))) case fptr: panic("illegal call of *T method with T receiver") } fn = m } for _, arg := range call.Args { args = append(args, fr.get(arg)) } return }
// genInvoke generates constraints for a dynamic method invocation. func (a *analysis) genInvoke(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { if call.Value.Type() == a.reflectType { a.genInvokeReflectType(caller, site, call, result) return } sig := call.Signature() // Allocate a contiguous targets/params/results block for this call. block := a.nextNode() // pts(targets) will be the set of possible call targets site.targets = a.addOneNode(sig, "invoke.targets", nil) p := a.addNodes(sig.Params(), "invoke.params") r := a.addNodes(sig.Results(), "invoke.results") // Copy the actual parameters into the call's params block. for i, n := 0, sig.Params().Len(); i < n; i++ { sz := a.sizeof(sig.Params().At(i).Type()) a.copy(p, a.valueNode(call.Args[i]), sz) p += nodeid(sz) } // Copy the call's results block to the actual results. if result != 0 { a.copy(result, r, a.sizeof(sig.Results())) } // We add a dynamic invoke constraint that will add // edges from the caller's P/R block to the callee's // P/R block for each discovered call target. a.addConstraint(&invokeConstraint{call.Method, a.valueNode(call.Value), block}) }
// genStaticCall generates constraints for a statically dispatched function call. func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { fn := call.StaticCallee() // Special cases for inlined intrinsics. switch fn { case a.runtimeSetFinalizer: // Inline SetFinalizer so the call appears direct. site.targets = a.addOneNode(tInvalid, "SetFinalizer.targets", nil) a.addConstraint(&runtimeSetFinalizerConstraint{ targets: site.targets, x: a.valueNode(call.Args[0]), f: a.valueNode(call.Args[1]), }) return case a.reflectValueCall: // Inline (reflect.Value).Call so the call appears direct. dotdotdot := false ret := reflectCallImpl(a, caller, site, a.valueNode(call.Args[0]), a.valueNode(call.Args[1]), dotdotdot) if result != 0 { a.addressOf(result, ret) } return } // Ascertain the context (contour/cgnode) for a particular call. var obj nodeid if a.shouldUseContext(fn) { obj = a.makeFunctionObject(fn, site) // new contour } else { obj = a.objectNode(nil, fn) // shared contour } a.callEdge(site, obj) sig := call.Signature() // Copy receiver, if any. params := a.funcParams(obj) args := call.Args if sig.Recv() != nil { sz := a.sizeof(sig.Recv().Type()) a.copy(params, a.valueNode(args[0]), sz) params += nodeid(sz) args = args[1:] } // Copy actual parameters into formal params block. // Must loop, since the actuals aren't contiguous. for i, arg := range args { sz := a.sizeof(sig.Params().At(i).Type()) a.copy(params, a.valueNode(arg), sz) params += nodeid(sz) } // Copy formal results block to actual result. if result != 0 { a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) } }
// genDynamicCall generates constraints for a dynamic function call. func (a *analysis) genDynamicCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { // pts(targets) will be the set of possible call targets. site.targets = a.valueNode(call.Value) // We add dynamic closure rules that store the arguments into, // and load the results from, the P/R block of each function // discovered in pts(targets). sig := call.Signature() var offset uint32 = 1 // P/R block starts at offset 1 for i, arg := range call.Args { sz := a.sizeof(sig.Params().At(i).Type()) a.genStore(caller, call.Value, a.valueNode(arg), offset, sz) offset += sz } if result != 0 { a.genLoad(caller, result, call.Value, offset, a.sizeof(sig.Results())) } }
// genDynamicCall generates constraints for a dynamic function call. // It returns a node whose pts() will be the set of possible call targets. // func (a *analysis) genDynamicCall(call *ssa.CallCommon, result nodeid) nodeid { fn := a.valueNode(call.Value) sig := call.Signature() // We add dynamic closure rules that store the arguments into, // and load the results from, the P/R block of each function // discovered in pts(fn). var offset uint32 = 1 // P/R block starts at offset 1 for i, arg := range call.Args { sz := a.sizeof(sig.Params().At(i).Type()) a.storeOffset(fn, a.valueNode(arg), offset, sz) offset += sz } if result != 0 { a.loadOffset(result, fn, offset, a.sizeof(sig.Results())) } return fn }
// genStaticCall generates constraints for a statically dispatched // function call. It returns a node whose pts() will be the set of // possible call targets (in this case, a singleton). // func (a *analysis) genStaticCall(call *ssa.CallCommon, result nodeid) nodeid { // Ascertain the context (contour/CGNode) for a particular call. var obj nodeid fn := call.StaticCallee() if a.shouldUseContext(fn) { obj = a.makeFunctionObject(fn) // new contour for this call } else { a.valueNode(fn) // ensure shared contour was created obj = a.funcObj[fn] // ordinary (shared) contour. } sig := call.Signature() targets := a.addOneNode(sig, "call.targets", nil) a.addressOf(targets, obj) // (a singleton) // Copy receiver, if any. params := a.funcParams(obj) args := call.Args if sig.Recv() != nil { sz := a.sizeof(sig.Recv().Type()) a.copy(params, a.valueNode(args[0]), sz) params += nodeid(sz) args = args[1:] } // Copy actual parameters into formal params block. // Must loop, since the actuals aren't contiguous. for i, arg := range args { sz := a.sizeof(sig.Params().At(i).Type()) a.copy(params, a.valueNode(arg), sz) params += nodeid(sz) } // Copy formal results block to actual result. if result != 0 { a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) } return targets }