예제 #1
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 connect the
	// caller's and the callee's P/R blocks for each discovered
	// call target.
	a.addConstraint(&invokeConstraint{call.Method, a.valueNode(call.Value), block})
}
예제 #2
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(fn.Signature.Results().At(0).Type(), 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(caller, 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()))
	}
}
예제 #3
0
파일: call.go 프로젝트: nickng/dingo-hunter
func (caller *Function) callClosure(common *ssa.CallCommon, closure *ssa.MakeClosure, infer *TypeInfer, b *Block, l *Loop) {
	callee := caller.prepareCallFn(common, closure.Fn.(*ssa.Function), nil)
	for _, b := range closure.Bindings {
		if inst, ok := caller.locals[b]; ok {
			callee.locals[b] = inst
		}
	}
	callee.call(common, common.StaticCallee(), nil, infer, b, l)
}
예제 #4
0
파일: call.go 프로젝트: nickng/dingo-hunter
func (caller *Function) invoke(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {
	iface, ok := common.Value.Type().Underlying().(*types.Interface)
	if !ok {
		infer.Logger.Fatalf("invoke: %s is not an interface", common.String())
		return nil
	}
	ifaceInst, ok := caller.locals[common.Value] // SSA value initialised
	if !ok {
		infer.Logger.Fatalf("invoke: %s: %s", common.Value.Name(), ErrUnknownValue)
		return nil
	}
	switch inst := ifaceInst.(type) {
	case *Value: // OK
	case *Const:
		if inst.Const.IsNil() {
			return nil
		}
		infer.Logger.Fatalf("invoke: %+v is not nil nor concrete", ifaceInst)
	case *External:
		infer.Logger.Printf(caller.Sprintf("invoke: %+v external", ifaceInst))
		return nil
	default:
		infer.Logger.Printf(caller.Sprintf("invoke: %+v unknown", ifaceInst))
		return nil
	}
	meth, _ := types.MissingMethod(ifaceInst.(*Value).Type(), iface, true) // static
	if meth != nil {
		meth, _ = types.MissingMethod(ifaceInst.(*Value).Type(), iface, false) // non-static
		if meth != nil {
			infer.Logger.Printf("invoke: missing method %s: %s", meth.String(), ErrIfaceIncomplete)
			return nil
		}
	}
	fn := findMethod(common.Value.Parent().Prog, common.Method, ifaceInst.(*Value).Type(), infer)
	if fn == nil {
		if meth == nil {
			infer.Logger.Printf("invoke: cannot locate concrete method")
		} else {
			infer.Logger.Printf("invoke: cannot locate concrete method: %s", meth.String())
		}
		return nil
	}
	return caller.call(common, fn, common.Value, infer, b, l)
}
예제 #5
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
	// the P-block and load the results from the 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()))
	}
}
예제 #6
0
파일: main.go 프로젝트: akwick/ssaview
func call(i ssa.Instruction, out *bytes.Buffer) {
	var callCom ssa.CallCommon
	switch i := i.(type) {
	case *ssa.Call:
		callCom = i.Call
	case *ssa.Go:
		callCom = i.Call
	case *ssa.Defer:
		callCom = i.Call
	default:
		return
	}
	// invoked call
	out.WriteString("  call is invoked")
	if callCom.IsInvoke() {
		out.WriteString(" true\n")
	} else {
		out.WriteString(" false\n")
	}
	// method
	if callCom.Method == nil {
		out.WriteString("  value is a *Builtin ")
		if _, ok := callCom.Value.(*ssa.Builtin); ok {
			out.WriteString(" true\n")
		} else {
			out.WriteString(" false\n")
		}
	}
	// signatue
	if callCom.Value != nil {
		out.WriteString("  signature(Value):" + callCom.Value.Name() + " " + callCom.Signature().String() + "\n")
	}
	if callCom.StaticCallee() != nil {
		out.WriteString("  signature:" + callCom.StaticCallee().Signature.String() + "\n")
	}
	// arguments
	for i, arg := range callCom.Args {
		if i == 1 {
			out.WriteString("  args:")
		}
		out.WriteString(arg.String() + "|")
	}
	out.WriteString("\n")
}
예제 #7
0
// Emit the code for a call to a function or builtin, which could be deferred.
func emitCall(isBuiltin, isGo, isDefer, usesGr bool, register string, callInfo ssa.CallCommon, errorInfo, comment string) {
	// usesGr gives the default position
	l := TargetLang
	fnToCall := ""
	if isBuiltin {
		fnToCall = callInfo.Value.(*ssa.Builtin).Name()
		usesGr = false
	} else if callInfo.StaticCallee() != nil {
		pName, _ := FuncPathName(callInfo.StaticCallee()) //fmt.Sprintf("fn%d", callInfo.StaticCallee().Pos())
		if callInfo.Signature().Recv() != nil {
			pName = callInfo.Signature().Recv().Pkg().Name() + ":" + callInfo.Signature().Recv().Type().String() // no use of Underlying() here
		} else {
			pkg := callInfo.StaticCallee().Package()
			if pkg != nil {
				pName = pkg.Object.Path() // was .Name()
			}
		}
		fnToCall = LanguageList[l].LangName(pName, callInfo.StaticCallee().Name())
		usesGr = grMap[callInfo.StaticCallee()]
	} else { // Dynamic call (take the default on usesGr)
		fnToCall = LanguageList[l].Value(callInfo.Value, errorInfo)
	}

	if isBuiltin {
		switch fnToCall {
		case "len", "cap", "append", "real", "imag", "complex": //  "copy" may have the results unused
			if register == "" {
				LogError(errorInfo, "pogo", fmt.Errorf("the result from a built-in function is not used"))
			}
		default:
		}
	} else {
		if callInfo.Signature().Results().Len() > 0 {
			if register == "" {
				LogWarning(errorInfo, "pogo", fmt.Errorf("the result from a function call is not used")) //TODO is this needed?
			}
		}
	}
	// target language code must do builtin emulation
	text := LanguageList[l].Call(register, callInfo, callInfo.Args, isBuiltin, isGo, isDefer, usesGr, fnToCall, errorInfo)
	fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment))
}
예제 #8
0
파일: call.go 프로젝트: nickng/dingo-hunter
func (caller *Function) callFn(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {
	return caller.call(common, common.StaticCallee(), nil, infer, b, l)
}
예제 #9
0
파일: func.go 프로젝트: nickng/dingo-hunter
func (caller *frame) callCommon(call *ssa.Call, common *ssa.CallCommon) {
	switch fn := common.Value.(type) {
	case *ssa.Builtin:
		caller.callBuiltin(common)

	case *ssa.MakeClosure:
		// TODO(nickng) Handle calling closure
		fmt.Fprintf(os.Stderr, "   # TODO (handle closure) %s\n", fn.String())

	case *ssa.Function:
		if common.StaticCallee() == nil {
			panic("Call with nil CallCommon!")
		}

		callee := &frame{
			fn:      common.StaticCallee(),
			locals:  make(map[ssa.Value]*utils.Definition),
			arrays:  make(map[*utils.Definition]Elems),
			structs: make(map[*utils.Definition]Fields),
			tuples:  make(map[ssa.Value]Tuples),
			phi:     make(map[ssa.Value][]ssa.Value),
			recvok:  make(map[ssa.Value]*sesstype.Chan),
			retvals: make(Tuples, common.Signature().Results().Len()),
			defers:  make([]*ssa.Defer, 0),
			caller:  caller,
			env:     caller.env,   // Use the same env as caller
			gortn:   caller.gortn, // Use the same role as caller
		}

		fmt.Fprintf(os.Stderr, "++ call %s(", orange(common.StaticCallee().String()))
		callee.translate(common)
		fmt.Fprintf(os.Stderr, ")\n")

		if callee.isRecursive() {
			fmt.Fprintf(os.Stderr, "-- Recursive %s()\n", orange(common.StaticCallee().String()))
			callee.printCallStack()
		} else {
			if hasCode := visitFunc(callee.fn, callee); hasCode {
				caller.handleRetvals(call.Value(), callee)
			} else {
				caller.handleExtRetvals(call.Value(), callee)
			}
			fmt.Fprintf(os.Stderr, "-- return from %s (%d retvals)\n", orange(common.StaticCallee().String()), len(callee.retvals))
		}

	default:
		if !common.IsInvoke() {
			fmt.Fprintf(os.Stderr, "Unknown call type %v\n", common)
			return
		}

		switch vd, kind := caller.get(common.Value); kind {
		case Struct, LocalStruct:
			fmt.Fprintf(os.Stderr, "++ invoke %s.%s, type=%s\n", reg(common.Value), common.Method.String(), vd.Var.Type().String())
			// If dealing with interfaces, check that the method is invokable
			if iface, ok := common.Value.Type().Underlying().(*types.Interface); ok {
				if meth, _ := types.MissingMethod(vd.Var.Type(), iface, true); meth != nil {
					fmt.Fprintf(os.Stderr, "     ^ interface not fully implemented\n")
				} else {
					fn := findMethod(common.Value.Parent().Prog, common.Method, vd.Var.Type())
					if fn != nil {
						fmt.Fprintf(os.Stderr, "     ^ found function %s\n", fn.String())

						callee := &frame{
							fn:      fn,
							locals:  make(map[ssa.Value]*utils.Definition),
							arrays:  make(map[*utils.Definition]Elems),
							structs: make(map[*utils.Definition]Fields),
							tuples:  make(map[ssa.Value]Tuples),
							phi:     make(map[ssa.Value][]ssa.Value),
							recvok:  make(map[ssa.Value]*sesstype.Chan),
							retvals: make(Tuples, common.Signature().Results().Len()),
							defers:  make([]*ssa.Defer, 0),
							caller:  caller,
							env:     caller.env,   // Use the same env as caller
							gortn:   caller.gortn, // Use the same role as caller
						}

						common.Args = append([]ssa.Value{common.Value}, common.Args...)
						fmt.Fprintf(os.Stderr, "++ call %s(", orange(fn.String()))
						callee.translate(common)
						fmt.Fprintf(os.Stderr, ")\n")

						if callee.isRecursive() {
							fmt.Fprintf(os.Stderr, "-- Recursive %s()\n", orange(fn.String()))
							callee.printCallStack()
						} else {
							if hasCode := visitFunc(callee.fn, callee); hasCode {
								caller.handleRetvals(call.Value(), callee)
							} else {
								caller.handleExtRetvals(call.Value(), callee)
							}
							fmt.Fprintf(os.Stderr, "-- return from %s (%d retvals)\n", orange(fn.String()), len(callee.retvals))
						}

					} else {
						panic(fmt.Sprintf("Cannot call function: %s.%s is abstract (program not well-formed)", common.Value, common.Method.String()))
					}
				}
			} else {
				fmt.Fprintf(os.Stderr, "     ^ method %s.%s does not exist\n", reg(common.Value), common.Method.String())
			}

		default:
			fmt.Fprintf(os.Stderr, "++ invoke %s.%s\n", reg(common.Value), common.Method.String())
		}
	}
}