Example #1
0
// 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
}
Example #2
0
// 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})
}
Example #3
0
// 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()))
	}
}
Example #4
0
// 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()))
	}
}
Example #5
0
// 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
}
Example #6
0
// 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
}