// 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) } }
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) 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 }