func (l langType) LangName(p, o string) string { ovPkg, _, isOv := l.PackageOverloaded(p) if isOv { p = ovPkg } return pogo.MakeId(p) + "_" + pogo.MakeId(o) //+ "_" + makeHash(pogo.MakeId(o)) }
// 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) 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) FuncStart(packageName, objectName string, fn *ssa.Function, position string, isPublic bool, trackPhi bool, canOptMap map[string]bool) string { //fmt.Println("DEBUG: HAXE FuncStart: ", packageName, ".", objectName) nextReturnAddress = -1 hadReturn = false ret := "" // need to make private classes, aside from correctness, // because cpp & java have a problem with functions whose names are the same except for the case of the 1st letter if isPublic { ret += fmt.Sprintf(`#if js @:expose("Go_%s") #end `, l.LangName(packageName, objectName)) } else { ret += "#if (!php) private #end " // for some reason making classes private is a problem in php } ret += fmt.Sprintf("class Go_%s extends StackFrameBasis implements StackFrame { %s\n", l.LangName(packageName, objectName), l.Comment(position)) //Create the stack frame variables for p := range fn.Params { ret += "var " + "p_" + pogo.MakeId(fn.Params[p].Name()) + ":" + l.LangType(fn.Params[p].Type().Underlying(), false, fn.Params[p].Name()+position) + ";\n" } ret += "public function new(gr:Int," ret += "_bds:Array<Dynamic>" //bindings for p := range fn.Params { ret += ", " ret += "p_" + pogo.MakeId(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type().Underlying(), false, fn.Params[p].Name()+position) } ret += ") {\nsuper(gr," + fmt.Sprintf("%d", pogo.LatestValidPosHash) + ",\"Go_" + l.LangName(packageName, objectName) + "\");\nthis._bds=_bds;\n" for p := range fn.Params { ret += "this.p_" + pogo.MakeId(fn.Params[p].Name()) + "=p_" + pogo.MakeId(fn.Params[p].Name()) + ";\n" } ret += emitTrace(`New:` + l.LangName(packageName, objectName)) ret += "Scheduler.push(gr,this);\n}\n" rTyp := "" switch fn.Signature.Results().Len() { case 0: // NoOp case 1: rTyp = l.LangType(fn.Signature.Results().At(0).Type().Underlying(), false, position) default: rTyp = "{" for r := 0; r < fn.Signature.Results().Len(); r++ { if r != 0 { rTyp += ", " } rTyp += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type().Underlying(), false, position) } rTyp += "}" } if rTyp != "" { ret += "var _res:" + rTyp + ";\n" ret += "public inline function res():Dynamic " + "{return _res;}\n" } else { ret += "public inline function res():Dynamic {return null;}\n" // just to keep the interface definition happy } pseudoNextReturnAddress := -1 for b := range fn.Blocks { for i := range fn.Blocks[b].Instrs { in := fn.Blocks[b].Instrs[i] switch in.(type) { case *ssa.Call: switch in.(*ssa.Call).Call.Value.(type) { case *ssa.Builtin: //NoOp default: ret += fmt.Sprintf("var _SF%d:StackFrame;\n", -pseudoNextReturnAddress) //TODO set correct type, or let Haxe determine pseudoNextReturnAddress-- } case *ssa.Send, *ssa.RunDefers, *ssa.Panic: pseudoNextReturnAddress-- case *ssa.UnOp: if in.(*ssa.UnOp).Op == token.ARROW { pseudoNextReturnAddress-- } } reg := l.Value(in, pogo.CodePosition(in.Pos())) if reg != "" { // Underlying() not used in 2 lines below because of *ssa.(opaque type) typ := l.LangType(in.(ssa.Value).Type(), false, reg+"@"+position) init := l.LangType(in.(ssa.Value).Type(), true, reg+"@"+position) // this may be overkill... if strings.HasPrefix(init, "{") || strings.HasPrefix(init, "new Pointer") || strings.HasPrefix(init, "new UnsafePointer") || strings.HasPrefix(init, "new Array") || strings.HasPrefix(init, "new Slice") || strings.HasPrefix(init, "new Chan") || strings.HasPrefix(init, "new Map") || strings.HasPrefix(init, "new Complex") || strings.HasPrefix(init, "GOint64.make") { // stop unnecessary initialisation // all SSA registers are actually assigned to before use, so minimal initialisation is required, except for maps init = "null" } if typ != "" { switch len(*in.(ssa.Value).Referrers()) { case 0: // don't allocate unused temporary variables //case 1: // TODO optimization possible using register replacement but does not currenty work for: a,b=b,a+b, so code removed default: ret += haxeVar(reg, typ, init, position, "FuncStart()") + "\n" } } } } } //TODO optimise (again) for if only one block (as below) AND no calls (which create synthetic values for _Next) //if len(fn.Blocks) > 1 { // if there is only one block then we don't need to track which one is next if trackPhi { ret += "var _Phi:Int=0;\n" } ret += "var _Next:Int=0;\n" //} // call from haxe (TODO: maybe run in a new goroutine) ret += "public static inline function callFromHaxe( " for p := range fn.Params { if p != 0 { ret += ", " } ret += "p_" + pogo.MakeId(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type().Underlying(), false, fn.Params[p].Name()+position) } ret += ") : " switch fn.Signature.Results().Len() { case 0: ret += "Void" case 1: ret += l.LangType(fn.Signature.Results().At(0).Type().Underlying(), false, position) default: ret += "{" for r := 0; r < fn.Signature.Results().Len(); r++ { if r != 0 { ret += ", " } ret += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type().Underlying(), false, position) } ret += "}" } ret += " {\n" ret += "if(!Go.doneInit) Go.init();\n" // very defensive TODO remove this once everyone understands that Go.init() must be called first ret += "var _sf=new Go_" + l.LangName(packageName, objectName) ret += "(0,[]" // NOTE calls from Haxe hijack goroutine 0, so the main go goroutine will be suspended for the duration for p := range fn.Params { ret += ", " ret += "p_" + pogo.MakeId(fn.Params[p].Name()) } ret += ").run(); \nwhile(_sf._incomplete) Scheduler.runAll();\n" // TODO alter for multi-threading if ever implemented if fn.Signature.Results().Len() > 0 { ret += "return _sf.res();\n" } ret += "}\n" // call from haxe go runtime - use current goroutine ret += "public static inline function callFromRT( _gr" for p := range fn.Params { //if p != 0 { ret += ", " //} ret += "p_" + pogo.MakeId(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type().Underlying(), false, fn.Params[p].Name()+position) } ret += ") : " switch fn.Signature.Results().Len() { case 0: ret += "Void" case 1: ret += l.LangType(fn.Signature.Results().At(0).Type().Underlying(), false, position) default: ret += "{" for r := 0; r < fn.Signature.Results().Len(); r++ { if r != 0 { ret += ", " } ret += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type().Underlying(), false, position) } ret += "}" } ret += " {\n" /// we have already done Go.init() if we are calling from the runtime ret += "var _sf=new Go_" + l.LangName(packageName, objectName) ret += "(_gr,[]" // use the given Goroutine for p := range fn.Params { ret += ", " ret += "p_" + pogo.MakeId(fn.Params[p].Name()) } ret += ").run(); \nwhile(_sf._incomplete) Scheduler.run1(_gr);\n" // NOTE no "panic()" or "go" code in runtime Go if fn.Signature.Results().Len() > 0 { ret += "return _sf.res();\n" } ret += "}\n" // call ret += "public static inline function call( gr:Int," //this just creates the stack frame, NOTE does not run anything because also used for defer ret += "_bds:Array<Dynamic>" //bindings for p := range fn.Params { ret += ", " ret += "p_" + pogo.MakeId(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type().Underlying(), false, fn.Params[p].Name()+position) } ret += ") : Go_" + l.LangName(packageName, objectName) ret += "\n{" + "" ret += "return " ret += "new Go_" + l.LangName(packageName, objectName) + "(gr,_bds" for p := range fn.Params { ret += ", " ret += "p_" + pogo.MakeId(fn.Params[p].Name()) } ret += ");\n" ret += "}\n" ret += "public function run():Go_" + l.LangName(packageName, objectName) + " {\n" ret += emitTrace(`Run: ` + l.LangName(packageName, objectName)) //TODO optimise (again) for if only one block (as below) AND no calls (which create synthetic values for _Next) //if len(fn.Blocks) > 1 { // if there is only one block then we don't need to track which one is next ret += "while(true){\nswitch(_Next) {" //} return ret }