Example #1
0
// TODO see http://tip.golang.org/doc/go1.2#three_index
// TODO add third parameter when SSA code provides it to enable slice instructions to specify a capacity
func (l langType) Slice(register string, x, lv, hv interface{}, errorInfo string) string {
	xString := l.IndirectValue(x, errorInfo) // the target must be an array
	if xString == "" {
		xString = l.IndirectValue(x, errorInfo)
	}
	lvString := "0"
	if lv != nil {
		lvString = l.IndirectValue(lv, errorInfo)
		switch lv.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
		case types.Int64, types.Uint64:
			lvString = "GOint64.toInt(" + lvString + ")"
		}
	}
	hvString := "-1"
	if hv != nil {
		hvString = l.IndirectValue(hv, errorInfo)
		switch hv.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
		case types.Int64, types.Uint64:
			hvString = "GOint64.toInt(" + hvString + ")"
		}
	}
	switch x.(ssa.Value).Type().Underlying().(type) {
	case *types.Slice:
		return register + "=" + xString + `.subSlice(` + lvString + `,` + hvString + `);`
	case *types.Pointer:
		return register + "=new Slice(" + xString + `,` + lvString + `,` + hvString + `);` //was: ".copy();"
	case *types.Basic: // assume a string is in need of slicing...
		return register + "=Force.toRawString(this._goroutine,Force.toUTF8slice(this._goroutine," + xString +
			`).subSlice(` + lvString + `,` + hvString + `)` + `);`
	default:
		pogo.LogError(errorInfo, "Haxe",
			fmt.Errorf("haxe.Slice() - unhandled type: %v", reflect.TypeOf(x.(ssa.Value).Type().Underlying())))
		return ""
	}
}
Example #2
0
func (l langType) intTypeCoersion(t types.Type, v, errorInfo string) string {
	switch t.(type) {
	case *types.Basic:
		switch t.(*types.Basic).Kind() {
		case types.Int8:
			return "Force.toInt8(" + v + ")"
		case types.Int16:
			return "Force.toInt16(" + v + ")"
		case types.Int32, types.Int: // NOTE type int is always int32
			return "Force.toInt32(" + v + ")"
		case types.Int64:
			return "Force.toInt64(" + v + ")"
		case types.Uint8:
			return "Force.toUint8(" + v + ")"
		case types.Uint16:
			return "Force.toUint16(" + v + ")"
		case types.Uint32, types.Uint: // NOTE type uint is always uint32
			return "Force.toUint32(" + v + ")"
		case types.Uint64:
			return "Force.toUint64(" + v + ")"
		case types.UntypedInt, types.UntypedRune:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.intTypeCoersion(): unhandled types.UntypedInt or types.UntypedRune"))
			return ""
		case types.Uintptr: // held as the Dynamic type in Haxe
			return "" + v + "" // TODO review correct thing to do here
		default:
			return v
		}
	default:
		return v
	}
}
Example #3
0
func tgoString(s, errorInfo string) string {
	bits := strings.Split(s, `"`)
	if len(bits) < 2 {
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("hx.() argument is not a usable string constant"))
		return ""
	}
	return bits[1]
}
Example #4
0
// Returns the textual version of Value, possibly emmitting an error
// can't merge with indirectValue, as this is used by emit-func-setup to get register names
func (l langType) Value(v interface{}, errorInfo string) string {
	val, ok := v.(ssa.Value)
	if !ok {
		return "" // if it is not a value, an empty string will be returned
	}
	switch v.(type) {
	case *ssa.Global:
		return "Go." + l.LangName(v.(*ssa.Global).Pkg.Object.Name(), v.(*ssa.Global).Name())
	case *ssa.Alloc, *ssa.MakeSlice:
		return pogo.RegisterName(v.(ssa.Value))
	case *ssa.FieldAddr, *ssa.IndexAddr:
		return pogo.RegisterName(v.(ssa.Value))
	case *ssa.Const:
		ci := v.(*ssa.Const)
		_, c := l.Const(*ci, errorInfo)
		return c
	case *ssa.Parameter:
		return "p_" + pogo.MakeId(v.(*ssa.Parameter).Name())
	case *ssa.Capture:
		for b := range v.(*ssa.Capture).Parent().FreeVars {
			if v.(*ssa.Capture) == v.(*ssa.Capture).Parent().FreeVars[b] { // comparing the name gives the wrong result
				return `_bds[` + fmt.Sprintf("%d", b) + `]`
			}
		}
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Value(): *ssa.Capture name not found: %s", v.(*ssa.Capture).Name()))
		return `_bds["_b` + "ERROR: Captured bound variable name not found" + `"]` // TODO proper error
	case *ssa.Function:
		pk := "unknown"
		if v.(*ssa.Function).Signature.Recv() != nil { // it's a method
			pk = v.(*ssa.Function).Signature.Recv().Pkg().Name() + "." + v.(*ssa.Function).Signature.Recv().Name()
		} else {
			if v.(*ssa.Function).Pkg != nil {
				if v.(*ssa.Function).Pkg.Object != nil {
					pk = v.(*ssa.Function).Pkg.Object.Name()
				}
			}
		}
		if len(v.(*ssa.Function).Blocks) > 0 { //the function actually exists
			return "new Closure(Go_" + l.LangName(pk, v.(*ssa.Function).Name()) + ".call,[])" //TODO will change for go instr
		}
		// function has no implementation
		// TODO maybe put a list of over-loaded functions here and only error if not found
		// NOTE the reflect package comes through this path TODO fix!
		pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("haxe.Value(): *ssa.Function has no implementation: %s", v.(*ssa.Function).Name()))
		return "new Closure(null,[])" // Should fail at runtime if it is used...
	case *ssa.UnOp:
		return pogo.RegisterName(val)
	case *ssa.BinOp:
		return pogo.RegisterName(val)
	case *ssa.MakeInterface:
		return pogo.RegisterName(val)
	default:
		return pogo.RegisterName(val)
	}
}
Example #5
0
// utiltiy to set-up a haxe variable
func haxeVar(reg, typ, init, position, errorStart string) string {
	if typ == "" {
		pogo.LogError(position, "Haxe", fmt.Errorf(errorStart+" unhandled initialisation for empty type"))
		return ""
	}
	ret := "var " + reg + ":" + typ
	if init != "" {
		ret += "=" + init
	}
	return ret + ";"
}
Example #6
0
func (l langType) IndexAddr(register string, v interface{}, errorInfo string) string {
	if register == "" {
		return "" // we can't make an address if there is nowhere to put it...
	}
	idxString := l.IndirectValue(v.(*ssa.IndexAddr).Index, errorInfo)
	switch v.(*ssa.IndexAddr).Index.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
	case types.Int64, types.Uint64:
		idxString = idxString + ".toInt()"
	}
	switch v.(*ssa.IndexAddr).X.Type().Underlying().(type) {
	case *types.Pointer, *types.Slice:
		return fmt.Sprintf(`%s=%s.addr(%s);`, register,
			l.IndirectValue(v.(*ssa.IndexAddr).X, errorInfo),
			idxString)
	case *types.Array: // need to create a pointer before using it
		return fmt.Sprintf(`%s={var _v=new Pointer(%s); _v.addr(%s);};`, register,
			l.IndirectValue(v.(*ssa.IndexAddr).X, errorInfo),
			idxString)
	default:
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.IndirectValue():IndexAddr unknown operand type"))
		return ""
	}
}
Example #7
0
func (l langType) codeBinOp(op string, v1, v2 interface{}, errorInfo string) string {
	ret := ""
	useInt64 := false
	v1LangType := l.LangType(v1.(ssa.Value).Type().Underlying(), false, errorInfo)
	v2LangType := l.LangType(v2.(ssa.Value).Type().Underlying(), false, errorInfo)
	v1string := l.IndirectValue(v1, errorInfo)
	v2string := l.IndirectValue(v2, errorInfo)
	if v1LangType == "GOint64" {
		useInt64 = true
	}

	// neko target platform requires special handling because in makes whole-number Float into Int without asking
	// see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but not fixed as at 2013.9.6
	switch v1LangType {
	case "Float":
		v1string = "Force.toFloat(" + v1string + ")"
	case "Dynamic": // assume it is a uintptr, so integer arithmetic is required
		v1string = "(" + v1string + "|0)"
	}
	switch v2LangType {
	case "Float":
		v2string = "Force.toFloat(" + v2string + ")"
	case "Dynamic": // assume it is a uintptr, so integer arithmetic is required
		v2string = "(" + v2string + "|0)"
	}

	if v1LangType == "Complex" {
		switch op {
		case "+":
			return "Complex.add(" + v1string + "," + v2string + ")"
		case "/": // TODO review divide by zero error handling for this case (currently in Haxe Complex class)
			return "Complex.div(" + v1string + "," + v2string + ")"
		case "*":
			return "Complex.mul(" + v1string + "," + v2string + ")"
		case "-":
			return "Complex.sub(" + v1string + "," + v2string + ")"
		case "==":
			return "Complex.eq(" + v1string + "," + v2string + ")"
		case "!=":
			return "Complex.neq(" + v1string + "," + v2string + ")"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Complex op: %s", op))
			return ""
		}

	} else if v1LangType == "String" {
		switch op {
		case ">", "<", "<=", ">=":
			return "(Go_haxegoruntime_StringCompare.callFromRT(this._goroutine," + v1string + "," + v2string +
				")" + op + "0)"
		default:
			return "(" + v1string + op + v2string + ")"
		}

	} else if v1LangType == "Interface" {
		switch op {
		case "==":
			return "Interface.isEqual(" + v1string + "," + v2string + ")"
		case "!=":
			return "!Interface.isEqual(" + v1string + "," + v2string + ")"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Interface op: %s", op))
			return ""
		}

	} else {
		if useInt64 { // explicitly enumerate all of the Int64 functions
			isSignedStr := "true"
			if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 {
				isSignedStr = "false"
			}

			switch op { // roughly in the order of the GOint64 api spec
			case "+":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.add("+v1string+","+v2string+")", errorInfo)
			case "&":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.and("+v1string+","+v2string+")", errorInfo)
			case "/":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.div("+v1string+","+v2string+","+isSignedStr+")", errorInfo)
			case "%":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.mod("+v1string+","+v2string+","+isSignedStr+")", errorInfo)
			case "*":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.mul("+v1string+","+v2string+")", errorInfo)
			case "|":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.or("+v1string+","+v2string+")", errorInfo)
			case "<<":
				if v2LangType == "GOint64" {
					v2string = "GOint64.toInt(" + v2string + ")"
				}
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.shl("+v1string+","+v2string+")", errorInfo)
			case ">>":
				if v2LangType == "GOint64" {
					v2string = "GOint64.toInt(" + v2string + ")"
				}
				if isSignedStr == "true" {
					ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
						"GOint64.shr("+v1string+","+v2string+")", errorInfo) // GOint64.shr does sign extension
				} else {
					ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
						"GOint64.ushr("+v1string+","+v2string+")", errorInfo) // GOint64.ushr does not do sign extension
				}
			case "-":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.sub("+v1string+","+v2string+")", errorInfo)
			case "^":
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.xor("+v1string+","+v2string+")", errorInfo)
			case "&^":
				v2string = "GOint64.xor(" + v2string + ",GOint64.make(-1,-1))"
				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
					"GOint64.and("+v1string+","+v2string+")", errorInfo)
			case "==", "!=", "<", ">", "<=", ">=":
				compFunc := "GOint64.compare("
				if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 {
					compFunc = "GOint64.ucompare("
				}
				ret = "(" + compFunc + v1string + "," + v2string + ")" + op + "0)"
			default:
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled 64-bit op: %s", op))
				return ""
			}

		} else {
			switch op { // standard case, use Haxe operators
			case "==", "!=", "<", ">", "<=", ">=": // no integer coersion, boolian results
				switch v1.(ssa.Value).Type().Underlying().(type) {
				case *types.Basic:
					if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 {
						ret = "(Force.uintCompare(" + v1string + "," + v2string + ")" + op + "0)"
					} else {
						ret = "(" + v1string + op + v2string + ")"
					}
				default:
					ret = "(" + v1string + op + v2string + ")"
				}
			case ">>", "<<":
				if v2LangType == "GOint64" {
					v2string = "GOint64.toInt(" + v2string + ")"
				}
				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned bit shift
					if op == ">>" {
						op = ">>>" // logical right shift if unsigned
					}
				}
				ret = "({var _v1:Int=" + v1string + "; var _v2:Int=" + v2string + "; _v2==0?_v1:_v1" + op + "_v2;})" //NoOp if v2==0
			case "/":
				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
				case types.Int8:
					ret = "Force.intDiv(" + v1string + "," + v2string + ",1)" // 1 byte special processing
				case types.Int16:
					ret = "Force.intDiv(" + v1string + "," + v2string + ",2)" // 2 byte special processing
				case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32
					ret = "Force.intDiv(" + v1string + "," + v2string + ",4)" // 4 byte special processing
				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned division
					ret = "Force.intDiv(" + v1string + "," + v2string + ",0)" // spec does not require special processing, but is unsigned
				case types.UntypedFloat, types.Float32, types.Float64:
					ret = "Force.floatDiv(" + v1string + "," + v2string + ")"
				default:
					pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type"))
					ret = "(ERROR)"
				}
			case "%":
				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
				case types.Int8:
					ret = "Force.intMod(" + v1string + "," + v2string + ", 1)"
				case types.Int16:
					ret = "Force.intMod(" + v1string + "," + v2string + ", 2)"
				case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32
					ret = "Force.intMod(" + v1string + "," + v2string + ", 4)"
				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned mod
					ret = "Force.intMod(" + v1string + "," + v2string + ", 0)"
				case types.UntypedFloat, types.Float32, types.Float64:
					ret = "Force.floatMod(" + v1string + "," + v2string + ")"
				default:
					pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type"))
					ret = "(ERROR)"
				}

			case "&^":
				op = "&~" // Haxe has a different operator for bit-wise complement
				fallthrough
			default:
				innerCode := "(" + v1string + op + v2string + ")"
				ret = l.intTypeCoersion(
					v1.(ssa.Value).Type().Underlying(),
					innerCode, errorInfo)
			}
		}
		return ret
	}
}
Example #8
0
func (l langType) codeUnOp(op string, v interface{}, CommaOK bool, errorInfo string) string {
	useInt64 := false
	lt := l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo)
	if lt == "GOint64" {
		useInt64 = true
	}
	// neko target platform requires special handling because in makes whole-number Float into Int without asking
	// see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but not fixed as at 2013.9.6

	switch op {
	case "<-":
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): impossible to reach <- code"))
		return ""
	case "*":
		lt = l.LangType(v.(ssa.Value).Type().Underlying().(*types.Pointer).Elem().Underlying(), false, errorInfo)
		iVal := "" + l.IndirectValue(v, errorInfo) + "" // need to cast it to pointer, when using -dce full and closures
		switch lt {
		case "Pointer":
			return "({var _v:Pointer=" + iVal + `.load(); _v;})` // Ensure Haxe can work out that it is a pointer being returned
		case "Int":
			return "(" + iVal + ".load()|0)" // force to Int for js, compiled platforms should optimize this away
		default:
			return iVal + ".load()"
		}
	case "-":
		if l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) == "Complex" {
			return "Complex.neg(" + l.IndirectValue(v, errorInfo) + ")"
		}
		fallthrough
	default:
		if useInt64 {
			switch op { // roughly in the order of the GOint64 api spec
			case "-":
				return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(),
					"GOint64.neg("+l.IndirectValue(v, errorInfo)+")", errorInfo)
			case "^":
				return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(),
					"GOint64.xor("+l.IndirectValue(v, errorInfo)+",GOint64.make(-1,-1))", errorInfo)
			default:
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): unhandled Int64 op: %s", op))
				return ""
			}
		} else {
			valStr := l.IndirectValue(v, errorInfo)
			switch v.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
			case types.Uintptr: // although held as Dynamic, uintpointers are integers when doing calculations
				valStr = "Force.toInt(" + valStr + ")"
			case types.Float32, types.Float64:
				valStr = "Force.toFloat(" + valStr + ")"
			}
			switch op {
			case "^":
				op = "~" // Haxe has a different operator for bit-wise complement
				fallthrough
			case "-": //both negation and bit-complement can overflow
				return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(),
					"("+op+valStr+")", errorInfo)
			default: //no overflow issues
				return "(" + op + valStr + ")"
			}
		}
	}
}
Example #9
0
func (l langType) hxPseudoFuncs(fnToCall string, args []ssa.Value, errorInfo string) string {
	//fmt.Println("DEBUG l.hxPseudoFuncs()", fnToCall, args, errorInfo)
	fnToCall = strings.TrimPrefix(fnToCall, pseudoFnPrefix)

	switch fnToCall {
	case "init":
		return "" // no need to generate code for the go init function
	case "RResource":
		return "Slice.fromResource(" + l.IndirectValue(args[0], errorInfo) + ");"
	case "MMalloc":
		return "Pointer.make(Object.make(Force.toInt(" + l.IndirectValue(args[0], errorInfo) + ")));"
	case "IIsNNull":
		return l.IndirectValue(args[0], errorInfo) + "==null;"
	case "NNull":
		return "null;"
	case "CComplex":
		return "cast(" + l.IndirectValue(args[0], errorInfo) + ",Complex);"
	case "IInt64":
		return "new GOint64(" + l.IndirectValue(args[0], errorInfo) + ");"
	case "CCallbackFFunc":
		// NOTE there will be a preceeding MakeInterface call that is made redundant by this code
		if len(args) == 1 {
			goMI, ok := args[0].(*ssa.MakeInterface)
			if ok {
				goFn, ok := (*(goMI.Operands(nil)[0])).(*ssa.Function)
				if ok {
					return "new Interface(-1," + l.IndirectValue(args[0], errorInfo) + ".val.buildCallbackFn()); // Go_" + l.FuncName(goFn)
				}
				_, ok = (*(goMI.Operands(nil)[0])).(*ssa.MakeClosure)
				if ok {
					return "new Interface(-1," + l.IndirectValue(args[0], errorInfo) + ".val.buildCallbackFn());"
				}
				con, ok := (*(goMI.Operands(nil)[0])).(*ssa.Const)
				if ok {
					return "new Interface(-1," + tgoString(l.IndirectValue(con, errorInfo), errorInfo) + ");"
				}
			}
		}
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("hx.Func() argument is not a function constant"))
		return ""
	}

	argOff := 1 // because of the ifLogic
	wrapStart := ""
	wrapEnd := ""
	usesArgs := true

	ifLogic := l.IndirectValue(args[0], errorInfo)
	//fmt.Println("DEBUG:ifLogic=", ifLogic, "AT", errorInfo)
	ifLogic = tgoString(ifLogic, errorInfo)
	if len(ifLogic) > 0 {
		wrapStart = " #if (" + ifLogic + ") "
		defVal := "null"
		if strings.HasSuffix(fnToCall, "Bool") {
			defVal = "false"
		}
		if strings.HasSuffix(fnToCall, "Int") {
			defVal = "0"
		}
		if strings.HasSuffix(fnToCall, "Float") {
			defVal = "0.0"
		}
		if strings.HasSuffix(fnToCall, "String") {
			defVal = `""`
		}
		wrapEnd = " #else " + defVal + "; #end "
	}

	if strings.HasSuffix(fnToCall, "SString") &&
		!strings.HasPrefix(fnToCall, "CCode") &&
		!strings.HasPrefix(fnToCall, "FFset") &&
		!strings.HasPrefix(fnToCall, "SSet") {
		wrapStart += " Force.fromHaxeString({"
		wrapEnd = "});" + wrapEnd
	}

	if strings.HasSuffix(fnToCall, "IIface") {
		argOff = 2
		wrapStart += "new Interface(TypeInfo.getId(" + l.IndirectValue(args[1], errorInfo) + "),{"
		wrapEnd = "});" + wrapEnd
	}
	code := ""
	if strings.HasPrefix(fnToCall, "NNew") {
		code = "new "
	}
	code += strings.Trim(l.IndirectValue(args[argOff], errorInfo), `"`) // trim quotes if it has any
	if strings.HasPrefix(fnToCall, "CCall") ||
		strings.HasPrefix(fnToCall, "MMeth") || strings.HasPrefix(fnToCall, "NNew") {
		argOff++
		if strings.HasPrefix(fnToCall, "MMeth") {
			haxeType := tgoString(l.IndirectValue(args[argOff], errorInfo), errorInfo)
			if len(haxeType) > 0 {
				code = "cast(" + code + "," + haxeType + ")"
			}
			argOff++
			obj := l.IndirectValue(args[argOff], errorInfo)
			code += "." + strings.Trim(obj, `"`) + "("
			argOff++
		} else {
			code += "("
		}
		textLen := l.IndirectValue(args[argOff], errorInfo) // see Const() for format
		aLen, err := strconv.ParseUint(textLen, 0, 64)
		if err != nil {
			code += " ERROR Go ParseUint on number of arguments to hx.Meth() or hx.Call() - " + err.Error() + "! "
		} else {
			if aLen == 0 {
				usesArgs = false
			}
			for i := uint64(0); i < aLen; i++ {
				if i > 0 {
					code += ","
				}
				//code += fmt.Sprintf("Force.toHaxeParam(_a.itemAddr(%d).load())", i)
				code += fmt.Sprintf("Force.toHaxeParam(_a.param(%d))", i)
			}
		}
		code += ");"
	}
	if strings.HasPrefix(fnToCall, "GGet") {
		code += ";"
		usesArgs = false
	}
	if strings.HasPrefix(fnToCall, "SSet") {
		argOff++
		code = code + "=" + l.IndirectValue(args[argOff], errorInfo) + ";"
		usesArgs = false
	}
	if strings.HasPrefix(fnToCall, "FFget") {
		argOff++
		if l.IndirectValue(args[argOff], errorInfo) != `""` {
			code = "cast(" + code + "," + tgoString(l.IndirectValue(args[argOff], errorInfo), errorInfo) + ")"
		}
		code += "." + tgoString(l.IndirectValue(args[argOff+1], errorInfo), errorInfo) + "; "
		usesArgs = false
	}
	if strings.HasPrefix(fnToCall, "FFset") {
		argOff++
		if l.IndirectValue(args[argOff], errorInfo) != `""` {
			code = "cast(" + code + "," + tgoString(l.IndirectValue(args[argOff], errorInfo), errorInfo) + ")"
		}
		code += "." + tgoString(l.IndirectValue(args[argOff+1], errorInfo), errorInfo) +
			"=Force.toHaxeParam(" + l.IndirectValue(args[argOff+2], errorInfo) + "); "
		usesArgs = false
	}

	ret := "{"
	if usesArgs {
		ret += "var _a=" + l.IndirectValue(args[argOff+1], errorInfo) + "; "
	}
	return ret + wrapStart + code + wrapEnd + " }"
}
Example #10
0
func (l langType) Convert(register, langType string, destType types.Type, v interface{}, errorInfo string) string {
	srcTyp := l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo)
	if srcTyp == langType { // no cast required because the Haxe type is the same
		return register + "=" + l.IndirectValue(v, errorInfo) + ";"
	}
	switch langType { // target Haxe type
	case "Dynamic": // no cast allowed for dynamic variables
		return register + "=" + l.IndirectValue(v, errorInfo) + ";" // TODO review if this is correct for Int64
	case "String":
		switch srcTyp {
		case "Slice":
			switch v.(ssa.Value).Type().Underlying().(*types.Slice).Elem().Underlying().(*types.Basic).Kind() {
			case types.Rune: // []rune
				return "{var _r:Slice=Go_haxegoruntime_Runes2Raw.callFromRT(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" +
					register + "=\"\";for(_i in 0..._r.len())" +
					register + "+=String.fromCharCode(_r.getAt(_i" + "));};"
			case types.Byte: // []byte
				return register + "=Force.toRawString(this._goroutine," + l.IndirectValue(v, errorInfo) + ");"
			default:
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected slice type to convert to String"))
				return ""
			}
		case "Int": // make a string from a single rune
			return "{var _r:Slice=Go_haxegoruntime_Rune2Raw.callFromRT(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" +
				register + "=\"\";for(_i in 0..._r.len())" +
				register + "+=String.fromCharCode(_r.getAt(_i" + "));};"
		case "GOint64": // make a string from a single rune (held in 64 bits)
			return "{var _r:Slice=Go_haxegoruntime_Rune2Raw.callFromRT(this._goroutine,GOint64.toInt(" + l.IndirectValue(v, errorInfo) + "));" +
				register + "=\"\";for(_i in 0..._r.len())" +
				register + "+=String.fromCharCode(_r.getAt(_i" + "));};"
		case "Dynamic":
			return register + "=cast(" + l.IndirectValue(v, errorInfo) + ",String);"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected type to convert to String: %s", srcTyp))
			return ""
		}
	case "Slice": // []rune or []byte
		if srcTyp != "String" {
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected type to convert to %s ([]rune or []byte): %s",
				langType, srcTyp))
			return ""
		}
		switch destType.Underlying().(*types.Slice).Elem().Underlying().(*types.Basic).Kind() {
		case types.Rune:
			return register + "=" + newSliceCode("Int", "0",
				l.IndirectValue(v, errorInfo)+".length", l.IndirectValue(v, errorInfo)+".length", errorInfo) + ";" +
				"for(_i in 0..." + l.IndirectValue(v, errorInfo) + ".length)" +
				register + ".setAt(_i,({var _c:Null<Int>=" + l.IndirectValue(v, errorInfo) +
				`.charCodeAt(_i);(_c==null)?0:cast(_c,Int);})` + ");" +
				register + "=Go_haxegoruntime_Raw2Runes.callFromRT(this._goroutine," + register + ");"
		case types.Byte:
			return register + "=Force.toUTF8slice(this._goroutine," + l.IndirectValue(v, errorInfo) + ");"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected slice elementto convert to %s ([]rune/[]byte): %s",
				langType, srcTyp))
			return ""
		}
	case "Int": //TODO check that unsigned handelled correctly here
		vInt := ""
		switch srcTyp {
		case "GOint64":
			vInt = "GOint64.toInt(" + l.IndirectValue(v, errorInfo) + ")"
		case "Float":
			vInt = "{var _f:Float=" + l.IndirectValue(v, errorInfo) + ";_f>=0?Math.floor(_f):Math.ceil(_f);}"
		default:
			vInt = "cast(" + l.IndirectValue(v, errorInfo) + "," + langType + ")"
		}
		return register + "=" + l.intTypeCoersion(destType, vInt, errorInfo) + ";"
	case "GOint64":
		switch srcTyp {
		case "Int":
			return register + "=GOint64.ofInt(" + l.IndirectValue(v, errorInfo) + ");"
		case "Float":
			if destType.Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.ofUFloat(" + l.IndirectValue(v, errorInfo) + ");"
			}
			return register + "=GOint64.ofFloat(" + l.IndirectValue(v, errorInfo) + ");"
		case "Dynamic": // uintptr
			return register + "=" + l.IndirectValue(v, errorInfo) + ";" // let Haxe work out how to do the cast
		default:
			return register + "=cast(" + l.IndirectValue(v, errorInfo) + "," + langType + ");" //TODO unreliable in Java from Dynamic?
		}
	case "Float":
		switch srcTyp {
		case "GOint64":
			if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.toUFloat(" + l.IndirectValue(v, errorInfo) + ");"
			}
			return register + "=GOint64.toFloat(" + l.IndirectValue(v, errorInfo) + ");"
		case "Int":
			if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.toUFloat(GOint64.make(0," + l.IndirectValue(v, errorInfo) + "));"
			}
			return register + "=" + l.IndirectValue(v, errorInfo) + ";" // just the default conversion to float required
		default:
			return register + "=cast(" + l.IndirectValue(v, errorInfo) + "," + langType + ");"
		}
	case "UnsafePointer":
		pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("attempt to convert a value to be an Unsafe Pointer, which is unsupported"))
		return register + "=new UnsafePointer(" + l.IndirectValue(v, errorInfo) + ");" // this will generate a runtime exception if called
	default:
		if strings.HasPrefix(srcTyp, "Array<") {
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - No way to convert to %s from %s ", langType, srcTyp))
			return ""
		}
		return register + "=cast(" + l.IndirectValue(v, errorInfo) + "," + langType + ");"
	}
}
Example #11
0
func (l langType) LangType(t types.Type, retInitVal bool, errorInfo string) string {
	if pogo.IsValidInPogo(t, errorInfo) {
		switch t.(type) {
		case *types.Basic:
			switch t.(*types.Basic).Kind() {
			case types.Bool, types.UntypedBool:
				if retInitVal {
					return "false"
				}
				return "Bool"
			case types.String, types.UntypedString:
				if retInitVal {
					return `""`
				}
				return "String"
			case types.Float64, types.Float32, types.UntypedFloat:
				if retInitVal {
					return "0.0"
				}
				return "Float"
			case types.Complex64, types.Complex128, types.UntypedComplex:
				if retInitVal {
					return "new Complex(0.0,0.0)"
				}
				return "Complex"
			case types.Int, types.Int8, types.Int16, types.Int32, types.UntypedRune,
				types.Uint8, types.Uint16, types.Uint, types.Uint32: // NOTE: untyped integers default to Int without a warning
				if retInitVal {
					return "0"
				}
				return "Int"
			case types.Int64, types.Uint64:
				if retInitVal {
					return "GOint64.make(0,0)"
				}
				return "GOint64"
			case types.UntypedInt: // TODO: investigate further the situations in which this warning is generated
				pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("haxe.LangType() types.UntypedInt is ambiguous"))
				return "UNTYPED_INT" // NOTE: if this value were ever to be used, it would cause a Haxe compilation error
			case types.UnsafePointer:
				if retInitVal {
					return "new UnsafePointer(null)" // TODO is this correct? or should it be null
				}
				return "UnsafePointer"
			case types.Uintptr: // Uintptr sometimes used as an integer type, sometimes as a container for another type
				if retInitVal {
					return "null"
				}
				return "Dynamic"
			default:
				pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("haxe.LangType() unrecognised basic type, Dynamic assumed"))
				if retInitVal {
					return "null"
				}
				return "Dynamic"
			}
		case *types.Interface:
			if retInitVal {
				return `null`
			}
			return "Interface"
		case *types.Named:
			return l.LangType(t.(*types.Named).Underlying(), retInitVal, errorInfo)
		case *types.Chan:
			if retInitVal {
				return "new Channel<" + l.LangType(t.(*types.Chan).Elem(), false, errorInfo) + ">(1)"
			}
			return "Channel<" + l.LangType(t.(*types.Chan).Elem(), false, errorInfo) + ">"
		case *types.Map:
			if retInitVal {
				return "new Map<" + l.LangType(t.(*types.Map).Key(), false, errorInfo) + "," +
					l.LangType(t.(*types.Map).Elem(), false, errorInfo) + ">()"
			}
			return "Map<" + l.LangType(t.(*types.Map).Key(), false, errorInfo) + "," +
				l.LangType(t.(*types.Map).Elem(), false, errorInfo) + ">"
		case *types.Slice:
			if retInitVal {
				return "new Slice(new Pointer(new Array<" + l.LangType(t.(*types.Slice).Elem(), false, errorInfo) +
					">()),0,0" + ")"
			}
			return "Slice"
		case *types.Array: // TODO consider using Vector rather than Array, if faster and can be made to work
			if retInitVal {
				return "{var _v=new Array<" +
					l.LangType(t.(*types.Array).Elem(), false, errorInfo) +
					fmt.Sprintf(">();for(_vi in 0...%d){_v[_vi]=%s;}; _v;}",
						t.(*types.Array).Len(), l.LangType(t.(*types.Array).Elem(), true, errorInfo))

			}
			return "Array<" + l.LangType(t.(*types.Array).Elem(), false, errorInfo) + ">"
		case *types.Struct: // TODO as this is fixed-length, should it be a Vector, like types.Array ?
			// TODO consider if it is possible to change from Array<Dynamic> to individual named elements - requires considerable work to do this
			if retInitVal {
				ret := "{var _v=new Array<Dynamic>();_v=["
				for ele := 0; ele < t.(*types.Struct).NumFields(); ele++ {
					if ele != 0 {
						ret += ","
					}
					ret += l.LangType(t.(*types.Struct).Field(ele).Type().Underlying(), true, errorInfo)
				}
				return ret + "]; _v;}"
			}
			return "Array<Dynamic>"
		case *types.Tuple: // what is returned by a call and some other instructions, not in the Go language spec!
			tup := t.(*types.Tuple)
			switch tup.Len() {
			case 0:
				return ""
			case 1:
				return l.LangType(tup.At(0).Type().Underlying(), retInitVal, errorInfo)
			default:
				ret := "{"
				for ele := 0; ele < tup.Len(); ele++ {
					if ele != 0 {
						ret += ","
					}
					ret += pogo.MakeId("r"+fmt.Sprintf("%d", ele)) +
						":" + l.LangType(tup.At(ele).Type().Underlying(), retInitVal, errorInfo)
				}
				return ret + "}"
			}
		case *types.Pointer:
			if retInitVal {
				// NOTE pointer declarations create endless recursion for self-referencing structures unless initialized with null
				return "null" //rather than: + l.LangType(t.(*types.Pointer).Elem(), retInitVal, errorInfo) + ")"
			}
			return "Pointer"
		case *types.Signature:
			if retInitVal {
				return "null"
			}
			ret := "Closure"
			return ret
		default:
			rTyp := reflect.TypeOf(t).String()
			if rTyp == "*ssa.opaqueType" { // NOTE the type for map itterators, not in the Go language spec!
				if retInitVal { // use dynamic type, brief tests seem OK, but may not always work on static hosts
					return "null"
				}
				return "Dynamic"
			}
			pogo.LogError(errorInfo, "Haxe",
				fmt.Errorf("haxe.LangType() internal error, unhandled non-basic type: %s", rTyp))
		}
	}
	return "UNKNOWN_LANGTYPE" // this should generate a Haxe compiler error
}
Example #12
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")
}
Example #13
0
/* SSA DOCUMENTAION EXTRACT
The Select instruction tests whether (or blocks until) one or more of the specified sent or received states is entered.

Let n be the number of States for which Dir==RECV and T_i (0<=i<n) be the element type of each such state's Chan.
Select returns an n+2-tuple

(index int, recvOk bool, r_0 T_0, ... r_n-1 T_n-1)
The tuple's components, described below, must be accessed via the Extract instruction.

If Blocking, select waits until exactly one state holds, i.e. a channel becomes ready for the designated operation
of sending or receiving; select chooses one among the ready states pseudorandomly, performs the send or receive operation,
and sets 'index' to the index of the chosen channel.

If !Blocking, select doesn't block if no states hold; instead it returns immediately with index equal to -1.

If the chosen channel was used for a receive, the r_i component is set to the received value,
where i is the index of that state among all n receive states; otherwise r_i has the zero value of type T_i.
Note that the the receive index i is not the same as the state index index.

The second component of the triple, recvOk, is a boolean whose value is true iff
the selected operation was a receive and the receive successfully yielded a value.
*/
func (l langType) Select(isSelect bool, register string, v interface{}, CommaOK bool, errorInfo string) string {
	ret := emitReturnHere() // even if we are in a non-blocking select, we need to give the other goroutines a chance!
	if isSelect {
		sel := v.(*ssa.Select)
		if register == "" {
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("select statement has no register"))
			return ""
		}
		ret += register + "=" + l.LangType(v.(ssa.Value).Type(), true, errorInfo) + ";\n" //initialize
		ret += register + ".r0= -1;\n"                                                    // the returned index if nothing is found

		// Spec requires a pseudo-random order to which item is processed
		ret += fmt.Sprintf("{ var _states:Array<Bool> = new Array(); var _rnd=Std.random(%d);\n", len(sel.States))
		for s := range sel.States {
			switch sel.States[s].Dir {
			case types.SendOnly:
				ch := l.IndirectValue(sel.States[s].Chan, errorInfo)
				ret += fmt.Sprintf("_states[%d]=%s.hasSpace();\n", s, ch)
			case types.RecvOnly:
				ch := l.IndirectValue(sel.States[s].Chan, errorInfo)
				ret += fmt.Sprintf("_states[%d]=%s.hasContents();\n", s, ch)
			default:
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("select statement has invalid ChanDir"))
				return ""
			}
		}
		ret += fmt.Sprintf("for(_s in 0...%d) {var _i=(_s+_rnd)%s%d; if(_states[_i]) {%s.r0=_i; break;};}\n",
			len(sel.States), "%", len(sel.States), register)
		ret += fmt.Sprintf("switch(%s.r0){", register)
		rxIdx := 0
		for s := range sel.States {
			ret += fmt.Sprintf("case %d:\n", s)
			switch sel.States[s].Dir {
			case types.SendOnly:
				ch := l.IndirectValue(sel.States[s].Chan, errorInfo)
				snd := l.IndirectValue(sel.States[s].Send, errorInfo)
				ret += fmt.Sprintf("%s.send(%s);\n", ch, snd)
			case types.RecvOnly:
				ch := l.IndirectValue(sel.States[s].Chan, errorInfo)
				ret += fmt.Sprintf("{ var _v=%s.receive(%s); ", ch,
					l.LangType(sel.States[s].Chan.(ssa.Value).Type().Underlying().(*types.Chan).Elem().Underlying(), true, errorInfo))
				ret += fmt.Sprintf("%s.r%d= _v.r0; ", register, 2+rxIdx)
				rxIdx++
				ret += register + ".r1= _v.r1; }\n"
			default:
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("select statement has invalid ChanDir"))
				return ""
			}
		}
		ret += "};}\n" // end switch; _states, _rnd scope
		if sel.Blocking {
			ret += "if(" + register + ".r0 == -1) return this;\n"
		}

	} else {
		ret += "if(" + l.IndirectValue(v, errorInfo) + ".hasNoContents())return this;\n" // go round the loop again and wait if not OK
		if register != "" {
			ret += register + "="
		}
		ret += l.IndirectValue(v, errorInfo) + ".receive("
		ret += l.LangType(v.(ssa.Value).Type().Underlying().(*types.Chan).Elem().Underlying(), true, errorInfo) + ")" // put correct result into register
		if !CommaOK {
			ret += ".r0"
		}
		ret += ";"
	}
	nextReturnAddress-- // decrement to set new return address for next code generation
	return ret
}
Example #14
0
func (l langType) LangType(t types.Type, retInitVal bool, errorInfo string) string {
	if pogo.IsValidInPogo(t, errorInfo) {
		switch t.(type) {
		case *types.Basic:
			switch t.(*types.Basic).Kind() {
			case types.Bool, types.UntypedBool:
				if retInitVal {
					return "false"
				}
				return "Bool"
			case types.String, types.UntypedString:
				if retInitVal {
					return `""`
				}
				return "String"
			case types.Float64, types.Float32, types.UntypedFloat:
				if retInitVal {
					return "0.0"
				}
				return "Float"
			case types.Complex64, types.Complex128, types.UntypedComplex:
				if retInitVal {
					return "new Complex(0.0,0.0)"
				}
				return "Complex"
			case types.Int, types.Int8, types.Int16, types.Int32, types.UntypedRune,
				types.Uint8, types.Uint16, types.Uint, types.Uint32: // NOTE: untyped runes default to Int without a warning
				if retInitVal {
					return "0"
				}
				return "Int"
			case types.Int64, types.Uint64:
				if retInitVal {
					return "GOint64.ofInt(0)"
				}
				return "GOint64"
			case types.UntypedInt: // TODO: investigate further the situations in which this warning is generated
				if retInitVal {
					return "0"
				}
				return "UNTYPED_INT" // NOTE: if this value were ever to be used, it would cause a Haxe compilation error
			case types.UnsafePointer:
				if retInitVal {
					return "null" // NOTE ALL pointers are unsafe
				}
				return "Pointer"
			case types.Uintptr: // Uintptr sometimes used as an integer type, sometimes as a container for another type
				if retInitVal {
					return "null"
				}
				return "Dynamic"
			default:
				pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("haxe.LangType() unrecognised basic type, Dynamic assumed"))
				if retInitVal {
					return "null"
				}
				return "Dynamic"
			}
		case *types.Interface:
			if retInitVal {
				return `null`
			}
			return "Interface"
		case *types.Named:
			haxeName := getHaxeClass(t.(*types.Named).String())
			//fmt.Println("DEBUG Go named type -> Haxe type :", t.(*types.Named).String(), "->", haxeName)
			if haxeName != "" {
				if retInitVal {
					return `null` // NOTE code to the right does not work in openfl/flash: `Type.createEmptyInstance(` + haxeName + ")"
				}
				return haxeName
			}
			return l.LangType(t.(*types.Named).Underlying(), retInitVal, errorInfo)
		case *types.Chan:
			if retInitVal {
				return "new Channel(1)" //waa: <" + l.LangType(t.(*types.Chan).Elem(), false, errorInfo) + ">(1)"
			}
			return "Channel" //was: <" + l.LangType(t.(*types.Chan).Elem(), false, errorInfo) + ">"
		case *types.Map:
			if retInitVal {
				k := t.(*types.Map).Key().Underlying()
				kv := l.LangType(k, true, errorInfo)
				e := t.(*types.Map).Elem().Underlying()
				ev := "null" // TODO review, required for encode/gob to stop recursion
				if _, isMap := e.(*types.Map); !isMap {
					ev = l.LangType(e, true, errorInfo)
				}
				return "new GOmap(" + kv + "," + ev + ")"
			}
			return "GOmap"
		case *types.Slice:
			if retInitVal {
				return "new Slice(Pointer.make(" +
					"Object.make(0)" +
					"),0,0,0," + "1" + arrayOffsetCalc(t.(*types.Slice).Elem().Underlying()) + ")"
			}
			return "Slice"
		case *types.Array:
			if retInitVal {
				return fmt.Sprintf("Object.make(%d)", haxeStdSizes.Sizeof(t))
			}
			return "Object"
		case *types.Struct:
			if retInitVal {
				return fmt.Sprintf("Object.make(%d)", haxeStdSizes.Sizeof(t.(*types.Struct).Underlying()))
			}
			return "Object"
		case *types.Tuple: // what is returned by a call and some other instructions, not in the Go language spec!
			tup := t.(*types.Tuple)
			switch tup.Len() {
			case 0:
				return ""
			case 1:
				return l.LangType(tup.At(0).Type().Underlying(), retInitVal, errorInfo)
			default:
				ret := "{"
				for ele := 0; ele < tup.Len(); ele++ {
					if ele != 0 {
						ret += ","
					}
					ret += pogo.MakeID("r"+fmt.Sprintf("%d", ele)) + ":"
					if !retInitVal {
						ret += "Null<"
					}
					ret += l.LangType(tup.At(ele).Type().Underlying(), retInitVal, errorInfo)
					if !retInitVal {
						ret += ">"
					}
				}
				return ret + "}"
			}
		case *types.Pointer:
			if retInitVal {
				// NOTE pointer declarations create endless recursion for self-referencing structures unless initialized with null
				return "null" //rather than: + l.LangType(t.(*types.Pointer).Elem(), retInitVal, errorInfo) + ")"
			}
			return "Pointer"
		case *types.Signature:
			if retInitVal {
				return "null"
			}
			ret := "Closure"
			return ret
		default:
			rTyp := reflect.TypeOf(t).String()
			if rTyp == "*ssa.opaqueType" { // NOTE the type for map itterators, not in the Go language spec!
				if retInitVal { // use dynamic type, brief tests seem OK, but may not always work on static hosts
					return "null"
				}
				return "Dynamic"
			}
			pogo.LogError(errorInfo, "Haxe",
				fmt.Errorf("haxe.LangType() internal error, unhandled non-basic type: %s", rTyp))
		}
	}
	return "UNKNOWN_LANGTYPE" // this should generate a Haxe compiler error
}
Example #15
0
func (l langType) Convert(register, langType string, destType types.Type, v interface{}, errorInfo string) string {
	srcTyp := l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo)
	if srcTyp == langType && langType != "Float" && langType != "Int" { // no cast required because the Haxe type is the same
		return register + "=" + l.IndirectValue(v, errorInfo) + ";"
	}
	switch langType { // target Haxe type
	case "Dynamic": // no cast allowed for dynamic variables
		vInt := l.IndirectValue(v, errorInfo)
		// but some Go code uses uintptr as just another integer, so ensure it is unsigned
		switch srcTyp {
		case "GOint64":
			vInt = "Force.toUint32(GOint64.toInt(" + vInt + "))"
		case "Float":
			vInt = "Force.toUint32({var _f:Float=" + vInt + ";_f>=0?Math.floor(_f):Math.ceil(_f);})" // same as signed
		case "Int":
			vInt = "Force.toUint32(" + vInt + ")"
		}
		return register + "=" + vInt + ";"
	case "Pointer":
		if srcTyp == "Dynamic" {
			_ptr := "_ptr"
			if pogo.DebugFlag {
				_ptr = "Pointer.check(_ptr)"
			}
			return register + "=({var _ptr=" + l.IndirectValue(v, errorInfo) + ";_ptr==null?null:" +
				_ptr + ";});"
		}
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - can only convert uintptr to unsafe.Pointer"))
		return ""
	case "String":
		switch srcTyp {
		case "Slice":
			switch v.(ssa.Value).Type().Underlying().(*types.Slice).Elem().Underlying().(*types.Basic).Kind() {
			case types.Rune: // []rune
				return register +
					"=Force.toRawString(this._goroutine,Go_haxegoruntime_RRunesTToUUTTFF8.callFromRT(this._goroutine," +
					l.IndirectValue(v, errorInfo) + "));"
			case types.Byte: // []byte
				return register + "=Force.toRawString(this._goroutine," + l.IndirectValue(v, errorInfo) + ");"
			default:
				pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected slice type to convert to String"))
				return ""
			}
		case "Int": // make a string from a single rune
			//return register + "=({var _ret:String;var _r:Slice=Go_haxegoruntime_RRune2RRaw.callFromRT(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" +
			//	"_ret=\"\";for(_i in 0..._r.len())" +
			//	"_ret+=String.fromCharCode(_r.itemAddr(_i).load_int32(" + "));_ret;});"
			return register + "=Force.stringFromRune(" + l.IndirectValue(v, errorInfo) + ");"
		case "GOint64": // make a string from a single rune (held in 64 bits)
			//return register + "=({var _ret:String;var _r:Slice=Go_haxegoruntime_RRune2RRaw.callFromRT(this._goroutine,GOint64.toInt(" + l.IndirectValue(v, errorInfo) + "));" +
			//	"_ret=\"\";for(_i in 0..._r.len())" +
			//	"_ret+=String.fromCharCode(_r.itemAddr(_i).load_int32(" + "));_ret;});"
			return register + "=Force.stringFromRune(GOint64.toInt(" + l.IndirectValue(v, errorInfo) + "));"
		case "Dynamic":
			return register + "=cast(" + l.IndirectValue(v, errorInfo) + ",String);"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected type to convert to String: %s", srcTyp))
			return ""
		}
	case "Slice": // []rune or []byte
		if srcTyp != "String" {
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected type to convert to %s ([]rune or []byte): %s",
				langType, srcTyp))
			return ""
		}
		switch destType.Underlying().(*types.Slice).Elem().Underlying().(*types.Basic).Kind() {
		case types.Rune:
			//return register + "=" + newSliceCode("Int", "0",
			//	l.IndirectValue(v, errorInfo)+".length",
			//	l.IndirectValue(v, errorInfo)+".length", errorInfo, "4 /*len(rune)*/") + ";" +
			//	"for(_i in 0..." + l.IndirectValue(v, errorInfo) + ".length)" +
			//	register + ".itemAddr(_i).store_int32(({var _c:Null<Int>=" + l.IndirectValue(v, errorInfo) +
			//	`.charCodeAt(_i);(_c==null)?0:Std.int(_c)&0xff;})` + ");" +
			//	register + "=Go_haxegoruntime_Raw2Runes.callFromRT(this._goroutine," + register + ");"
			return register +
				"=Go_haxegoruntime_UUTTFF8toRRunes.callFromRT(this._goroutine,Force.toUTF8slice(this._goroutine," +
				l.IndirectValue(v, errorInfo) + "));"
		case types.Byte:
			return register + "=Force.toUTF8slice(this._goroutine," + l.IndirectValue(v, errorInfo) + ");"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected slice elementto convert to %s ([]rune/[]byte): %s",
				langType, srcTyp))
			return ""
		}
	case "Int":
		vInt := ""
		switch srcTyp {
		case "Int":
			vInt = l.IndirectValue(v, errorInfo) // to get the type coercion below
		case "GOint64":
			vInt = "GOint64.toInt(" + l.IndirectValue(v, errorInfo) + ")" // un/signed OK as just truncates
		case "Float":
			vInt = "{var _f:Float=" + l.IndirectValue(v, errorInfo) + ";_f>=0?Math.floor(_f):Math.ceil(_f);}"
		case "Dynamic":
			vInt = "Force.toInt(" + l.IndirectValue(v, errorInfo) + ")" // Dynamic == uintptr
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - unhandled convert to u/int from: %s", srcTyp))
			return ""
		}
		return register + "=" + l.intTypeCoersion(destType, vInt, errorInfo) + ";"
	case "GOint64":
		switch srcTyp {
		case "Int":
			if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.ofUInt(" + l.IndirectValue(v, errorInfo) + ");"
			}
			return register + "=GOint64.ofInt(" + l.IndirectValue(v, errorInfo) + ");"
		case "Float":
			if destType.Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.ofUFloat(" + l.IndirectValue(v, errorInfo) + ");"
			}
			return register + "=GOint64.ofFloat(" + l.IndirectValue(v, errorInfo) + ");"
		case "Dynamic": // uintptr
			return register + "=GOint64.ofUInt(Force.toInt(" + l.IndirectValue(v, errorInfo) + "));" // let Haxe work out how to do the cast
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - unhandled convert to u/int64 from: %s", srcTyp))
			return ""
		}
	case "Float":
		switch srcTyp {
		case "GOint64":
			if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.toUFloat(" + l.IndirectValue(v, errorInfo) + ");"
			}
			return register + "=GOint64.toFloat(" + l.IndirectValue(v, errorInfo) + ");"
		case "Int":
			if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
				return register + "=GOint64.toUFloat(GOint64.make(0," + l.IndirectValue(v, errorInfo) + "));"
			}
			return register + "=Force.toFloat(" + l.IndirectValue(v, errorInfo) + ");" // just the default conversion to float required
		case "Dynamic":
			return register + "=GOint64.toUFloat(GOint64.ofUInt(Force.toInt(" + l.IndirectValue(v, errorInfo) + ")));"
		case "Float":
			if destType.Underlying().(*types.Basic).Kind() == types.Float32 {
				return register + "=Force.toFloat32(" +
					l.IndirectValue(v, errorInfo) + ");" // need to truncate to float32
			}
			return register + "=Force.toFloat(" + l.IndirectValue(v, errorInfo) + ");"
		default:
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - unhandled convert to float from: %s", srcTyp))
			return ""
		}
	case "UnsafePointer":
		//pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("converting a pointer to an Unsafe Pointer"))
		return register + "=" + l.IndirectValue(v, errorInfo) + ";" // ALL Pointers are unsafe ?
	default:
		if strings.HasPrefix(srcTyp, "Array<") {
			pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - No way to convert to %s from %s ", langType, srcTyp))
			return ""
		}
		pogo.LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unhandled convert to %s from %s ", langType, srcTyp))
		//return register + "=cast(" + l.IndirectValue(v, errorInfo) + "," + langType + ");"
		return ""
	}
}