// FuncPathName returns a unique function path and name. func (comp *Compilation) FuncPathName(fn *ssa.Function) (path, name string) { rx := fn.Signature.Recv() pf := tgoutil.MakeID(comp.rootProgram.Fset.Position(fn.Pos()).String()) //fmt.Sprintf("fn%d", fn.Pos()) if rx != nil { // it is not the name of a normal function, but that of a method, so append the method description pf = rx.Type().String() // NOTE no underlying() } else { if fn.Pkg != nil { pf = fn.Pkg.Object.Path() // was .Name(), but not unique } else { goroot := tgoutil.MakeID(LanguageList[comp.TargetLang].GOROOT + string(os.PathSeparator)) pf1 := strings.Split(pf, goroot) // make auto-generated names shorter if len(pf1) == 2 { pf = pf1[1] } // TODO use GOPATH for names not in std pkgs } } return pf, fn.Name() }
// Handle an individual instruction. func (comp *Compilation) emitInstruction(instruction interface{}, operands []*ssa.Value) (emitPhiFlag bool) { l := comp.TargetLang emitPhiFlag = true errorInfo := "" _, isDebug := instruction.(*ssa.DebugRef) if !isDebug { // Don't update the code position for debug refs prev := comp.LatestValidPosHash comp.MakePosHash(instruction.(ssa.Instruction).Pos()) // this so that we log the nearby position info if prev != comp.LatestValidPosHash { // new info, so put out an update if comp.DebugFlag { // but only in Debug mode fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].SetPosHash()) } } errorInfo = comp.CodePosition(instruction.(ssa.Instruction).Pos()) } if errorInfo == "" { errorInfo = comp.previousErrorInfo } else { comp.previousErrorInfo = "near " + errorInfo errorInfo = "@ " + errorInfo } errorInfo = reflect.TypeOf(instruction).String() + " " + errorInfo //TODO consider removing as for DEBUG only instrVal, hasVal := instruction.(ssa.Value) register := "" comment := "" if hasVal { register = comp.RegisterName(instrVal) comment = fmt.Sprintf("%s = %+v %s", register, instruction, errorInfo) //emitComment(comment) switch len(*instruction.(ssa.Value).Referrers()) { case 0: // no other instruction uses the result of this one comment += " [REGISTER VALUE UN-USED]" register = "" case 1: // only 1 other use of the register // TODO register optimisation currently disabled, consider reimplimentation user := (*instruction.(ssa.Value).Referrers())[0] if user.Block() == instruction.(ssa.Instruction).Block() { comment += " [REGISTER MAY BE OPTIMIZABLE]" } default: //multiple usage of the register } if len(register) > 0 { if LanguageList[comp.TargetLang].LangType(instruction.(ssa.Value).Type(), false, errorInfo) == "" { // NOTE an empty type def makes a register useless too register = "" } } } else { comment = fmt.Sprintf("%+v %s", instruction, errorInfo) //emitComment(comment) } switch instruction.(type) { case *ssa.Jump: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Jump(instruction.(*ssa.Jump).Block().Succs[0].Index, instruction.(*ssa.Jump).Block().Index, LanguageList[l].PhiCode(false, instruction.(*ssa.Jump).Block().Index, instruction.(*ssa.Jump).Block().Succs[0].Instrs, errorInfo))+ LanguageList[l].Comment(comment)) case *ssa.If: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].If(*operands[0], instruction.(*ssa.If).Block().Succs[0].Index, instruction.(*ssa.If).Block().Succs[1].Index, instruction.(*ssa.If).Block().Index, LanguageList[l].PhiCode(false, instruction.(*ssa.If).Block().Index, instruction.(*ssa.If).Block().Succs[0].Instrs, errorInfo), LanguageList[l].PhiCode(false, instruction.(*ssa.If).Block().Index, instruction.(*ssa.If).Block().Succs[1].Instrs, errorInfo), errorInfo)+LanguageList[l].Comment(comment)) case *ssa.Phi: text := "" if len(*instruction.(*ssa.Phi).Referrers()) > 0 { phiEntries := make([]int, len(operands)) valEntries := make([]interface{}, len(operands)) for o := range operands { phiEntries[o] = instruction.(*ssa.Phi).Block().Preds[o].Index valEntries[o] = *operands[o] } text = LanguageList[l].Phi(register, phiEntries, valEntries, LanguageList[l].LangType(instrVal.Type(), true, errorInfo), errorInfo) } fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment)) case *ssa.Call: if instruction.(*ssa.Call).Call.IsInvoke() { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Call).Parent()), false, false, comp.grMap[instruction.(*ssa.Call).Parent()], instruction.(*ssa.Call).Call, errorInfo)+LanguageList[l].Comment(comment)) } else { switch instruction.(*ssa.Call).Call.Value.(type) { case *ssa.Builtin: comp.emitCall(true, false, false, comp.grMap[instruction.(*ssa.Call).Parent()], register, instruction.(*ssa.Call).Call, errorInfo, comment) default: comp.emitCall(false, false, false, comp.grMap[instruction.(*ssa.Call).Parent()], register, instruction.(*ssa.Call).Call, errorInfo, comment) } } case *ssa.Go: if instruction.(*ssa.Go).Call.IsInvoke() { if comp.grMap[instruction.(*ssa.Go).Parent()] != true { panic("attempt to Go a method, from a function that does not use goroutines at " + errorInfo) } fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Go).Parent()), true, false, true, instruction.(*ssa.Go).Call, errorInfo)+ LanguageList[l].Comment(comment)) } else { switch instruction.(*ssa.Go).Call.Value.(type) { case *ssa.Builtin: // no builtin functions can be go'ed comp.LogError(errorInfo, "pogo", fmt.Errorf("builtin functions cannot be go'ed")) default: if comp.grMap[instruction.(*ssa.Go).Parent()] != true { panic("attempt to Go a function, from a function does not use goroutines at " + errorInfo) } comp.emitCall(false, true, false, true, register, instruction.(*ssa.Go).Call, errorInfo, comment) } } case *ssa.Defer: if instruction.(*ssa.Defer).Call.IsInvoke() { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Defer).Parent()), false, true, comp.grMap[instruction.(*ssa.Defer).Parent()], instruction.(*ssa.Defer).Call, errorInfo)+ LanguageList[l].Comment(comment)) } else { switch instruction.(*ssa.Defer).Call.Value.(type) { case *ssa.Builtin: // no builtin functions can be defer'ed - TODO: the spec does allow this in some circumstances switch instruction.(*ssa.Defer).Call.Value.(*ssa.Builtin).Name() { case "close": //LogError(errorInfo, "pogo", fmt.Errorf("builtin function close() cannot be defer'ed")) comp.emitCall(true, false, true, comp.grMap[instruction.(*ssa.Defer).Parent()], register, instruction.(*ssa.Defer).Call, errorInfo, comment) default: comp.LogError(errorInfo, "pogo", fmt.Errorf("builtin functions cannot be defer'ed")) } default: comp.emitCall(false, false, true, comp.grMap[instruction.(*ssa.Defer).Parent()], register, instruction.(*ssa.Defer).Call, errorInfo, comment) } } case *ssa.Return: emitPhiFlag = false r := LanguageList[l].Ret(operands, errorInfo) fmt.Fprintln(&LanguageList[l].buffer, r+LanguageList[l].Comment(comment)) case *ssa.Panic: emitPhiFlag = false fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Panic(*operands[0], errorInfo, comp.grMap[instruction.(*ssa.Panic).Parent()])+LanguageList[l].Comment(comment)) case *ssa.UnOp: if register == "" && instruction.(*ssa.UnOp).Op.String() != "<-" { comp.emitComment(comment) } else { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].UnOp(register, instrVal.Type(), instruction.(*ssa.UnOp).Op.String(), *operands[0], instruction.(*ssa.UnOp).CommaOk, errorInfo)+ LanguageList[l].Comment(comment)) } case *ssa.BinOp: if register == "" { comp.emitComment(comment) } else { op := instruction.(*ssa.BinOp).Op.String() fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].BinOp(register, instrVal.Type(), op, *operands[0], *operands[1], errorInfo)+ LanguageList[l].Comment(comment)) } case *ssa.Store: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Store(*operands[0], *operands[1], errorInfo)+LanguageList[l].Comment(comment)) case *ssa.Send: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Send(*operands[0], *operands[1], errorInfo)+LanguageList[l].Comment(comment)) case *ssa.Convert: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Convert(register, LanguageList[l].LangType(instrVal.Type(), false, errorInfo), instrVal.Type(), *operands[0], errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.ChangeType: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].ChangeType(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.MakeInterface: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].MakeInterface(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.ChangeInterface: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].ChangeInterface(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.TypeAssert: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].TypeAssert(register, instruction.(*ssa.TypeAssert).X, instruction.(*ssa.TypeAssert).AssertedType, instruction.(*ssa.TypeAssert).CommaOk, errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.RunDefers: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].RunDefers(comp.grMap[instruction.(*ssa.RunDefers).Parent()])+ LanguageList[l].Comment(comment)) case *ssa.Alloc: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Alloc(register, instruction.(*ssa.Alloc).Heap, instruction.(*ssa.Alloc).Type(), errorInfo)+ LanguageList[l].Comment(instruction.(*ssa.Alloc).Comment+" "+comment)) case *ssa.MakeClosure: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].MakeClosure(register, instruction, errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.MakeSlice: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].MakeSlice(register, instruction, errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.MakeChan: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].MakeChan(register, instruction, errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.MakeMap: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].MakeMap(register, instruction, errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.MapUpdate: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].MapUpdate(*operands[0], *operands[1], *operands[2], errorInfo)+LanguageList[l].Comment(comment)) case *ssa.Range: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Range(register, *operands[0], errorInfo)+LanguageList[l].Comment(comment)) case *ssa.Next: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Next(register, *operands[0], instruction.(*ssa.Next).IsString, errorInfo)+LanguageList[l].Comment(comment)) case *ssa.Lookup: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Lookup(register, *operands[0], *operands[1], instruction.(*ssa.Lookup).CommaOk, errorInfo)+ LanguageList[l].Comment(comment)) case *ssa.Extract: if register == "" { // rquired here because of a "feature" in the generated SSA form comp.emitComment(comment) } else { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Extract(register, *operands[0], instruction.(*ssa.Extract).Index, errorInfo)+ LanguageList[l].Comment(comment)) } case *ssa.Slice: // 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 if register == "" { comp.emitComment(comment) } else { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Slice(register, instruction.(*ssa.Slice).X, instruction.(*ssa.Slice).Low, instruction.(*ssa.Slice).High, errorInfo)+ LanguageList[l].Comment(comment)) } case *ssa.Index: if register == "" { comp.emitComment(comment) } else { doRangeCheck := true aLen := 0 switch instruction.(*ssa.Index).X.Type().(type) { case *types.Array: aLen = int(instruction.(*ssa.Index).X.Type().(*types.Array).Len()) case *types.Pointer: switch instruction.(*ssa.Index).X.Type().(*types.Pointer).Elem().(type) { case *types.Array: aLen = int(instruction.(*ssa.Index).X.Type().(*types.Pointer).Elem().(*types.Array).Len()) } } if aLen > 0 { _, indexIsConst := instruction.(*ssa.Index).Index.(*ssa.Const) if indexIsConst { // this error handling is defensive, as the Go SSA code catches this error index := instruction.(*ssa.Index).Index.(*ssa.Const).Int64() if (index < 0) || (index >= int64(aLen)) { comp.LogError(errorInfo, "pogo", fmt.Errorf("index [%d] out of range: 0 to %d", index, aLen-1)) } doRangeCheck = false } } if doRangeCheck { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].RangeCheck(instruction.(*ssa.Index).X, instruction.(*ssa.Index).Index, aLen, errorInfo)) } fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Index(register, *operands[0], *operands[1], errorInfo)+ LanguageList[l].Comment(comment)) } case *ssa.IndexAddr: if register == "" { comp.emitComment(comment) } else { doRangeCheck := true aLen := 0 switch instruction.(*ssa.IndexAddr).X.Type().(type) { case *types.Array: aLen = int(instruction.(*ssa.IndexAddr).X.Type().(*types.Array).Len()) case *types.Pointer: switch instruction.(*ssa.IndexAddr).X.Type().(*types.Pointer).Elem().(type) { case *types.Array: aLen = int(instruction.(*ssa.IndexAddr).X.Type().(*types.Pointer).Elem().(*types.Array).Len()) } } if aLen > 0 { _, indexIsConst := instruction.(*ssa.IndexAddr).Index.(*ssa.Const) if indexIsConst { index := instruction.(*ssa.IndexAddr).Index.(*ssa.Const).Int64() if (index < 0) || (index >= int64(aLen)) { comp.LogError(errorInfo, "pogo", fmt.Errorf("index [%d] out of range: 0 to %d", index, aLen-1)) } doRangeCheck = false } } if doRangeCheck { fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].RangeCheck(instruction.(*ssa.IndexAddr).X, instruction.(*ssa.IndexAddr).Index, aLen, errorInfo)+ LanguageList[l].Comment(comment+" [POINTER]")) } fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].IndexAddr(register, instruction, errorInfo), LanguageList[l].Comment(comment+" [POINTER]")) } case *ssa.FieldAddr: fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].FieldAddr(register, instruction, errorInfo), LanguageList[l].Comment(comment+" [POINTER]")) case *ssa.Field: if register == "" { comp.emitComment(comment) } else { st := instruction.(*ssa.Field).X.Type().Underlying().(*types.Struct) fName := tgoutil.MakeID(st.Field(instruction.(*ssa.Field).Field).Name()) l := comp.TargetLang fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Field(register, instruction.(*ssa.Field).X, instruction.(*ssa.Field).Field, fName, errorInfo, false)+ LanguageList[l].Comment(comment)) } case *ssa.DebugRef: // TODO the comment could include the actual Go code debugCode := "" ident, ok := instruction.(*ssa.DebugRef).Expr.(*ast.Ident) if ok { if ident.Obj != nil { if ident.Obj.Kind == ast.Var { //fmt.Printf("DEBUGref %s (%s) => %s %+v %+v %+v\n", instruction.(*ssa.DebugRef).X.Name(), // instruction.(*ssa.DebugRef).X.Type().String(), // ident.Name, ident.Obj.Decl, ident.Obj.Data, ident.Obj.Type) name := ident.Name glob, isGlob := instruction.(*ssa.DebugRef).X.(*ssa.Global) if isGlob { name = glob.Pkg.String()[len("package "):] + "." + name } debugCode = LanguageList[l].DebugRef(name, instruction.(*ssa.DebugRef).X, errorInfo) } } } fmt.Fprintln(&LanguageList[l].buffer, debugCode+LanguageList[l].Comment(comment)) case *ssa.Select: text := LanguageList[l].Select(true, register, instruction, false, errorInfo) fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment)) default: comp.emitComment(comment + " [NO CODE GENERATED]") comp.LogError(errorInfo, "pogo", fmt.Errorf("SSA instruction not implemented: %v", reflect.TypeOf(instruction))) } if false { //TODO add instruction detail DEBUG FLAG for o := range operands { // this loop for the creation of comments to show what is in the instructions val := *operands[o] vip := valIsPointer(val) if vip { vipOut := showIndirectValue(val) comp.emitComment(fmt.Sprintf("Op[%d].VIP: %+v", o, vipOut)) } else { var ic interface{} = *operands[o] constVal, isConst := ic.(*ssa.Const) if isConst { comp.emitComment(fmt.Sprintf("Op[%d]: Constant= %+v", o, constVal)) } else { comp.emitComment(fmt.Sprintf("Op[%d]: %v = %+v", o, (*operands[o]), val)) } } // l := TargetLang // fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Value(*operands[o], "TEST")) } } return // return value is named and set in the code above }
func (l langType) LangType(t types.Type, retInitVal bool, errorInfo string) string { if l.PogoComp().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: l.PogoComp().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 += tgoutil.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" } l.PogoComp().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 }
package haxe import ( "fmt" "io/ioutil" "os" "strconv" "strings" "golang.org/x/tools/go/exact" "golang.org/x/tools/go/ssa" "github.com/tardisgo/tardisgo/tgoutil" ) var pseudoFnPrefix = tgoutil.MakeID("github.com/tardisgo/tardisgo/haxe/hx_") 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 "SSource": fn := strings.Trim(args[0].(*ssa.Const).Value.String(), "\"") fn = l.hc.langEntry.TgtDir + string(os.PathSeparator) + fn + ".hx" code := strings.Trim(args[1].(*ssa.Const).Value.String(), "\"") code = strings.Replace(code, "\\n", "\n", -1) code = strings.Replace(code, "\\t", "\t", -1) code = strings.Replace(code, "\\\"", "\"", -1) //println("DEBUG fn: " + fn + "\nCode: " + code) err := ioutil.WriteFile(fn, []byte(code), 0666)