Ejemplo n.º 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})
}
Ejemplo n.º 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()))
	}
}
Ejemplo n.º 3
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()))
	}
}
Ejemplo n.º 4
0
// Emit the code for a call to a function or builtin, which could be deferred.
func emitCall(isBuiltin, isGo, isDefer bool, register string, callInfo ssa.CallCommon, errorInfo, comment string) {
	l := TargetLang
	fnToCall := ""
	if isBuiltin {
		fnToCall = callInfo.Value.(*ssa.Builtin).Name()
	} else if callInfo.StaticCallee() != nil {
		pName := "unknown"
		if callInfo.Signature().Recv() != nil {
			pName = callInfo.Signature().Recv().Type().String() // no use of Underlying() here
		} else {
			pkg := callInfo.StaticCallee().Pkg
			if pkg != nil {
				pName = pkg.Object.Name()
			}
		}
		fnToCall = LanguageList[l].LangName(pName, callInfo.StaticCallee().Name())
	} else { // Dynamic call
		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"))
			} else {
			}
		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?
			} else {
			}
		}
	}
	// target language code must do builtin emulation
	text := LanguageList[l].Call(register, callInfo, callInfo.Args, isBuiltin, isGo, isDefer, fnToCall, errorInfo)
	fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment))
}
Ejemplo n.º 5
0
func (l langType) Call(register string, cc ssa.CallCommon, args []ssa.Value, isBuiltin, isGo, isDefer bool, fnToCall, errorInfo string) string {
	isHaxeAPI := false
	hashIf := ""  // #if  - only if required
	hashEnd := "" // #end - ditto
	ret := ""
	pn := "" // package name

	if isBuiltin {
		if register != "" {
			register += "="
		}
		switch fnToCall { // TODO handle other built-in functions?
		case "len", "cap":
			switch args[0].Type().Underlying().(type) {
			case *types.Chan, *types.Slice:
				if fnToCall == "len" {
					return register + "({var _v=" + l.IndirectValue(args[0], errorInfo) + ";_v==null?0:_v.len();});"
				}
				// cap
				return register + "({var _v=" + l.IndirectValue(args[0], errorInfo) + ";_v==null?0:_v.cap();});"
			case *types.Array: // assume len
				return register + l.IndirectValue(args[0], errorInfo /*, false*/) + ".length;"
			case *types.Map: // assume len(map) - requires counting the itterator
				return register + l.IndirectValue(args[0], errorInfo) + "==null?0:{var _l:Int=0;" + // TODO remove two uses of same variable
					"var _it=" + l.IndirectValue(args[0], errorInfo) + ".iterator();" +
					"while(_it.hasNext()) {_l++; _it.next();};" +
					"_l;};"
			case *types.Basic: // assume string as anything else would have produced an error previously
				return register + "Force.toUTF8length(this._goroutine," + l.IndirectValue(args[0], errorInfo /*, false*/) + ");"
			default: // TODO handle other types?
				// TODO error on string?
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Call() - unhandled len/cap type: %s",
					reflect.TypeOf(args[0].Type().Underlying())))
				return register + `null;`
			}
		case "print", "println": // NOTE ugly and target-specific output!
			ret += "trace(" + fmt.Sprintf("Go.CPos(%d)", pogo.LatestValidPosHash)
			if len(args) > 0 { // if there are more arguments to pass, add a comma
				ret += ","
			}
		case "delete":
			if l.LangType(args[1].Type().Underlying(), false, errorInfo) == "GOint64" {
				//useInt64 = true
			}
			return register + l.IndirectValue(args[0], errorInfo) + ".remove(" + l.IndirectValue(args[1], errorInfo) + ");"
		case "append":
			return register + l.append(args, errorInfo) + ";"
		case "copy": //TODO rework & test
			return register + "{var _n={if(" + l.IndirectValue(args[0], errorInfo) + ".len()<" + l.IndirectValue(args[1], errorInfo) + ".len())" +
				l.IndirectValue(args[0], errorInfo) + ".len();else " + l.IndirectValue(args[1], errorInfo) + ".len();};" +
				"for(_i in 0..._n)" + l.IndirectValue(args[0], errorInfo) + ".setAt(_i,Deep.copy(" + l.IndirectValue(args[1], errorInfo) +
				`.getAt(_i` + `))` + `);` +
				"_n;};" // TODO remove two uses of same variable in case of side-effects
		case "close":
			return register + "" + l.IndirectValue(args[0], errorInfo) + ".close();"
		case "recover":
			return register + "" + "Scheduler.recover(this._goroutine);"
		case "real":
			return register + "" + l.IndirectValue(args[0], errorInfo) + ".real;"
		case "imag":
			return register + "" + l.IndirectValue(args[0], errorInfo) + ".imag;"
		case "complex":
			return register + "new Complex(" + l.IndirectValue(args[0], errorInfo) + "," + l.IndirectValue(args[1], errorInfo) + ");"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Call() - Unhandled builtin function: %s", fnToCall))
			ret = "MISSING_BUILTIN("
		}
	} else {
		switch fnToCall {

		//
		// pogo specific function rewriting
		//
		case "tardisgolib_HAXE": // insert raw haxe code into the ouput file!! BEWARE
			nextReturnAddress-- //decrement to set new return address for next call generation
			if register != "" {
				register += "="
			}
			return register + strings.Trim(l.IndirectValue(args[0], errorInfo), `"`) // NOTE user must supply own ";" if required
		case "tardisgolib_Host":
			nextReturnAddress-- //decrement to set new return address for next call generation
			return register + `="` + l.LanguageName() + `";`
		case "tardisgolib_Platform":
			nextReturnAddress-- //decrement to set new return address for next call generation
			return register + `=Go.Platform();`
		case "tardisgolib_CPos":
			nextReturnAddress-- //decrement to set new return address for next call generation
			return register + fmt.Sprintf("=Go.CPos(%d);", pogo.LatestValidPosHash)
		case "tardisgolib_Zilen":
			nextReturnAddress-- //decrement to set new return address for next call generation
			return register + "='хнЧ'.length;"

		//
		// Go library complex function rewriting
		//
		case "math_Inf":
			nextReturnAddress-- //decrement to set new return address for next call generation
			return register + "=(" + l.IndirectValue(args[0], errorInfo) + ">=0?Math.POSITIVE_INFINITY:Math.NEGATIVE_INFINITY);"

		default:

			if cc.Method != nil {
				pn = cc.Method.Pkg().Name()
			} else {
				if cc.StaticCallee() != nil {
					if cc.StaticCallee().Package() != nil {
						pn = cc.StaticCallee().Package().String()
					} else {
						if cc.StaticCallee().Object() != nil {
							if cc.StaticCallee().Object().Pkg() != nil {
								pn = cc.StaticCallee().Object().Pkg().Name()
							}
						}
					}
				}
			}
			pns := strings.Split(pn, "/")
			pn = pns[len(pns)-1]

			targetFunc := "Go_" + fnToCall + ".call"

			if strings.HasPrefix(pn, "_") && // in a package that starts with "_"
				!strings.HasPrefix(fnToCall, "_t") { // and not a temp var TODO this may not always be accurate
				// start _HAXELIB SPECIAL PROCESSING
				nextReturnAddress-- // decrement to set new return address for next call generation
				isBuiltin = true    // pretend we are in a builtin function to avoid passing 1st param as bindings
				isHaxeAPI = true    // we are calling a Haxe native function
				//**************************
				//TODO ensure correct conversions for interface{} <-> uintptr (haxe:Dynamic)
				//**************************
				bits := strings.Split(fnToCall, "_47_") // split the parts of the string separated by /
				endbit := bits[len(bits)-1]
				foundDot := false
				if strings.Contains(endbit, "_dot_") { // it's a dot
					ss := strings.Split(endbit, "_dot_")
					endbit = "_ignore_" + ss[len(ss)-1]
					foundDot = true
				}
				bits = strings.Split(endbit, "_") // split RHS after / into parts separated by _
				bits = bits[2:]                   // discard the leading _ and package name
				switch bits[0][0:1] {             // the letter that gives the Haxe language in which to use the api
				case "X": // cross platform, so noOp
				case "P":
					hashIf = " #if cpp "
					hashEnd = " #end "
				case "C":
					hashIf = " #if cs "
					hashEnd = " #end "
				case "F":
					hashIf = " #if flash "
					hashEnd = " #end "
				case "J":
					hashIf = " #if java "
					hashEnd = " #end "
				case "S":
					hashIf = " #if js "
					hashEnd = " #end "
				case "N":
					hashIf = " #if neko "
					hashEnd = " #end "
				case "H":
					hashIf = " #if php "
					hashEnd = " #end "
				case "i":
					if bits[0] == "init" {
						return "" // no calls to _haxelib init functions
					}
					fallthrough
				default:
					pogo.LogError(errorInfo, "Haxe", fmt.Errorf("call to function %s unknown Haxe API first letter %v of %v",
						fnToCall, bits[0][0:1], bits))
				}
				bits[0] = bits[0][1:] // discard the magic letter from the front of the function name
				if foundDot {         // it's a Haxe method
					ss := strings.Split(args[0].Type().String(), "/")
					rhs := ss[len(ss)-1]                                        // lose leading slashes
					rxTypBits := strings.Split(strings.Split(rhs, ".")[1], "_") // loose module name
					rxTypBits[0] = rxTypBits[0][1:]                             // loose leading capital letter
					rxTyp := strings.Join(rxTypBits, ".")                       // reconstitute with the Haxe dots
					switch bits[len(bits)-1] {
					case "goget":
						return hashIf + register + "=cast(" + l.IndirectValue(args[0], errorInfo) + "," +
							rxTyp + ")." + bits[len(bits)-2][1:] + ";" + hashEnd
					case "goset":
						return hashIf + "cast(" + l.IndirectValue(args[0], errorInfo) + "," +
							rxTyp + ")." + bits[len(bits)-2][1:] +
							"=" + l.IndirectValue(args[1], errorInfo) + ";" + hashEnd
					default:
						targetFunc = "cast(" + l.IndirectValue(args[0], errorInfo) + ","
						targetFunc += rxTyp + ")." + bits[len(bits)-1][1:] //remove leading capital letter

						args = args[1:]
					}
				} else {
					switch bits[len(bits)-1] {
					case "new": // special processing for creating a new class
						targetFunc = "new " + strings.Join(bits[:len(bits)-1], ".") // put it back into the Haxe format for names
					case "goget": // special processing to get a class static variable
						return hashIf + register + "=" +
							strings.Join(strings.Split(strings.Join(bits[:len(bits)-1], "."), "..."), "_") + ";" + hashEnd
					case "goset": // special processing to set a class static variable
						return hashIf + strings.Join(strings.Split(strings.Join(bits[:len(bits)-1], "."), "..."), "_") +
							"=" + l.IndirectValue(args[0], errorInfo) + ";" + hashEnd
					default:
						targetFunc = strings.Join(bits, ".") // put it back into the Haxe format for names
					}
				}
				targetFunc = strings.Join(strings.Split(targetFunc, "..."), "_")
				// end _HAXELIB SPECIAL PROCESSING
			} else {
				olv, ok := fnToVarOverloadMap[fnToCall]
				if ok { // replace the function call with a variable
					nextReturnAddress-- //decrement to set new return address for next call generation
					if register == "" {
						return ""
					}
					return register + "=" + olv + ";"
				}
				olf, ok := fnOverloadMap[fnToCall]
				if ok { // replace one go function with another
					targetFunc = olf
				} else {
					olf, ok := builtinOverloadMap[fnToCall]
					if ok { // replace a go function with a haxe one
						targetFunc = olf
						nextReturnAddress-- //decrement to set new return address for next call generation
						isBuiltin = true    // pretend we are in a builtin function to avoid passing 1st param as bindings or waiting for completion
					} else {
						// TODO at this point the package-level overloading could occur, but I cannot make it reliable, so code removed
					}
				}
			}

			switch cc.Value.(type) {
			case *ssa.Function: //simple case
				ret += targetFunc + "("
			case *ssa.MakeClosure: // it is a closure, but with a static callee
				ret += targetFunc + "("
			default: // closure with a dynamic callee
				ret += fnToCall + ".callFn([" // the callee is in a local variable
			}
		}
	}
	if !isBuiltin {
		if isGo {
			ret += "Scheduler.makeGoroutine(),"
		} else {
			ret += "this._goroutine,"
		}
	}
	switch cc.Value.(type) {
	case *ssa.Function: //simple case
		if !isBuiltin { // don't pass bindings to built-in functions
			ret += "[]" // goroutine + bindings
		}
	case *ssa.MakeClosure: // it is a closure, but with a static callee
		ret += "" + l.IndirectValue(cc.Value, errorInfo) + ".bds"
	default: // closure with a dynamic callee
		if !isBuiltin { // don't pass bindings to built-in functions
			ret += "" + fnToCall + ".bds"
		}
	}
	for arg := range args {
		if arg != 0 || !isBuiltin {
			ret += ","
		}
		// SAME LOGIC AS SWITCH IN INVOKE - keep in line
		switch args[arg].Type().Underlying().(type) { // TODO this may be in need of further optimization
		case *types.Pointer, *types.Slice, *types.Chan: // must pass a reference, not a copy
			ret += l.IndirectValue(args[arg], errorInfo)
		case *types.Basic: // NOTE Complex is an object as is Int64 (in java & cs), but copy does not seem to be required
			ret += l.IndirectValue(args[arg], errorInfo)
		case *types.Interface:
			if isHaxeAPI { // for Go interface{} parameters, substitute the Haxe Dynamic part
				ret += l.IndirectValue(args[arg], errorInfo) + ".val" // TODO check works in all situations
			} else {
				ret += l.IndirectValue(args[arg], errorInfo)
			}
		default: // TODO review
			ret += "Deep.copy(" + l.IndirectValue(args[arg], errorInfo) + ")"
		}
	}
	if isBuiltin {
		ret += ")"
	} else {
		switch cc.Value.(type) {
		case *ssa.Function, *ssa.MakeClosure: // it is a call with a list of args
			ret += ")"
		default: // it is a call with a single arg that is a list
			ret += "])" // the callee is in a local variable
		}
	}
	if isBuiltin {
		if register != "" {
			//**************************
			//TODO ensure correct conversions for interface{} <-> Dynamic when isHaxeAPI
			//**************************
			return hashIf + register + "=" + ret + ";" + hashEnd
		}
		return hashIf + ret + ";" + hashEnd
	}
	if isGo {
		return ret + "; "
	}
	if isDefer {
		return ret + ";\nthis.defer(Scheduler.pop(this._goroutine));"
	}
	return doCall(register, ret+";\n")
}