Example #1
0
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))
}
Example #2
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 #3
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 #4
0
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
}