// 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 "" } }
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 } }
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] }
// 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) } }
// 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 + ";" }
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 "" } }
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 } }
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 + ")" } } } }
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 + " }" }
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 + ");" } }
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 }
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") }
/* 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 }
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 }
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 "" } }