github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/haxe/base.go (about) 1 // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors 2 // Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package haxe 6 7 import ( 8 "errors" 9 "fmt" 10 "go/token" 11 "go/types" 12 "reflect" 13 "sort" 14 "strings" 15 "unicode" 16 17 "golang.org/x/tools/go/ssa" 18 19 "github.com/tardisgo/tardisgo/tgossa" 20 "github.com/tardisgo/tardisgo/tgoutil" 21 ) 22 23 func (l langType) emitTrace(s string) string { 24 if l.PogoComp().TraceFlag { 25 return `trace(this._functionName,this._latestBlock,"TRACE ` + s + ` "` /* + ` "+Scheduler.stackDump()` */ + ");\n" 26 } 27 return "" 28 } 29 30 func (langType) LanguageName() string { return "haxe" } 31 func (langType) FileTypeSuffix() string { return ".hx" } 32 33 // make a comment 34 func (l langType) Comment(c string) string { 35 if c != "" && l.PogoComp().DebugFlag { // only comment if something to say and in debug mode 36 return " // " + c 37 } 38 return "" 39 } 40 41 const imports = `` // nothing currently 42 43 const tardisgoLicence = `// This code generated using the TARDIS Go tool, elements are 44 // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors 45 // Use of this source code is governed by an MIT-style 46 // license that can be found in the LICENSE file at https://github.com/tardisgo/tardisgo 47 ` 48 49 func (langType) FileStart(haxePackageName, headerText string) string { 50 if haxePackageName == "" { 51 haxePackageName = "tardis" 52 } 53 return "package " + haxePackageName + ";\n" + imports + headerText + tardisgoLicence 54 } 55 56 // TODO rename 57 func (l langType) FileEnd() string { 58 return l.haxeruntime() // this deals with the individual runtime class files 59 } 60 61 // RegisterName returns the name of an ssa.Value, a utility function in case it needs to be altered. 62 func (l langType) RegisterName(val ssa.Value) string { 63 //NOTE the SSA code says that name() should not be relied on, so this code may need to alter 64 65 if l.hc.useRegisterArray { // we must use a register array when there are too many registers declared at class level for C++/Java to handle 66 reg := val.Name() 67 if reg[0] != 't' { 68 panic("Register Name does not begin with t: " + reg) 69 } 70 return "_t[" + reg[1:] + "]" 71 } 72 return "_" + val.Name() 73 } 74 75 type regToFree struct { 76 reg, typ string 77 } 78 79 func recycle(list []regToFree) string { 80 ret := []string{} 81 for _, x := range list { 82 switch x.typ { 83 case "GOint64": 84 //ret += "#if !(cpp|cs|java) " + x.reg + "=null; #end\n" // TODO 85 default: 86 ret = append(ret, ""+x.reg+"=null; // "+x.typ+"") // this improves GC performance on all targets 87 } 88 } 89 ret = sort.StringSlice(ret) // make sure it is always done in the same order 90 return strings.Join(ret, "\n") + "\n" 91 } 92 93 func (l langType) FuncStart(packageName, objectName string, fn *ssa.Function, blks []*ssa.BasicBlock, position string, isPublic, trackPhi, usesGr bool, canOptMap map[string]bool, reconstruct []tgossa.BlockFormat) string { 94 95 //fmt.Println("DEBUG: HAXE FuncStart: ", packageName, ".", objectName, usesGr) 96 97 l.hc.nextReturnAddress = -1 98 l.hc.hadReturn = false 99 l.hc.hadBlockReturn = false 100 l.hc.pseudoBlockNext = -1 101 l.hc.currentfn = fn 102 l.hc.currentfnName = "Go_" + l.LangName(packageName, objectName) 103 l.hc.funcNamesUsed[l.hc.currentfnName] = true 104 l.hc.fnUsesGr = usesGr 105 l.hc.fnTracksPhi = trackPhi 106 l.hc.fnCanOptMap = canOptMap 107 nullOnExitList := []regToFree{} // names to set to null before we exit the function 108 l.reset1useMap() 109 110 if l.PogoComp().DebugFlag { 111 l.hc.reconstructInstrs = nil 112 } else { 113 l.hc.reconstructInstrs = reconstruct 114 } 115 l.hc.elseStack = []string{} 116 117 ret := "" 118 119 // need to make private classes, aside from correctness, 120 // because cpp & java have a problem with functions whose names are the same except for the case of the 1st letter 121 if isPublic { 122 ret += fmt.Sprintf(`#if js @:expose("Go_%s") #end `, l.LangName(packageName, objectName)) 123 } else { 124 // ret += "#if (!php) private #end " // for some reason making classes private is a problem in php 125 } 126 ret += fmt.Sprintf("class %s extends StackFrameBasis implements StackFrame { %s\n", 127 l.hc.currentfnName, l.Comment(position)) 128 129 //Create the stack frame variables 130 hadBlank := false 131 for p := range fn.Params { 132 prefix := "p_" 133 if hadBlank && fn.Params[p].Name() == "_" { 134 prefix += fmt.Sprintf("%d", p) 135 } 136 pnam := prefix + tgoutil.MakeID(fn.Params[p].Name()) 137 ptyp := l.LangType(fn.Params[p].Type() /*.Underlying()*/, false, fn.Params[p].Name()+position) 138 ret += "private var " + pnam + ":" + ptyp + ";\n" 139 switch ptyp { 140 case "Int", "Float", "Bool": // not objects 141 default: 142 nullOnExitList = append(nullOnExitList, regToFree{pnam, ptyp}) 143 } 144 if fn.Params[p].Name() == "_" { 145 hadBlank = true 146 } 147 } 148 ret += "public function new(gr:Int," 149 ret += "_bds:Array<Dynamic>" //bindings 150 for p := range fn.Params { 151 ret += ", " 152 pnam := "p_" + tgoutil.MakeID(fn.Params[p].Name()) 153 ptyp := l.LangType(fn.Params[p].Type() /*.Underlying()*/, false, fn.Params[p].Name()+position) 154 ret += pnam + " : " + ptyp 155 } 156 ret += ") {\nsuper(gr," + fmt.Sprintf("%d", l.PogoComp().LatestValidPosHash) + ",\"Go_" + l.LangName(packageName, objectName) + "\");\nthis._bds=_bds;\n" 157 hadBlank = false 158 for p := range fn.Params { 159 prefix := "this.p_" 160 if hadBlank && fn.Params[p].Name() == "_" { 161 prefix += fmt.Sprintf("%d", p) 162 } 163 ret += prefix + tgoutil.MakeID(fn.Params[p].Name()) + "=p_" + tgoutil.MakeID(fn.Params[p].Name()) + ";\n" 164 if l.PogoComp().DebugFlag { 165 ret += `this.setDebugVar("` + fn.Params[p].Name() + `",p_` + tgoutil.MakeID(fn.Params[p].Name()) + ");\n" 166 } 167 if fn.Params[p].Name() == "_" { 168 hadBlank = true 169 } 170 } 171 if fn.Recover != nil { 172 //for b := 0; b < len(blks); b++ { 173 // if fn.Recover.Index == blks[b].Index { 174 // ret += fmt.Sprintf("this._recoverNext=%d;\n", b) 175 // break 176 // } 177 //} 178 ret += fmt.Sprintf("this._recoverNext=%d;\n", fn.Recover.Index) 179 } 180 ret += l.emitTrace(`New:` + l.LangName(packageName, objectName)) 181 ret += "Scheduler.push(gr,this);\n}\n" 182 183 rTyp := "" 184 rInit := "" 185 switch fn.Signature.Results().Len() { 186 case 0: 187 // NoOp 188 case 1: 189 rTyp = l.LangType(fn.Signature.Results().At(0).Type() /*.Underlying()*/, false, position) 190 rInit = l.LangType(fn.Signature.Results().At(0).Type() /*.Underlying()*/, true, position) 191 default: 192 rTyp = "{" 193 rInit = "{" 194 for r := 0; r < fn.Signature.Results().Len(); r++ { 195 if r != 0 { 196 rTyp += ", " 197 rInit += ", " 198 } 199 rTyp += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type() /*.Underlying()*/, false, position) 200 rInit += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type() /*.Underlying()*/, true, position) 201 } 202 rTyp += "}" 203 rInit += "}" 204 } 205 if rTyp != "" { 206 ret += "private var _res:" + rTyp + "=" + rInit + ";\n" // code may not be generated if return val is default 207 ret += "public inline function res():Dynamic " + "{return _res;}\n" 208 } else { 209 ret += "public inline function res():Dynamic {return null;}\n" // just to keep the interface definition happy 210 } 211 212 // call from haxe (TODO: maybe run in a new goroutine) 213 ret += "public static function hx( " // used to call this function from Haxe 214 for p := range fn.Params { 215 if p != 0 { 216 ret += ", " 217 } 218 ret += "p_" + tgoutil.MakeID(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type() /*.Underlying()*/, false, fn.Params[p].Name()+position) 219 } 220 ret += ") : " 221 switch fn.Signature.Results().Len() { 222 case 0: 223 ret += "Void" 224 case 1: 225 ret += l.LangType(fn.Signature.Results().At(0).Type() /*.Underlying()*/, false, position) 226 default: 227 ret += "{" 228 for r := 0; r < fn.Signature.Results().Len(); r++ { 229 if r != 0 { 230 ret += ", " 231 } 232 ret += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type() /*.Underlying()*/, false, position) 233 } 234 ret += "}" 235 } 236 ret += " {\n" 237 ret += "if(!Go.doneInit) Go.init();\n" // very defensive TODO remove this once everyone understands that Go.init() must be called first 238 ret += "var _sf=new Go_" + l.LangName(packageName, objectName) 239 ret += "(0,null" // NOTE calls from Haxe hijack goroutine 0, so the main go goroutine will be suspended for the duration 240 for p := range fn.Params { 241 ret += ", " 242 if fn.Params[p].Type().Underlying().String() == "string" { 243 ret += "Force.fromHaxeString(" 244 } 245 ret += "p_" + tgoutil.MakeID(fn.Params[p].Name()) 246 if fn.Params[p].Type().Underlying().String() == "string" { 247 ret += ")" 248 } 249 } 250 ret += ").run(); \n" 251 if usesGr { 252 ret += "while(_sf._incomplete) Scheduler.runAll();\n" // TODO alter for multi-threading if ever implemented 253 } 254 if fn.Signature.Results().Len() > 0 { 255 if fn.Signature.Results().Len() == 1 { 256 if fn.Signature.Results().At(0).Type().Underlying().String() == "string" { 257 ret += "return Force.toHaxeString(cast(_sf.res(),String));\n" 258 } else { 259 ret += "return _sf.res();\n" 260 } 261 } else { 262 ret += "var _r = _sf.res();\n" 263 for rv := 0; rv < fn.Signature.Results().Len(); rv++ { 264 if fn.Signature.Results().At(rv).Type().Underlying().String() == "string" { 265 ret += fmt.Sprintf("_r.r%d = Force.toHaxeString(cast(_r.r%d,String));\n", rv, rv) 266 } 267 } 268 ret += "return _r;\n" 269 } 270 } 271 ret += "}\n" 272 273 // call from haxe go runtime - use current goroutine 274 ret += "public static function callFromRT( _gr:Int" 275 for p := range fn.Params { 276 //if p != 0 { 277 ret += ", " 278 //} 279 ret += "p_" + tgoutil.MakeID(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type() /*.Underlying()*/, false, fn.Params[p].Name()+position) 280 } 281 ret += ") : " 282 switch fn.Signature.Results().Len() { 283 case 0: 284 ret += "Void" 285 case 1: 286 ret += l.LangType(fn.Signature.Results().At(0).Type() /*.Underlying()*/, false, position) 287 default: 288 ret += "{" 289 for r := 0; r < fn.Signature.Results().Len(); r++ { 290 if r != 0 { 291 ret += ", " 292 } 293 ret += fmt.Sprintf("r%d:", r) + l.LangType(fn.Signature.Results().At(r).Type() /*.Underlying()*/, false, position) 294 } 295 ret += "}" 296 } 297 ret += " {\n" /// we have already done Go.init() if we are calling from the runtime 298 ret += "var _sf=new Go_" + l.LangName(packageName, objectName) 299 ret += "(_gr,null" // use the given Goroutine 300 for p := range fn.Params { 301 ret += ", " 302 ret += "p_" + tgoutil.MakeID(fn.Params[p].Name()) 303 } 304 ret += ").run(); \n" 305 if usesGr { 306 ret += "while(_sf._incomplete) Scheduler.run1(_gr);\n" // NOTE no "panic()" or "go" code in runtime Go 307 } 308 if fn.Signature.Results().Len() > 0 { 309 ret += "return _sf.res();\n" 310 } 311 ret += "}\n" 312 313 // call 314 ret += "public static function call( gr:Int," //this just creates the stack frame, NOTE does not run anything because also used for defer 315 ret += "_bds:Array<Dynamic>" //bindings 316 for p := range fn.Params { 317 ret += ", " 318 ret += "p_" + tgoutil.MakeID(fn.Params[p].Name()) + " : " + l.LangType(fn.Params[p].Type() /*.Underlying()*/, false, fn.Params[p].Name()+position) 319 } 320 ret += ") : Go_" + l.LangName(packageName, objectName) 321 ret += "\n{" + "" 322 ret += "return " 323 ret += "new Go_" + l.LangName(packageName, objectName) + "(gr,_bds" 324 for p := range fn.Params { 325 ret += ", " 326 ret += "p_" + tgoutil.MakeID(fn.Params[p].Name()) 327 } 328 ret += ");\n" 329 ret += "}\n" 330 331 if !usesGr { 332 if l.hc.reconstructInstrs != nil { 333 ret += l.runFunctionCode(packageName, objectName, "[ RECONSTRUCTED NON-GOROUTINE FUNCTION ]") 334 } else { 335 ret += l.runFunctionCode(packageName, objectName, "[ UN-RECONSTRUCTED NON-GOROUTINE FUNCTION ]") 336 } 337 } 338 339 /* 340 if reconstructInstrs != nil { 341 for k, v := range reconstructInstrs { 342 if v.IsWhileCandidate { 343 ret += fmt.Sprintf("#if jsX var _wh%d:Dynamic=null; #end\n", blks[k].Index) 344 } 345 } 346 } 347 */ 348 349 regCount := 0 350 regDefs := "" 351 l.hc.useRegisterArray = false 352 353 l.hc.pseudoNextReturnAddress = -1 354 for b := range blks { 355 for i := range blks[b].Instrs { 356 in := blks[b].Instrs[i] 357 if !l.CanInline(in) { 358 359 reg := l.Value(in, l.PogoComp().CodePosition(in.Pos())) 360 361 switch in.(type) { 362 case *ssa.Call: 363 switch in.(*ssa.Call).Call.Value.(type) { 364 case *ssa.Builtin: 365 //NoOp 366 default: 367 // Optimise here not to declare Stack Frames for pseudo-functions used when calling Haxe code direct 368 pp := l.getPackagePath(in.(*ssa.Call).Common()) 369 ppBits := strings.Split(pp, "/") 370 if ppBits[len(ppBits)-1] != "hx" && !strings.HasPrefix(ppBits[len(ppBits)-1], "_") { 371 //if usesGr { 372 // ret += "private " 373 //} 374 ret += fmt.Sprintf("var _SF%d:StackFrame", -l.hc.pseudoNextReturnAddress) //TODO set correct type, or let Haxe determine 375 nullOnExitList = append(nullOnExitList, regToFree{fmt.Sprintf("_SF%d", -l.hc.pseudoNextReturnAddress), "StackFrame"}) 376 if usesGr { 377 ret += " #if jsinit =null #end " // v8 opt 378 ret += ";\n" 379 } else { 380 //if reconstructInstrs == nil { 381 // ret += "=null;\n" // need to initalize when using the native stack for these vars 382 //} else { 383 ret += " #if jsinit =null #end " // v8 opt 384 ret += ";\n" 385 //} 386 } 387 } 388 l.hc.pseudoNextReturnAddress-- 389 } 390 case *ssa.Send, *ssa.Select, *ssa.RunDefers, *ssa.Panic: 391 l.hc.pseudoNextReturnAddress-- 392 case *ssa.UnOp: 393 if in.(*ssa.UnOp).Op == token.ARROW { 394 l.hc.pseudoNextReturnAddress-- 395 } 396 case *ssa.Alloc: 397 if !in.(*ssa.Alloc).Heap { // allocate space on the stack if possible 398 //fmt.Println("DEBUG allocate stack space for", reg, "at", position) 399 if reg != "" { 400 reg = strings.TrimSuffix(reg, "inline()") // if there is one 401 ret += l.haxeVar(reg+"_stackalloc", "Object", "="+allocNewObject(in.(*ssa.Alloc).Type()), position, "FuncStart()") + "\n" 402 } 403 } 404 } 405 406 if reg != "" && !canOptMap[reg[1:]] { // only add the reg to the SF if not defined in sub-functions 407 // Underlying() not used in 2 lines below because of *ssa.(opaque type) 408 typ := l.LangType(in.(ssa.Value).Type(), false, reg+"@"+position) 409 init := l.LangType(in.(ssa.Value).Type(), true, reg+"@"+position) // this may be overkill... 410 411 if strings.HasPrefix(init, "{") || strings.HasPrefix(init, "Pointer.make") || 412 strings.HasPrefix(init, "Object.make") || strings.HasPrefix(init, "new Slice") || 413 strings.HasPrefix(init, "new Chan") || strings.HasPrefix(init, "new GOmap") || 414 strings.HasPrefix(init, "new Complex") { // stop unnecessary initialisation 415 // all SSA registers are actually assigned to before use, so minimal initialisation is required, except for maps 416 init = "null" 417 } 418 if typ != "" { 419 switch len(*in.(ssa.Value).Referrers()) { 420 case 0: // don't allocate unused temporary variables 421 default: 422 if usesGr { 423 if init == "null" { 424 nullOnExitList = append(nullOnExitList, regToFree{reg, typ}) 425 } 426 init = " #if jsinit =" + init + " #end " // only init in JS, to tell the var type for v8 opt 427 } else { 428 if init == "null" { 429 nullOnExitList = append(nullOnExitList, regToFree{reg, typ}) 430 } 431 if init == "null" && l.hc.reconstructInstrs != nil { 432 init = " #if jsinit = null #end " 433 } else { 434 init = " = " + init + " " // when not using goroutines, sadly they all need initializing because the Haxe compiler objects 435 } 436 } 437 switch typ { 438 case "String", "GOint64": 439 nullOnExitList = append(nullOnExitList, regToFree{reg, typ}) 440 } 441 hv := l.haxeVar(reg, typ, init, position, "FuncStart()") + "\n" 442 regDefs += hv 443 regCount++ 444 } 445 } 446 } 447 } 448 } 449 } 450 451 if regCount > l.hc.langEntry.InstructionLimit { // should only affect very large init() fns 452 //fmt.Println("DEBUG regCount", currentfnName, regCount) 453 l.hc.useRegisterArray = true 454 ret += "var _t=new Array<Dynamic>();\n" 455 ret += "inline function nullOnExit(){_t=null;};\n" 456 } else { 457 l.hc.useRegisterArray = false 458 ret += regDefs 459 ret += "inline function nullOnExit(){\n" 460 ret += "#if nulltempvars\n" 461 ret += recycle(nullOnExitList) 462 ret += "#end\n" 463 ret += "nullOnExitSF();\n" 464 ret += "};\n" 465 } 466 //if trackPhi { 467 // ret += "var _Phi:Int=0;\n" 468 //} 469 470 if usesGr { 471 ret += l.runFunctionCode(packageName, objectName, "") 472 } 473 474 return ret 475 } 476 477 func (l langType) runFunctionCode(packageName, objectName, msg string) string { 478 ret := "public function run():Go_" + l.LangName(packageName, objectName) + " { //" + msg + "\n" 479 ret += l.emitTrace(`Run: ` + l.LangName(packageName, objectName) + " " + msg) 480 return ret 481 } 482 483 func (l langType) whileCaseCode() string { 484 // NOTE this rather odd arrangement improves JS V8 optimization 485 ret := "#if uselocalfunctions\n" 486 ret += "function " + l.hc.currentfnName + "_loop():" + l.hc.currentfnName + "{\n" 487 ret += "\tvar retVal:" + l.hc.currentfnName + "=null;\n" 488 489 ret += "\twhile(retVal==null) \n" 490 491 if l.hc.fnUsesGr { 492 ret += "\t\tswitch(_Next){\n" 493 for k, v := range l.hc.localFunctionMap { 494 ret += fmt.Sprintf("\t\t\tcase %d: retVal=%s();\n", k, v) 495 } 496 ret += "\t\t}\n" 497 } else { 498 ret += "\t\tretVal=fnMap.get(_Next)();\n" 499 ret += "\tfnMap=null;\n" // tidy up 500 } 501 ret += "\treturn retVal;\n" 502 ret += "}\n" 503 if !l.hc.fnUsesGr { 504 ret += "return " + l.hc.currentfnName + "_loop();\n" 505 } 506 ret += "#else\n" 507 ret += "\tdefault: Scheduler.bbi();\n}\n" 508 ret += ` 509 #if js 510 return null; }; // the end of a separate function to encourage JS V8 optimisation 511 while(sw()==null) {} // repeatedly call the local JS function 512 #end 513 ` 514 ret += "#end\n" 515 return ret 516 } 517 518 func (l langType) RunEnd(fn *ssa.Function) string { 519 // TODO reoptimize if blocks >0 and no calls that create synthetic block entries 520 ret := "" 521 if l.hc.reconstructInstrs == nil { 522 ret += l.emitUnseenPseudoBlocks() 523 ret += l.whileCaseCode() 524 if l.hc.fnUsesGr { 525 ret += "\n#if !uselocalfunctions return this; } #end\n" 526 } else { 527 ret += "\n#if !uselocalfunctions return this; #end\n}\n" 528 } 529 } else { 530 ret += "// Func code all emitted (handle extra reconstruction block for function)\n" 531 l.hc.thisBlock++ 532 ret += l.reconstructBlock() 533 534 //for b := thisBlock; b < len(reconstructInstrs); b++ { 535 // for i := 0; i < reconstructInstrs[b].EndBracketCount; i++ { 536 // ret += " } " 537 // } 538 //} 539 540 // TODO optimise to only emit this code if directly previous block does not have an explicit return 541 ret += `this._incomplete=false; 542 Scheduler.pop(this._goroutine); 543 nullOnExit(); 544 return this; 545 ` // for when the SSA code does not contain an explicit return; 546 547 ret += "}\n" // for the run function 548 } 549 return ret 550 } 551 func (l langType) FuncEnd(fn *ssa.Function) string { 552 // actually, the end of the class for that Go function 553 l.PogoComp().WriteAsClass(l.hc.currentfnName, "}\n") 554 return `` 555 } 556 557 // utiltiy to set-up a haxe variable 558 func (l langType) haxeVar(reg, typ, init, position, errorStart string) string { 559 if typ == "" { 560 l.PogoComp().LogError(position, "Haxe", fmt.Errorf(errorStart+" unhandled initialisation for empty type")) 561 return "" 562 } 563 ret := "var " + reg + ":" + typ 564 if init != "" { 565 ret += init 566 } 567 return ret + ";" 568 } 569 570 func (l langType) SetPosHash() string { 571 return "this.setPH(" + fmt.Sprintf("%d", l.PogoComp().LatestValidPosHash) + ");" 572 } 573 574 func (l langType) BlockStart(block []*ssa.BasicBlock, num int, emitPhi bool) string { 575 l.hc.rangeChecks = make(map[string]struct{}) 576 l.hc.thisBlock = num 577 l.hc.tempVarList = []regToFree{} 578 l.hc.hadBlockReturn = false 579 // TODO optimise is only 1 block AND no calls 580 // TODO if len(block) > 1 { // no need for a case statement if only one block 581 ret := "" 582 583 ret += fmt.Sprintf("// BlockID: %d Idom: %v Dominees: %v Successors: %v\n", 584 block[num].Index, block[num].Idom(), block[num].Dominees(), block[num].Succs) 585 586 if l.hc.reconstructInstrs == nil { // Normal unreconstructed code 587 588 if num == 0 { 589 l.hc.localFunctionMap = make(map[int]string) 590 ret += ` 591 #if !uselocalfunctions 592 #if js 593 var sw = function(){ switch(_Next){ // put in a separate function to encourage JS V8 optimisation 594 #else 595 while(true) switch(_Next){ // while(true) and similar formulas disable JS V8 optimisation 596 #end 597 #end 598 ` 599 ret += "#if uselocalfunctions " 600 if l.hc.fnUsesGr { 601 ret += "return " + l.hc.currentfnName + "_loop(); } " 602 } else { 603 ret += "var fnMap=new Map<Int,Void->" + l.hc.currentfnName + ">(); " 604 } 605 ret += "#end\n" 606 } 607 ret += fmt.Sprintf("#if !uselocalfunctions case %d: #end", block[num].Index) + l.Comment(block[num].Comment) + "\n" 608 if l.hc.fnUsesGr { 609 fn := fmt.Sprintf(l.hc.currentfnName+"_%d", block[num].Index) 610 l.hc.localFunctionMap[block[num].Index] = fn 611 ret += "#if uselocalfunctions function " + fn + "():" + l.hc.currentfnName + " { #end\n" 612 } else { 613 ret += fmt.Sprintf("#if uselocalfunctions fnMap.set(%d,function "+l.hc.currentfnName+"_%d():"+l.hc.currentfnName+" { #end\n", 614 block[num].Index, block[num].Index) 615 } 616 ret += l.emitTrace(fmt.Sprintf("Function: %s Block:%d", block[num].Parent(), block[num].Index)) 617 if l.PogoComp().DebugFlag { 618 ret += "this.setLatest(" + fmt.Sprintf("%d", l.PogoComp().LatestValidPosHash) + "," + fmt.Sprintf("%d", block[num].Index) + ");\n" 619 } 620 621 } else { // reconstruct 622 ret += l.reconstructBlock() 623 } 624 return ret 625 } 626 627 func (l langType) reconstructBlock() string { 628 ret := "" 629 for l.hc.reconstructInstrs[l.hc.thisBlock].Stack.Len() > 0 { 630 action, seq, idx, ok := l.hc.reconstructInstrs[l.hc.thisBlock].Stack.Pop() 631 if !ok { 632 msg := "haxe.reconstructBlock internal error blockStack is empty " 633 panic(msg) 634 } 635 switch action { 636 case tgossa.EndWhile: 637 ret += fmt.Sprintf(" break; } /* EndWhile for seq %d id %d */ \n", seq, idx) 638 case tgossa.NotElse: 639 ret += fmt.Sprintf(" } else { /* NotElse for seq %d id %d */ \n", seq, idx) 640 case tgossa.IsElse: 641 ret += fmt.Sprintf(" } else { /* for seq %d id %d */ \n", seq, idx) 642 case tgossa.EndElseBracket: 643 ret += fmt.Sprintf(" } /* EndElse for seq %d id %d */ \n", seq, idx) 644 } 645 switch action { 646 case tgossa.NotElse, tgossa.IsElse: 647 if len(l.hc.elseStack) == 0 { 648 msg := "haxe.reconstructBlock internal error elseStack is empty " 649 panic(msg) 650 //ret += " // DEBUG HELP! " + msg + "\n" 651 } else { 652 ret += l.hc.elseStack[len(l.hc.elseStack)-1] 653 l.hc.elseStack = l.hc.elseStack[0 : len(l.hc.elseStack)-1] // pop the stack 654 } 655 } 656 switch action { 657 case tgossa.NotElse: 658 ret += " /*end NotElse*/ } \n" 659 } 660 } 661 if l.hc.reconstructInstrs[l.hc.thisBlock].IsWhileCandidate { 662 ret += "\nwhile(true){\n" 663 } 664 return ret 665 } 666 667 func (l langType) BlockEnd(block []*ssa.BasicBlock, num int, emitPhi bool) string { 668 ret := "" 669 if l.hc.reconstructInstrs == nil { // Normal unreconstructed code 670 if !l.hc.hadBlockReturn { 671 ret += "#if uselocalfunctions return null; #end\n" 672 } 673 l.hc.hadBlockReturn = true 674 if l.hc.fnUsesGr { 675 ret += "#if uselocalfunctions } #end\n" 676 } else { 677 ret += "#if uselocalfunctions }); #end\n" 678 } 679 } else { // reconstruct 680 //for i := 0; i < reconstructInstrs[thisBlock].EndBracketCount; i++ { 681 // ret += " } /* EndBracket */" 682 //} 683 //if block[num].Succs[len(block[num].Succs)-1].Index != block[num+1].Index { 684 // ret += "continue;" 685 //} 686 } 687 return ret 688 } 689 690 func (l langType) Jump(block int, phi int, code string) string { 691 692 ret := l.nullTempVars() 693 694 if l.hc.reconstructInstrs == nil { // Normal unreconstructed code 695 // use tail-calls for backward jumps where we definately know the function name 696 return ret + code + fmt.Sprintf("_Next=%d;", block) + "\n#if uselocalfunctions return null; #end " 697 } 698 // reconstruct 699 ret += fmt.Sprintf("// Jump to ID %d\n", block) + code 700 for _, ri := range l.hc.reconstructInstrs { // TODO pull reconstruct lookup map through 701 if ri.Index == block { 702 if ri.Seq != l.hc.thisBlock+1 { 703 if ri.Seq < l.hc.thisBlock { 704 ret += "continue;\n" 705 } else { 706 //ret += "break;\n" 707 } 708 } 709 break 710 } 711 } 712 return ret 713 } 714 715 func (l langType) If(v interface{}, trueNext, falseNext, phi int, trueCode, falseCode, errorInfo string) string { 716 if l.hc.reconstructInstrs == nil { // Normal unreconstructed code 717 ret := "if(" + l.IndirectValue(v, errorInfo) + "){\n" 718 ret += l.Jump(trueNext, phi, trueCode) 719 ret += "\n}else{\n" 720 ret += l.Jump(falseNext, phi, falseCode) 721 return ret + "\n}\n" 722 } 723 // reconstruct 724 ret := "" 725 //if reconstructInstrs[thisBlock].IsWhile { 726 // ret += fmt.Sprintf( 727 // " #if jsX if(_wh%d==null) _wh%d = function():Dynamic { #end /*DEBUG-isWhile*/ while(", 728 // phi, phi) 729 //} else { 730 ret += "if(" 731 //} 732 if l.hc.reconstructInstrs[l.hc.thisBlock].ReversePolarity { 733 ret += "!(" + l.IndirectValue(v, errorInfo) + ")" 734 } else { 735 ret += l.IndirectValue(v, errorInfo) 736 } 737 ret += "){\n" 738 if l.hc.reconstructInstrs[l.hc.thisBlock].ReversePolarity { 739 ret += l.Jump(falseNext, phi, falseCode) 740 l.hc.elseStack = append(l.hc.elseStack, l.Jump(trueNext, phi, trueCode)) 741 } else { // as you would expect 742 ret += l.Jump(trueNext, phi, trueCode) 743 l.hc.elseStack = append(l.hc.elseStack, l.Jump(falseNext, phi, falseCode)) 744 } 745 return ret 746 } 747 748 func (l langType) Phi(register string, phiEntries []int, valEntries []interface{}, defaultValue, errorInfo string) string { 749 panic("haxe.Phi() should never be called") 750 /* 751 ret := register + "=(" 752 for e := range phiEntries { 753 val := l.IndirectValue(valEntries[e], errorInfo) 754 ret += fmt.Sprintf("(_Phi==%d)?%s:", phiEntries[e], val) 755 } 756 return ret + defaultValue + ");" 757 */ 758 } 759 760 func (l langType) LangName(p, o string) string { 761 return tgoutil.MakeID(p) + "_" + tgoutil.MakeID(o) 762 } 763 764 // Returns the textual version of Value, possibly emmitting an error 765 // can't merge with indirectValue, as this is used by emit-func-setup to get register names 766 func (l langType) Value(v interface{}, errorInfo string) string { 767 val, ok := v.(ssa.Value) 768 if !ok { 769 return "" // if it is not a value, an empty string will be returned 770 } 771 switch v.(type) { 772 case *ssa.Global: 773 return "Go." + l.LangName(v.(*ssa.Global).Pkg.Pkg.Path() /* was .Name()*/, v.(*ssa.Global).Name()) 774 case *ssa.Const: 775 ci := v.(*ssa.Const) 776 _, c := l.Const(*ci, errorInfo) 777 return c 778 case *ssa.Parameter: 779 return "p_" + tgoutil.MakeID(v.(*ssa.Parameter).Name()) 780 case *ssa.FreeVar: 781 for n := 0; n < len(l.hc.currentfn.FreeVars); n++ { 782 if l.hc.currentfn.FreeVars[n].Name() == v.(*ssa.FreeVar).Name() { 783 return fmt.Sprintf(`_bds[%d /*%s*/]`, n, v.(*ssa.FreeVar).Name()) 784 } 785 } 786 panic(fmt.Sprintf("unable to find FreeVar %s in function %s with freeVars %v", 787 v.(*ssa.FreeVar).Name(), l.hc.currentfn, l.hc.currentfn.FreeVars)) 788 case *ssa.Function: 789 pk, _ := l.PogoComp().FuncPathName(v.(*ssa.Function)) //fmt.Sprintf("fn%d", v.(*ssa.Function).Pos()) 790 if v.(*ssa.Function).Signature.Recv() != nil { // it's a method 791 pn := v.(*ssa.Function).Signature.Recv().Pkg().Path() // was .Name() 792 pk = pn + "." + v.(*ssa.Function).Signature.Recv().Name() 793 } else { 794 if v.(*ssa.Function).Pkg != nil { 795 if v.(*ssa.Function).Pkg.Pkg != nil { 796 pk = v.(*ssa.Function).Pkg.Pkg.Path() // was .Name() 797 } 798 } 799 } 800 if len(v.(*ssa.Function).Blocks) > 0 { //the function actually exists 801 return "new Closure(Go_" + l.LangName(pk, v.(*ssa.Function).Name()) + ".call,null)" //TODO will change for go instr 802 } 803 // function has no implementation 804 // TODO maybe put a list of over-loaded functions here and only error if not found 805 // NOTE the reflect package comes through this path TODO fix! 806 l.PogoComp().LogWarning(errorInfo, "Haxe", fmt.Errorf("haxe.Value(): *ssa.Function has no implementation: %s", v.(*ssa.Function).Name())) 807 return "new Closure(null,null)" // Should fail at runtime if it is used... 808 case *ssa.UnOp: 809 switch v.(*ssa.UnOp).Op { 810 case token.ARROW, token.MUL: 811 return l.PogoComp().RegisterName(val) 812 } 813 return l.inlineRegisterName(v.(*ssa.UnOp)) 814 case *ssa.BinOp: 815 return l.inlineRegisterName(v.(*ssa.BinOp)) 816 case *ssa.Convert: 817 return l.inlineRegisterName(v.(*ssa.Convert)) 818 default: 819 return l.PogoComp().RegisterName(val) 820 } 821 } 822 func (l langType) FieldAddr(register string, v interface{}, errorInfo string) string { 823 if register != "" { 824 ptr := l.IndirectValue(v.(*ssa.FieldAddr).X, errorInfo) 825 if l.PogoComp().DebugFlag { 826 ptr = "Pointer.check(" + ptr + ")" 827 } 828 fld := v.(*ssa.FieldAddr).X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(v.(*ssa.FieldAddr).Field) 829 off := fieldOffset(v.(*ssa.FieldAddr).X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct), v.(*ssa.FieldAddr).Field) 830 if off == 0 { 831 if l.is1usePtr(v) { 832 return l.set1usePtr(v.(ssa.Value), oneUsePtr{obj: ptr + ".obj", off: ptr + ".off"}) + 833 "// virtual oneUsePtr " + register + "=" + l.hc.map1usePtr[v.(ssa.Value)].obj + ":" + l.hc.map1usePtr[v.(ssa.Value)].off 834 } 835 return fmt.Sprintf(`%s=%s; // .fieldAddr( /*%d : %s */ %d )`, register, 836 ptr, v.(*ssa.FieldAddr).Field, fixKeyWds(fld.Name()), off) 837 } 838 if l.is1usePtr(v) { 839 return l.set1usePtr(v.(ssa.Value), oneUsePtr{obj: ptr + ".obj", off: fmt.Sprintf("%d", off) + "+" + ptr + ".off"}) + 840 "// virtual oneUsePtr " + register + "=" + l.hc.map1usePtr[v.(ssa.Value)].obj + ":" + l.hc.map1usePtr[v.(ssa.Value)].off 841 } 842 return l.deDupAssign(register, fmt.Sprintf(`%s.fieldAddr( /*%d : %s */ %d );`, 843 ptr, v.(*ssa.FieldAddr).Field, fixKeyWds(fld.Name()), off)) 844 } 845 return "" 846 } 847 848 func wrapForceToUInt(v string, k types.BasicKind) string { 849 switch k { 850 case types.Uintptr: 851 return "Force.toUint32(Force.toInt(" + v + "))" 852 case types.Int64, types.Uint64: 853 return "Force.toUint32(GOint64.toInt(" + v + "))" 854 case types.Float32, types.Float64, types.UntypedFloat: 855 return "Force.toUint32(" + v + "<=0?Math.ceil(" + v + "):Math.floor(" + v + "))" 856 } 857 return v 858 } 859 860 func (l langType) IndexAddr(register string, v interface{}, errorInfo string) string { 861 if register == "" { 862 return "" // we can't make an address if there is nowhere to put it... 863 } 864 idxString := wrapForceToUInt(l.IndirectValue(v.(*ssa.IndexAddr).Index, errorInfo), 865 v.(*ssa.IndexAddr).Index.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 866 switch v.(*ssa.IndexAddr).X.Type().Underlying().(type) { 867 case *types.Pointer: 868 ptr := l.IndirectValue(v.(*ssa.IndexAddr).X, errorInfo) 869 if l.PogoComp().DebugFlag { 870 ptr = "Pointer.check(" + ptr + ")" 871 } 872 ele := v.(*ssa.IndexAddr).X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Elem().Underlying() 873 if idxString == "0" { 874 if l.is1usePtr(v) { 875 return l.set1usePtr(v.(ssa.Value), oneUsePtr{obj: ptr + ".obj", off: ptr + ".off"}) + 876 "// virtual oneUsePtr " + register + "=" + l.hc.map1usePtr[v.(ssa.Value)].obj + ":" + l.hc.map1usePtr[v.(ssa.Value)].off 877 } 878 return fmt.Sprintf(`%s=%s; // .addr(0)`, register, ptr) 879 } 880 idxString += arrayOffsetCalc(ele) 881 if l.is1usePtr(v) { 882 return l.set1usePtr(v.(ssa.Value), oneUsePtr{obj: ptr + ".obj", off: "(" + idxString + ")+" + ptr + ".off"}) + 883 "// virtual oneUsePtr " + register + "=" + l.hc.map1usePtr[v.(ssa.Value)].obj + ":" + l.hc.map1usePtr[v.(ssa.Value)].off 884 } 885 return l.deDupAssign(register, fmt.Sprintf(`%s.addr(%s);`, ptr, idxString)) 886 case *types.Slice: 887 x := l.IndirectValue(v.(*ssa.IndexAddr).X, errorInfo) 888 if l.is1usePtr(v) { 889 return l.set1usePtr(v.(ssa.Value), oneUsePtr{obj: x + ".baseArray.obj", off: x + ".itemOff(" + idxString + ")+" + x + ".baseArray.off"}) + 890 "// virtual oneUsePtr " + register + "=" + l.hc.map1usePtr[v.(ssa.Value)].obj + ":" + l.hc.map1usePtr[v.(ssa.Value)].off 891 } 892 code := fmt.Sprintf(`%s.itemAddr(%s);`, x, idxString) 893 return l.deDupAssign(register, code) 894 default: 895 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.IndirectValue():IndexAddr unknown operand type")) 896 return "" 897 } 898 } 899 900 func (l langType) IndirectValue(v interface{}, errorInfo string) string { 901 return l.Value(v, errorInfo) 902 } 903 904 func (l langType) intTypeCoersion(t types.Type, v, errorInfo string) string { 905 switch t.Underlying().(type) { 906 case *types.Basic: 907 switch t.Underlying().(*types.Basic).Kind() { 908 case types.Int8: 909 return "Force.toInt8(" + v + ")" 910 case types.Int16: 911 return "Force.toInt16(" + v + ")" 912 case types.Int32, types.Int: // NOTE type int is always int32 913 return "Force.toInt32(" + v + ")" 914 case types.Int64: 915 return "Force.toInt64(" + v + ")" 916 case types.Uint8: 917 return "Force.toUint8(" + v + ")" 918 case types.Uint16: 919 return "Force.toUint16(" + v + ")" 920 case types.Uint32, types.Uint, types.Uintptr: // NOTE type uint is always uint32 921 return "Force.toUint32(" + v + ")" 922 case types.Uint64: 923 return "Force.toUint64(" + v + ")" 924 case types.UntypedInt, types.UntypedRune: 925 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.intTypeCoersion(): unhandled types.UntypedInt or types.UntypedRune")) 926 return "" 927 case types.Float32: 928 return "Force.toFloat32(" + v + ")" 929 case types.Float64, types.Bool: 930 return v 931 default: 932 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.intTypeCoersion():unhandled basic kind %v", 933 t.Underlying().(*types.Basic).Kind())) 934 return v 935 } 936 default: 937 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.intTypeCoersion():unhandled type %T", t.Underlying())) 938 return v 939 } 940 } 941 942 func (l langType) Store(v1, v2 interface{}, errorInfo string) string { 943 ptr := l.IndirectValue(v1, errorInfo) 944 if l.PogoComp().DebugFlag { 945 ptr = "Pointer.check(" + ptr + ")" 946 } 947 if l.is1usePtr(v1) { 948 oup, found := l.hc.map1usePtr[v1.(ssa.Value)] 949 if !found { 950 panic("haxe.Store can't find oneUsePtr " + v1.(ssa.Value).Name() + "=" + v1.(ssa.Value).String()) 951 } 952 return oup.obj + ".set" + loadStoreSuffix(v2.(ssa.Value).Type().Underlying(), true) + oup.off + "," + 953 l.IndirectValue(v2, errorInfo) + ");" + 954 " /* " + v2.(ssa.Value).Type().Underlying().String() + " */ " 955 } 956 return ptr + ".store" + loadStoreSuffix(v2.(ssa.Value).Type().Underlying(), true) + 957 l.IndirectValue(v2, errorInfo) + ");" + 958 " /* " + v2.(ssa.Value).Type().Underlying().String() + " */ " 959 } 960 961 func (l langType) Send(v1, v2 interface{}, errorInfo string) string { 962 ret := fmt.Sprintf("_Next=%d;\n", l.hc.nextReturnAddress) 963 ret += "return this;\n" 964 if l.hc.fnUsesGr { 965 ret += "#if uselocalfunctions } #end\n" 966 } else { 967 ret += "#if uselocalfunctions }); #end\n" 968 } 969 ret += l.emitUnseenPseudoBlocks() 970 ret += fmt.Sprintf("#if !uselocalfunctions case %d: #end\n", l.hc.nextReturnAddress) 971 if l.hc.fnUsesGr { 972 fn := fmt.Sprintf(l.hc.currentfnName+"__%d", -l.hc.nextReturnAddress) 973 l.hc.localFunctionMap[l.hc.nextReturnAddress] = fn 974 ret += "#if uselocalfunctions function " + fn + "():" + l.hc.currentfnName + " { #end\n" 975 } else { 976 ret += fmt.Sprintf("#if uselocalfunctions fnMap.set(%d,function "+l.hc.currentfnName+"__%d():"+l.hc.currentfnName+" { #end\n", 977 l.hc.nextReturnAddress, -l.hc.nextReturnAddress) 978 } 979 //ret += fmt.Sprintf("#if uselocalfunctions function _Block_%d(){ #end\n", -nextReturnAddress) 980 if l.PogoComp().DebugFlag { 981 ret += "this.setLatest(" + fmt.Sprintf("%d", l.PogoComp().LatestValidPosHash) + "," + fmt.Sprintf("%d", l.hc.nextReturnAddress) + ");\n" 982 } 983 ret += l.emitTrace(fmt.Sprintf("Block:%d", l.hc.nextReturnAddress)) 984 // TODO panic if the chanel is null 985 ret += "if(!Channel.hasSpace(" + l.IndirectValue(v1, errorInfo) + "))return this;\n" // go round the loop again and wait if not OK 986 ret += l.IndirectValue(v1, errorInfo) + ".send(" + l.IndirectValue(v2, errorInfo) + ");" 987 l.hc.nextReturnAddress-- // decrement to set new return address for next code generation 988 l.hc.hadBlockReturn = false 989 return ret 990 } 991 992 func (l langType) emitReturnHere() string { 993 ret := "" 994 ret += fmt.Sprintf("_Next=%d;\n", l.hc.nextReturnAddress) 995 ret += "return this;\n" 996 if l.hc.fnUsesGr { 997 ret += "#if uselocalfunctions } #end\n" 998 } else { 999 ret += "#if uselocalfunctions }); #end\n" 1000 } 1001 ret += l.emitUnseenPseudoBlocks() 1002 ret += fmt.Sprintf("#if !uselocalfunctions case %d: #end\n", l.hc.nextReturnAddress) 1003 if l.hc.fnUsesGr { 1004 fn := fmt.Sprintf(l.hc.currentfnName+"__%d", -l.hc.nextReturnAddress) 1005 l.hc.localFunctionMap[l.hc.nextReturnAddress] = fn 1006 ret += "#if uselocalfunctions function " + fn + "():" + l.hc.currentfnName + " { #end\n" 1007 } else { 1008 ret += fmt.Sprintf("#if uselocalfunctions fnMap.set(%d,function "+l.hc.currentfnName+"__%d():"+l.hc.currentfnName+" { #end\n", 1009 l.hc.nextReturnAddress, -l.hc.nextReturnAddress) 1010 } 1011 //ret += fmt.Sprintf("#if uselocalfunctions function _Block_%d(){ #end\n", -nextReturnAddress) 1012 if l.PogoComp().DebugFlag { 1013 ret += "this.setLatest(" + fmt.Sprintf("%d", l.PogoComp().LatestValidPosHash) + "," + fmt.Sprintf("%d", l.hc.nextReturnAddress) + ");\n" 1014 } 1015 ret += l.emitTrace(fmt.Sprintf("Block:%d", l.hc.nextReturnAddress)) 1016 l.hc.hadBlockReturn = false 1017 return ret 1018 } 1019 1020 func (l langType) emitUnseenPseudoBlocks() string { 1021 ret := "" 1022 if l.hc.nextReturnAddress == l.hc.pseudoBlockNext { 1023 l.hc.pseudoBlockNext = l.hc.nextReturnAddress - 1 1024 return ret 1025 } 1026 // we've missed some 1027 for l.hc.pseudoBlockNext > l.hc.nextReturnAddress { 1028 if l.hc.fnUsesGr { 1029 fn := fmt.Sprintf(l.hc.currentfnName+"__%d", -l.hc.pseudoBlockNext) 1030 l.hc.localFunctionMap[l.hc.pseudoBlockNext] = fn 1031 ret += "#if uselocalfunctions function " + fn + "():" + l.hc.currentfnName + " {return null;} #end\n" 1032 } else { 1033 ret += fmt.Sprintf("#if uselocalfunctions fnMap.set(%d,function "+l.hc.currentfnName+"_dummy_%d():"+l.hc.currentfnName+" {return null;}); #end\n", 1034 l.hc.pseudoBlockNext, -l.hc.pseudoBlockNext) 1035 } 1036 //ret += fmt.Sprintf("#if uselocalfunctions function _Block_%d():Dynamic{return null;} #end\n", -pseudoBlockNext) 1037 l.hc.pseudoBlockNext-- 1038 } 1039 l.hc.pseudoBlockNext = l.hc.nextReturnAddress - 1 1040 return ret 1041 } 1042 1043 // if isSelect is false, v is the UnOp value, otherwise the ssa.Select instruction 1044 /* SSA DOCUMENTAION EXTRACT 1045 The Select instruction tests whether (or blocks until) one or more of the specified sent or received states is entered. 1046 1047 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. 1048 Select returns an n+2-tuple 1049 1050 (index int, recvOk bool, r_0 T_0, ... r_n-1 T_n-1) 1051 The tuple's components, described below, must be accessed via the Extract instruction. 1052 1053 If Blocking, select waits until exactly one state holds, i.e. a channel becomes ready for the designated operation 1054 of sending or receiving; select chooses one among the ready states pseudorandomly, performs the send or receive operation, 1055 and sets 'index' to the index of the chosen channel. 1056 1057 If !Blocking, select doesn't block if no states hold; instead it returns immediately with index equal to -1. 1058 1059 If the chosen channel was used for a receive, the r_i component is set to the received value, 1060 where i is the index of that state among all n receive states; otherwise r_i has the zero value of type T_i. 1061 Note that the the receive index i is not the same as the state index index. 1062 1063 The second component of the triple, recvOk, is a boolean whose value is true iff 1064 the selected operation was a receive and the receive successfully yielded a value. 1065 */ 1066 func (l langType) Select(isSelect bool, register string, v interface{}, CommaOK bool, errorInfo string) string { 1067 ret := l.emitReturnHere() // even if we are in a non-blocking select, we need to give the other goroutines a chance! 1068 if isSelect { 1069 sel := v.(*ssa.Select) 1070 if register == "" { 1071 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("select statement has no register")) 1072 return "" 1073 } 1074 ret += register + "=" + l.LangType(v.(ssa.Value).Type(), true, errorInfo) + ";\n" //initialize 1075 ret += register + ".r0= -1;\n" // the returned index if nothing is found 1076 1077 if len(sel.States) > 0 { // only do the logic if there are states to choose between 1078 // TODO a blocking select with no states could be further optimised to stop the goroutine 1079 1080 // Spec requires a pseudo-random order to which item is processed 1081 ret += fmt.Sprintf("{ var _states:Array<Bool> = new Array(); var _rnd=Std.random(%d);\n", len(sel.States)) 1082 for s := range sel.States { 1083 switch sel.States[s].Dir { 1084 case types.SendOnly: 1085 ch := l.IndirectValue(sel.States[s].Chan, errorInfo) 1086 ret += fmt.Sprintf("_states[%d]=Channel.hasSpace(%s);\n", s, ch) 1087 case types.RecvOnly: 1088 ch := l.IndirectValue(sel.States[s].Chan, errorInfo) 1089 ret += fmt.Sprintf("_states[%d]=Channel.hasContents(%s);\n", s, ch) 1090 default: 1091 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("select statement has invalid ChanDir")) 1092 return "" 1093 } 1094 } 1095 ret += fmt.Sprintf("for(_s in 0...%d) {var _i=(_s+_rnd)%s%d; if(_states[_i]) {%s.r0=_i; break;};}\n", 1096 len(sel.States), "%", len(sel.States), register) 1097 ret += fmt.Sprintf("switch(%s.r0){", register) 1098 rxIdx := 0 1099 for s := range sel.States { 1100 ret += fmt.Sprintf("case %d:\n", s) 1101 switch sel.States[s].Dir { 1102 case types.SendOnly: 1103 ch := l.IndirectValue(sel.States[s].Chan, errorInfo) 1104 snd := l.IndirectValue(sel.States[s].Send, errorInfo) 1105 ret += fmt.Sprintf("%s.send(%s);\n", ch, snd) 1106 case types.RecvOnly: 1107 ch := l.IndirectValue(sel.States[s].Chan, errorInfo) 1108 ret += fmt.Sprintf("{ var _v=%s.receive(%s); ", ch, 1109 l.LangType(sel.States[s].Chan.(ssa.Value).Type().Underlying().(*types.Chan).Elem().Underlying(), true, errorInfo)) 1110 ret += fmt.Sprintf("%s.r%d= _v.r0; ", register, 2+rxIdx) 1111 rxIdx++ 1112 ret += register + ".r1= _v.r1; }\n" 1113 default: 1114 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("select statement has invalid ChanDir")) 1115 return "" 1116 } 1117 } 1118 ret += "};}\n" // end switch; _states, _rnd scope 1119 1120 } // end only if len(sel.States)>0 1121 1122 if sel.Blocking { 1123 ret += "if(" + register + ".r0 == -1) return this;\n" 1124 } 1125 1126 } else { 1127 ret += "if(Channel.hasNoContents(" + l.IndirectValue(v, errorInfo) + "))return this;\n" // go round the loop again and wait if not OK 1128 if register != "" { 1129 ret += register + "=" 1130 } 1131 ret += l.IndirectValue(v, errorInfo) + ".receive(" 1132 ret += l.LangType(v.(ssa.Value).Type().Underlying().(*types.Chan).Elem().Underlying(), true, errorInfo) + ")" // put correct result into register 1133 if !CommaOK { 1134 ret += ".r0" 1135 } 1136 ret += ";" 1137 } 1138 l.hc.nextReturnAddress-- // decrement to set new return address for next code generation 1139 return ret 1140 } 1141 func (l langType) RegEq(r string) string { 1142 return r + "=" 1143 } 1144 1145 func (l langType) Ret(values []*ssa.Value, errorInfo string) string { 1146 l.hc.hadReturn = true 1147 _BlockEnd := "this._incomplete=false;\nScheduler.pop(this._goroutine);\n" 1148 l.hc.hadBlockReturn = true 1149 //_BlockEnd += nullTempVars() 1150 _BlockEnd += "nullOnExit();\nreturn this;\n" 1151 switch len(values) { 1152 case 0: 1153 return l.emitTrace("Ret0") + _BlockEnd 1154 case 1: 1155 return l.emitTrace("Ret1") + "_res= " + l.IndirectValue(*values[0], errorInfo) + ";\n" + _BlockEnd 1156 default: 1157 ret := l.emitTrace("RetN") + "_res= {" 1158 for r := range values { 1159 if r != 0 { 1160 ret += "," 1161 } 1162 if l.LangType((*values[r]).Type().Underlying(), false, errorInfo) == "GOint64" { 1163 ret += fmt.Sprintf("r%d:", r) + l.IndirectValue(*values[r], errorInfo) 1164 } else { 1165 ret += fmt.Sprintf("r%d:", r) + l.IndirectValue(*values[r], errorInfo) 1166 } 1167 } 1168 return ret + "};\n" + _BlockEnd 1169 } 1170 } 1171 1172 func (l langType) Panic(v1 interface{}, errorInfo string, usesGr bool) string { 1173 ret := l.doCall("", nil, "Scheduler.panic(this._goroutine,"+l.IndirectValue(v1, errorInfo)+");\n", usesGr) 1174 ret += l.Ret(nil, errorInfo) // just in case we return to this point without _recoverNext being set & used 1175 return ret 1176 } 1177 1178 func (l langType) getPackagePath(cc *ssa.CallCommon) string { 1179 // This code to find the package name 1180 pn := "UNKNOWN" // package name 1181 if cc.StaticCallee() != nil { 1182 pn, _ = l.PogoComp().FuncPathName(cc.StaticCallee()) // was =fmt.Sprintf("fn%d", cc.StaticCallee().Pos()) 1183 } 1184 if cc != nil { 1185 if cc.Method != nil { 1186 if cc.Method.Pkg() != nil { 1187 pn = cc.Method.Pkg().Path() // was .Name() 1188 } 1189 } else { 1190 if cc.StaticCallee() != nil { 1191 if cc.StaticCallee().Package() != nil { 1192 pn = cc.StaticCallee().Package().String() 1193 } else { 1194 if cc.StaticCallee().Object() != nil { 1195 if cc.StaticCallee().Object().Pkg() != nil { 1196 pn = cc.StaticCallee().Object().Pkg().Path() // was .Name() 1197 } 1198 } 1199 } 1200 } 1201 } 1202 } 1203 return pn 1204 } 1205 1206 func (l langType) Call(register string, cc ssa.CallCommon, args []ssa.Value, isBuiltin, isGo, isDefer, usesGr bool, fnToCall, errorInfo string) string { 1207 isHaxeAPI := false 1208 hashIf := "" // #if - only if required 1209 hashEnd := "" // #end - ditto 1210 ret := "" 1211 1212 //special case of: defer close(x) 1213 if isDefer && isBuiltin && fnToCall == "close" { 1214 fnToCall = "(new Closure(Go_haxegoruntime_defer_close.call,null))" 1215 isBuiltin = false 1216 } 1217 1218 if isBuiltin { 1219 if register != "" { 1220 register += "=" 1221 } 1222 switch fnToCall { // TODO handle other built-in functions? 1223 case "len", "cap": 1224 switch args[0].Type().Underlying().(type) { 1225 case *types.Chan, *types.Slice: 1226 if fnToCall == "len" { 1227 return register + "({var _v=" + l.IndirectValue(args[0], errorInfo) + ";_v==null?0:(_v.len());});" 1228 } 1229 // cap 1230 return register + "({var _v=" + l.IndirectValue(args[0], errorInfo) + ";_v==null?0:(_v.cap());});" 1231 case *types.Array: // assume len (same as cap anyway) 1232 return register + l.IndirectValue(args[0], errorInfo /*, false*/) + ".length;" 1233 case *types.Map: // assume len(map) 1234 return register + "({var _v=" + l.IndirectValue(args[0], errorInfo) + ";_v==null?0:_v.len();});" 1235 case *types.Basic: // assume string as anything else would have produced an error previously 1236 return register + "Force.toUTF8length(this._goroutine," + l.IndirectValue(args[0], errorInfo /*, false*/) + ");" 1237 default: // TODO handle other types? 1238 // TODO error on string? 1239 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Call() - unhandled len/cap type: %s", 1240 reflect.TypeOf(args[0].Type().Underlying()))) 1241 return register + `null;` 1242 } 1243 case "print", "println": 1244 ret += "Console." + fnToCall + "([" 1245 /* DEBUG if we want to know where all the prints happen 1246 ret += fmt.Sprintf("Go.CPos(%d)", l.PogoComp().LatestValidPosHash) 1247 if len(args) > 0 { // if there are more arguments to pass, add a comma 1248 ret += "," 1249 } 1250 */ 1251 case "delete": 1252 return register + l.IndirectValue(args[0], errorInfo) + ".remove(" + 1253 l.serializeKey(l.IndirectValue(args[1], errorInfo), 1254 l.LangType(args[1].Type().Underlying(), false, errorInfo)) + ");" 1255 case "append": 1256 return register + l.append(args, errorInfo) + ";" 1257 case "copy": //TODO rework & test 1258 return l.copy(register, args, errorInfo) + ";" 1259 case "close": 1260 return register + "" + l.IndirectValue(args[0], errorInfo) + ".close();" 1261 case "recover": 1262 return register + "" + "Scheduler.recover(this._goroutine);" 1263 case "real": 1264 return register + "" + l.IndirectValue(args[0], errorInfo) + ".real;" 1265 case "imag": 1266 return register + "" + l.IndirectValue(args[0], errorInfo) + ".imag;" 1267 case "complex": 1268 return register + "new Complex(" + l.IndirectValue(args[0], errorInfo) + "," + l.IndirectValue(args[1], errorInfo) + ");" 1269 case "ssa:wrapnilchk": 1270 return register + "Scheduler.wrapnilchk(" + l.IndirectValue(args[0], errorInfo) + ");" 1271 default: 1272 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Call() - Unhandled builtin function: %s", fnToCall)) 1273 ret = "MISSING_BUILTIN(" 1274 } 1275 } else { 1276 switch fnToCall { 1277 1278 // 1279 // Go library complex function rewriting 1280 // 1281 case "runtime_BBreakpoint": 1282 l.hc.nextReturnAddress-- //decrement to set new return address for next call generation 1283 return "this.breakpoint();" 1284 case "runtime_UUnzipTTestFFSS": 1285 l.hc.nextReturnAddress-- //decrement to set new return address for next call generation 1286 if l.hc.langEntry.TestFS != "" { 1287 return `Go_syscall_UUnzipFFSS.callFromRT(0,"` + l.hc.langEntry.TestFS + `");` 1288 } 1289 return "" 1290 //case "math_Inf": 1291 // nextReturnAddress-- //decrement to set new return address for next call generation 1292 // return register + "=(" + l.IndirectValue(args[0], errorInfo) + ">=0?Math.POSITIVE_INFINITY:Math.NEGATIVE_INFINITY);" 1293 1294 default: 1295 // 1296 // haxe interface pseudo-function re-writing 1297 // 1298 if strings.HasPrefix(fnToCall, pseudoFnPrefix) { 1299 l.hc.nextReturnAddress-- //decrement to set new return address for next call generation 1300 if register != "" { 1301 register += "=" 1302 } 1303 return register + l.hxPseudoFuncs(fnToCall, args, errorInfo) 1304 } 1305 1306 pn := l.getPackagePath(&cc) 1307 pnSplit := strings.Split(pn, "/") 1308 pn = pnSplit[len(pnSplit)-1] 1309 //fmt.Println("DEBUG package name", pn) 1310 1311 targetFunc := "Go_" + fnToCall + ".call" 1312 1313 if strings.HasPrefix(pn, "_") && // in a package that starts with "_" 1314 !strings.HasPrefix(fnToCall, "_t") { // and not a temp var TODO this may not always be accurate 1315 //fmt.Println("start _HAXELIB SPECIAL PROCESSING", pn, fnToCall) 1316 1317 // remove double uppercase characters in name 1318 ftc := "" 1319 skip := false 1320 for _, c := range fnToCall { 1321 if skip { 1322 skip = false 1323 } else { 1324 ftc += string(c) 1325 if unicode.IsUpper(c) { 1326 skip = true 1327 } 1328 } 1329 } 1330 fnToCall = ftc // fnToCall does not now contain doubled uppercase chars 1331 1332 l.hc.nextReturnAddress-- // decrement to set new return address for next call generation 1333 isBuiltin = true // pretend we are in a builtin function to avoid passing 1st param as bindings 1334 isHaxeAPI = true // we are calling a Haxe native function 1335 bits := strings.Split(fnToCall, "_47_") // split the parts of the string separated by / 1336 endbit := bits[len(bits)-1] 1337 foundDot := false 1338 if strings.Contains(endbit, "_dot_") { // it's a dot 1339 ss := strings.Split(endbit, "_dot_") 1340 endbit = "_ignore_" + ss[len(ss)-1] 1341 foundDot = true 1342 } 1343 bits = strings.Split(endbit, "_") // split RHS after / into parts separated by _ 1344 bits = bits[2:] // discard the leading _ and package name 1345 switch bits[0][0:1] { // the letter that gives the Haxe language in which to use the api 1346 case "X": // cross platform, so noOp 1347 case "P": 1348 hashIf = " #if cpp " 1349 hashEnd = " #end " 1350 case "C": 1351 hashIf = " #if cs " 1352 hashEnd = " #end " 1353 case "F": 1354 hashIf = " #if flash " 1355 hashEnd = " #end " 1356 case "J": 1357 hashIf = " #if java " 1358 hashEnd = " #end " 1359 case "S": 1360 hashIf = " #if js " 1361 hashEnd = " #end " 1362 case "N": 1363 hashIf = " #if neko " 1364 hashEnd = " #end " 1365 case "H": 1366 hashIf = " #if php " 1367 hashEnd = " #end " 1368 case "i": 1369 if bits[0] == "init" { 1370 return "" // no calls to _haxelib init functions 1371 } 1372 fallthrough 1373 default: 1374 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("call to function %s unknown Haxe API first letter %v of %v", 1375 fnToCall, bits[0][0:1], bits)) 1376 } 1377 bits[0] = bits[0][1:] // discard the magic letter from the front of the function name 1378 1379 if foundDot { // it's a Haxe method 1380 switch bits[len(bits)-1] { 1381 case "g": // get 1382 if register != "" { 1383 ret := l.IndirectValue(args[0], errorInfo) + "." + bits[len(bits)-2][1:] 1384 r := cc.Signature().Results() 1385 if r.Len() == 1 { 1386 switch r.At(0).Type().Underlying().(type) { 1387 case *types.Interface: 1388 ret = "Interface.fromDynamic(" + ret + ")" 1389 case *types.Basic: 1390 if r.At(0).Type().Underlying().(*types.Basic).Kind() == types.String { 1391 ret = "Force.fromHaxeString(" + ret + ")" 1392 } 1393 } 1394 } 1395 return hashIf + register + "=" + ret + ";" + hashEnd 1396 } 1397 return "" 1398 case "s": // set 1399 interfaceSuffix := "" 1400 interfacePrefix := "" 1401 switch args[1].Type().Underlying().(type) { 1402 case *types.Basic: 1403 if args[1].Type().Underlying().(*types.Basic).Kind() == types.String { 1404 interfacePrefix = "Force.toHaxeString(" 1405 interfaceSuffix = ")" 1406 } 1407 case *types.Interface: 1408 interfacePrefix = "Force.toHaxeParam(" 1409 interfaceSuffix = ")" 1410 } 1411 return hashIf + "" + l.IndirectValue(args[0], errorInfo) + 1412 "." + bits[len(bits)-2][1:] + 1413 "=" + interfacePrefix + l.IndirectValue(args[1], errorInfo) + interfaceSuffix + ";" + hashEnd 1414 default: 1415 bits = bits[:len(bits)-1] // trim off the "_digit" suffix 1416 targetFunc = l.IndirectValue(args[0], errorInfo) + "." + bits[len(bits)-1][1:] //remove leading capital letter 1417 1418 args = args[1:] 1419 } 1420 } else { 1421 switch bits[len(bits)-1] { 1422 case "g": // special processing to get a class static variable or enum 1423 if register != "" { 1424 ret := strings.Join(strings.Split(strings.Join(bits[:len(bits)-1], "."), "..."), "_") 1425 r := cc.Signature().Results() 1426 if r.Len() == 1 { 1427 switch r.At(0).Type().Underlying().(type) { 1428 case *types.Interface: 1429 ret = "Interface.fromDynamic(" + ret + ")" 1430 case *types.Basic: 1431 if r.At(0).Type().Underlying().(*types.Basic).Kind() == types.String { 1432 ret = "Force.fromHaxeString(" + ret + ")" 1433 } 1434 } 1435 } 1436 return hashIf + register + "=" + ret + ";" + hashEnd 1437 } 1438 return "" 1439 case "s": // special processing to set a class static variable 1440 interfaceSuffix := "" 1441 interfacePrefix := "" 1442 switch args[0].Type().Underlying().(type) { 1443 case *types.Basic: 1444 if args[0].Type().Underlying().(*types.Basic).Kind() == types.String { 1445 interfacePrefix = "Force.toHaxeString(" 1446 interfaceSuffix = ")" 1447 } 1448 case *types.Interface: 1449 interfacePrefix = "Force.toHaxeParam(" 1450 interfaceSuffix = ")" 1451 } 1452 return hashIf + strings.Join(strings.Split(strings.Join(bits[:len(bits)-1], "."), "..."), "_") + 1453 "=" + interfacePrefix + l.IndirectValue(args[0], errorInfo) + interfaceSuffix + ";" + hashEnd 1454 default: 1455 bits = bits[:len(bits)-1] // trim off the "_digit" suffix 1456 if bits[len(bits)-1] == "new" { 1457 targetFunc = "new " + strings.Join(bits[:len(bits)-1], ".") // put it back into the Haxe format for names 1458 } else { 1459 targetFunc = strings.Join(bits, ".") // put it back into the Haxe format for names 1460 } 1461 } 1462 } 1463 targetFunc = strings.Join(strings.Split(targetFunc, "..."), "_") 1464 // end _HAXELIB SPECIAL PROCESSING 1465 } else { 1466 olv, ok := fnToVarOverloadMap[fnToCall] 1467 if ok { // replace the function call with a variable 1468 l.hc.nextReturnAddress-- //decrement to set new return address for next call generation 1469 if register == "" { 1470 return "" 1471 } 1472 return register + "=" + olv + ";" 1473 } 1474 olf, ok := fnOverloadMap[fnToCall] 1475 if ok { // replace one go function with another 1476 targetFunc = olf 1477 } else { 1478 olf, ok := builtinOverloadMap[fnToCall] 1479 if ok { // replace a go function with a haxe one 1480 targetFunc = olf 1481 l.hc.nextReturnAddress-- //decrement to set new return address for next call generation 1482 isBuiltin = true // pretend we are in a builtin function to avoid passing 1st param as bindings or waiting for completion 1483 } else { 1484 // TODO at this point the package-level overloading could occur, but I cannot make it reliable, so code removed 1485 } 1486 } 1487 } 1488 1489 switch cc.Value.(type) { 1490 case *ssa.Function: //simple case 1491 ret += targetFunc + "(" 1492 case *ssa.MakeClosure: // it is a closure, but with a static callee 1493 ret += targetFunc + "(" 1494 default: // closure with a dynamic callee 1495 ret += "Closure.callFn(" + fnToCall + ",[" // the callee is in a local variable 1496 } 1497 } 1498 } 1499 if !isBuiltin { 1500 if isGo { 1501 ret += "Scheduler.makeGoroutine()," 1502 } else { 1503 ret += "this._goroutine," 1504 } 1505 } 1506 switch cc.Value.(type) { 1507 case *ssa.Function: //simple case 1508 if !isBuiltin { // don't pass bindings to built-in functions 1509 ret += "[]" // goroutine + bindings 1510 } 1511 case *ssa.MakeClosure: // it is a closure, but with a static callee 1512 ret += "({var _v=" + l.IndirectValue(cc.Value, errorInfo) + ";_v==null?null:_v.bds;})" 1513 default: // closure with a dynamic callee 1514 if !isBuiltin { // don't pass bindings to built-in functions 1515 ret += fnToCall + "==null?null:" + fnToCall + ".bds" 1516 } 1517 } 1518 for arg := range args { 1519 if arg != 0 || !isBuiltin { 1520 ret += "," 1521 } 1522 // SAME LOGIC AS SWITCH IN INVOKE - keep in line 1523 switch args[arg].Type().Underlying().(type) { // TODO this may be in need of further optimization 1524 case *types.Pointer, *types.Slice, *types.Chan: // must pass a reference, not a copy 1525 ret += l.IndirectValue(args[arg], errorInfo) 1526 case *types.Interface: 1527 if isHaxeAPI { 1528 ret += "Force.toHaxeParam(" + l.IndirectValue(args[arg], errorInfo) + ")" 1529 } else { 1530 ret += l.IndirectValue(args[arg], errorInfo) 1531 } 1532 case *types.Basic: 1533 if isHaxeAPI && args[arg].Type().Underlying().(*types.Basic).Kind() == types.String { 1534 ret += "Force.toHaxeString(" + l.IndirectValue(args[arg], errorInfo) + ")" 1535 } else { 1536 ret += l.IndirectValue(args[arg], errorInfo) 1537 } 1538 default: 1539 ret += l.IndirectValue(args[arg], errorInfo) 1540 } 1541 } 1542 if isBuiltin { 1543 switch fnToCall { 1544 case "print", "println": 1545 ret += "]" 1546 } 1547 ret += ")" 1548 } else { 1549 switch cc.Value.(type) { 1550 case *ssa.Function, *ssa.MakeClosure: // it is a call with a list of args 1551 ret += ")" 1552 default: // it is a call with a single arg that is a list 1553 ret += "])" // the callee is in a local variable 1554 } 1555 } 1556 if isBuiltin { 1557 if isGo || isDefer { 1558 l.PogoComp().LogError(errorInfo, "Haxe", 1559 fmt.Errorf("calling a builtin function (%s) via 'go' or 'defer' is not supported", 1560 fnToCall)) 1561 } 1562 if register != "" { 1563 //************************** 1564 // ensure correct conversions for interface{} <-> Dynamic when isHaxeAPI 1565 //************************** 1566 if isHaxeAPI { 1567 r := cc.Signature().Results() 1568 if r.Len() == 1 { 1569 switch r.At(0).Type().Underlying().(type) { 1570 case *types.Interface: 1571 ret = "Interface.fromDynamic(" + ret + ")" 1572 case *types.Basic: 1573 if r.At(0).Type().Underlying().(*types.Basic).Kind() == types.String { 1574 ret = "Force.fromHaxeString(" + ret + ")" 1575 } 1576 } 1577 } 1578 } 1579 return hashIf + register + "=" + ret + ";" + hashEnd 1580 } 1581 return hashIf + ret + ";" + hashEnd 1582 } 1583 if isGo { 1584 if isDefer { 1585 l.PogoComp().LogError(errorInfo, "Haxe", 1586 fmt.Errorf("calling a function (%s) using both 'go' and 'defer' is not supported", 1587 fnToCall)) 1588 } 1589 return ret + "; " 1590 } 1591 if isDefer { 1592 return ret + ";\nthis.defer(Scheduler.pop(this._goroutine));" 1593 } 1594 return l.doCall(register, cc.Signature().Results(), ret+";\n", usesGr) 1595 } 1596 1597 func (l langType) RunDefers(usesGr bool) string { 1598 return l.doCall("", nil, "this.runDefers();\n", usesGr) 1599 } 1600 1601 func (l langType) doCall(register string, tuple *types.Tuple, callCode string, usesGr bool) string { 1602 ret := "" 1603 if register != "" { 1604 ret += fmt.Sprintf("_SF%d=", -l.hc.nextReturnAddress) 1605 } 1606 if usesGr { 1607 ret += callCode 1608 //await completion 1609 ret += fmt.Sprintf("_Next = %d;\n", l.hc.nextReturnAddress) // where to come back to 1610 l.hc.hadBlockReturn = false 1611 ret += "return this;\n" 1612 if l.hc.fnUsesGr { 1613 ret += "#if uselocalfunctions } #end" 1614 } else { 1615 ret += "#if uselocalfunctions }); #end\n" 1616 } 1617 ret += l.emitUnseenPseudoBlocks() 1618 ret += fmt.Sprintf("#if !uselocalfunctions case %d: #end\n", l.hc.nextReturnAddress) // emit code to come back to 1619 if l.hc.fnUsesGr { 1620 fn := fmt.Sprintf(l.hc.currentfnName+"__%d", -l.hc.nextReturnAddress) 1621 l.hc.localFunctionMap[l.hc.nextReturnAddress] = fn 1622 ret += "#if uselocalfunctions function " + fn + "():" + l.hc.currentfnName + " { #end\n" 1623 } else { 1624 ret += fmt.Sprintf("#if uselocalfunctions fnMap.set(%d,function "+l.hc.currentfnName+"__%d():"+l.hc.currentfnName+" { #end\n", 1625 l.hc.nextReturnAddress, -l.hc.nextReturnAddress) 1626 } 1627 //ret += fmt.Sprintf("#if uselocalfunctions function _Block_%d(){ #end\n", 1628 // -nextReturnAddress) // optimize JS with closure to allow V8 to optimize big funcs 1629 if l.PogoComp().DebugFlag { 1630 ret += "this.setLatest(" + fmt.Sprintf("%d", l.PogoComp().LatestValidPosHash) + 1631 "," + fmt.Sprintf("%d", l.hc.nextReturnAddress) + ");\n" 1632 } 1633 ret += l.emitTrace(fmt.Sprintf("Block:%d", l.hc.nextReturnAddress)) 1634 } else { 1635 callCode = strings.TrimSpace(callCode) 1636 if register != "" { 1637 ret += callCode 1638 ret += l.emitTrace(`OPTIMIZED CALL (via stack frame)`) 1639 ret += fmt.Sprintf("_SF%d.run();\n", -l.hc.nextReturnAddress) 1640 } else { 1641 if strings.HasSuffix(callCode, ";") { 1642 ret += l.emitTrace(`OPTIMIZED CALL (no stack frame)`) 1643 ret += fmt.Sprintf("%s.run();\n", strings.TrimSuffix(callCode, ";")) 1644 } else { 1645 ret += l.emitTrace(`OPTIMIZED CALL (via scheduler)`) 1646 ret += fmt.Sprintf("Scheduler.run1();\n") 1647 //was: ret += "Scheduler.run1(this._goroutine);\n" 1648 } 1649 } 1650 } 1651 if register != "" { // if register, set return value, but only for non-null stack frames 1652 registerZero := "" 1653 switch tuple.Len() { 1654 case 0: // nothing to do 1655 case 1: 1656 registerZero = l.LangType(tuple.At(0).Type(), true, callCode) 1657 default: 1658 registerZero = l.LangType(tuple, true, callCode) 1659 } 1660 if registerZero != "" { 1661 //ret += fmt.Sprintf("%s=(_SF%d==null)?%s:_SF%d.res();\n", // goroutine of -1 => null closure 1662 // register, -nextReturnAddress, registerZero, -nextReturnAddress) 1663 ret += fmt.Sprintf("%s=_SF%d.res();\n", // will fail if _SF is null 1664 register, -l.hc.nextReturnAddress) 1665 } 1666 } 1667 l.hc.nextReturnAddress-- //decrement to set new return address for next call generation 1668 return ret 1669 } 1670 1671 func allocNewObject(t types.Type) string { 1672 typ := t.Underlying().(*types.Pointer).Elem().Underlying() 1673 switch typ.(type) { 1674 1675 // this should not be required... 1676 case *types.Array: 1677 ao := haxeStdSizes.Alignof(typ.(*types.Array).Elem().Underlying()) 1678 so := haxeStdSizes.Sizeof(typ.(*types.Array).Elem().Underlying()) 1679 for so%ao != 0 { 1680 so++ 1681 } 1682 return fmt.Sprintf("Object.make(%d) /* Array: %s */", 1683 typ.(*types.Array).Len()*so, typ.String()) 1684 1685 default: 1686 return fmt.Sprintf("Object.make(%d) /* %s */", 1687 haxeStdSizes.Sizeof(typ), 1688 typ.String()) 1689 } 1690 } 1691 1692 func (l langType) Alloc(reg string, heap bool, v interface{}, errorInfo string) string { 1693 if reg == "" { 1694 return "" // if the register is not used, don't emit the code! 1695 } 1696 /* 1697 typ := v.(types.Type).Underlying().(*types.Pointer).Elem().Underlying() 1698 //ele := l.LangType(typ, false, errorInfo) 1699 ptrTyp := "Pointer" 1700 switch typ.(type) { 1701 case *types.Array: 1702 //ele = l.LangType(typ.(*types.Array).Elem().Underlying(), false, errorInfo) 1703 ptrTyp = "Pointer" 1704 case *types.Slice: 1705 //ele = "Slice" 1706 ptrTyp = "Pointer" 1707 case *types.Struct: 1708 //ele = "Dynamic" 1709 ptrTyp = "Pointer" 1710 } 1711 return reg + "=new " + ptrTyp + 1712 "(" + l.LangType(typ, true, errorInfo) + ");" 1713 */ 1714 /* 1715 switch typ.(type) { 1716 case *types.Array: 1717 typ = typ.(*types.Array).Underlying() 1718 case *types.Struct: 1719 typ = typ.(*types.Struct).Underlying() 1720 default: 1721 l.PogoComp().LogError(errorInfo, "Haxe", 1722 fmt.Errorf("haxe.Alloc() - unhandled type: %v", reflect.TypeOf(typ))) 1723 return "" 1724 } 1725 */ 1726 if heap { 1727 return fmt.Sprintf("%s=Pointer.make(%s);", reg, allocNewObject(v.(types.Type))) 1728 } 1729 //fmt.Println("DEBUG Alloc on Stack", reg, errorInfo) 1730 reg2 := strings.Replace(strings.Replace(reg, "[", "", 1), "]", "", 1) // just in case we're in a big init() and are using a register array 1731 return fmt.Sprintf("%s=Pointer.make(%s_stackalloc.clear());", reg, reg2) 1732 } 1733 1734 func (l langType) MakeChan(reg string, v interface{}, errorInfo string) string { 1735 //typeElem := l.LangType(v.(*ssa.MakeChan).Type().Underlying().(*types.Chan).Elem().Underlying(), false, errorInfo) 1736 size := l.IndirectValue(v.(*ssa.MakeChan).Size, errorInfo) 1737 return reg + "=new Channel(" + size + `);` // <" + typeElem + ">(" + size + `);` 1738 } 1739 1740 func newSliceCode(typeElem, initElem, capacity, length, errorInfo, itemSize string) string { 1741 //return "new Slice(new Pointer(new Make<" + typeElem + ">((" + capacity + ")*(" + itemSize + "))" + 1742 // ".array(" + initElem + "," + capacity + ")" + 1743 // "),0," + length + "," + capacity + "," + itemSize + `)` 1744 return "new Slice(Pointer.make(Object.make((" + capacity + ")*(" + itemSize + "))" + 1745 "),0," + length + "," + capacity + "," + itemSize + `)` 1746 } 1747 1748 func (l langType) MakeSlice(reg string, v interface{}, errorInfo string) string { 1749 typeElem := l.LangType(v.(*ssa.MakeSlice).Type().Underlying().(*types.Slice).Elem().Underlying(), false, errorInfo) 1750 initElem := l.LangType(v.(*ssa.MakeSlice).Type().Underlying().(*types.Slice).Elem().Underlying(), true, errorInfo) 1751 length := wrapForceToUInt(l.IndirectValue(v.(*ssa.MakeSlice).Len, errorInfo), 1752 v.(*ssa.MakeSlice).Len.Type().Underlying().(*types.Basic).Kind()) // lengths can't be 64 bit 1753 capacity := wrapForceToUInt(l.IndirectValue(v.(*ssa.MakeSlice).Cap, errorInfo), 1754 v.(*ssa.MakeSlice).Cap.Type().Underlying().(*types.Basic).Kind()) // capacities can't be 64 bit 1755 itemSize := "1" + arrayOffsetCalc(v.(*ssa.MakeSlice).Type().Underlying().(*types.Slice).Elem().Underlying()) 1756 return reg + "=" + newSliceCode(typeElem, initElem, capacity, length, errorInfo, itemSize) + `;` 1757 } 1758 1759 // TODO see http://tip.golang.org/doc/go1.2#three_index 1760 // TODO add third parameter when SSA code provides it to enable slice instructions to specify a capacity 1761 func (l langType) Slice(register string, x, lv, hv interface{}, errorInfo string) string { 1762 xString := l.IndirectValue(x, errorInfo) // the target must be an array 1763 if xString == "" { 1764 xString = l.IndirectValue(x, errorInfo) 1765 } 1766 lvString := "0" 1767 if lv != nil { 1768 lvString = wrapForceToUInt(l.IndirectValue(lv, errorInfo), 1769 lv.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 1770 } 1771 hvString := "-1" 1772 if hv != nil { 1773 hvString = wrapForceToUInt(l.IndirectValue(hv, errorInfo), 1774 hv.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 1775 } 1776 switch x.(ssa.Value).Type().Underlying().(type) { 1777 case *types.Slice: 1778 return register + "=({var _v=" + xString + `;_v==null?null:(_v.subSlice(` + lvString + `,` + hvString + `));});` 1779 case *types.Pointer: 1780 eleSz := "1" + arrayOffsetCalc(x.(ssa.Value).Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Elem().Underlying()) 1781 return register + "=new Slice(" + xString + `,` + lvString + `,` + hvString + "," + 1782 fmt.Sprintf("%d", x.(ssa.Value).Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()) + 1783 "," + eleSz + `);` 1784 case *types.Basic: // assume a string is in need of slicing... 1785 if hvString == "-1" { 1786 hvString = "(" + xString + ").length" 1787 } 1788 return register + "= ({var _lvs=" + lvString + ";(" + xString + ").substr(_lvs," + hvString + "-_lvs) ;});" 1789 default: 1790 l.PogoComp().LogError(errorInfo, "Haxe", 1791 fmt.Errorf("haxe.Slice() - unhandled type: %v", reflect.TypeOf(x.(ssa.Value).Type().Underlying()))) 1792 return "" 1793 } 1794 } 1795 1796 func (l langType) Index(register string, v1, v2 interface{}, errorInfo string) string { 1797 keyString := wrapForceToUInt(l.IndirectValue(v2, errorInfo), 1798 v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 1799 typ := v1.(ssa.Value).Type().Underlying().(*types.Array).Elem().Underlying() 1800 return register + "=" + //l.IndirectValue(v1, errorInfo) + "[" + l.IndirectValue(v2, errorInfo) + "];" + // assign value 1801 fmt.Sprintf("%s.get%s%s%s)", 1802 l.IndirectValue(v1, errorInfo), 1803 loadStoreSuffix(typ, true), 1804 keyString, 1805 arrayOffsetCalc(typ)) + ";" 1806 } 1807 1808 func (l langType) codeField(v interface{}, fNum int, fName, errorInfo string, isFunctionName bool) string { 1809 str := v.(ssa.Value).Type().Underlying().(*types.Struct) 1810 //return fmt.Sprintf(" /* %d */ ", fieldOffset(str, fNum)) + 1811 return fmt.Sprintf("%s.get%s%d)", 1812 l.IndirectValue(v, errorInfo), 1813 loadStoreSuffix(str.Field(fNum).Type().Underlying(), true), 1814 fieldOffset(str, fNum)) 1815 } 1816 1817 // Field emits the code to load a field value into a register 1818 func (l langType) Field(register string, v interface{}, fNum int, fName, errorInfo string, isFunctionName bool) string { 1819 if register != "" { 1820 return register + "=" + l.codeField(v, fNum, fName, errorInfo, isFunctionName) + ";" 1821 } 1822 return "" 1823 } 1824 1825 // RangeCheck emits range check code 1826 func (l langType) RangeCheck(x, i interface{}, length int, errorInfo string) string { 1827 chk := "" 1828 iStr := "" 1829 switch i.(type) { 1830 case string: 1831 iStr = i.(string) 1832 default: 1833 iStr = l.IndirectValue(i, errorInfo) 1834 } 1835 if length <= 0 { // length unknown at compile time 1836 xStr := l.IndirectValue(x, errorInfo) 1837 tPtr := x.(ssa.Value).Type().Underlying() 1838 lStr := "" // should give a Haxe compile time error if this is not set below 1839 //fmt.Println("DEBUG:", l.LangType(x.(ssa.Value).Type().Underlying(), false, errorInfo)) 1840 if l.LangType(tPtr, false, errorInfo) == "Pointer" { 1841 tPtr = tPtr.(*types.Pointer).Elem().Underlying() 1842 } 1843 switch l.LangType(tPtr, false, errorInfo) { 1844 case "Slice": 1845 lStr += "" + xStr + ".length" 1846 case "Object": 1847 lStr += fmt.Sprintf("%d", tPtr.(*types.Array).Len()) 1848 } 1849 chk = fmt.Sprintf("Scheduler.wraprangechk(%s,%s);", iStr, lStr) 1850 } else { 1851 // length is known at compile time => an array 1852 chk = fmt.Sprintf("Scheduler.wraprangechk(%s,%d);", iStr, length) 1853 } 1854 ret := "" 1855 _, hadIt := l.hc.rangeChecks[chk] 1856 if !hadIt { // de-dupe 1857 ret = chk 1858 l.hc.rangeChecks[chk] = struct{}{} 1859 } 1860 return ret 1861 } 1862 1863 func (l langType) MakeMap(reg string, v interface{}, errorInfo string) string { 1864 if reg == "" { 1865 return "" 1866 } 1867 return reg + "=" + l.LangType(v.(*ssa.MakeMap).Type().Underlying(), true, errorInfo) + `;` 1868 } 1869 1870 func (l langType) serializeKey(val, haxeTyp string) string { // can the key be serialized? 1871 switch haxeTyp { 1872 case "String", "Int", "Float", "Bool", 1873 "Pointer", "Object", "GOint64", "Complex", "Interface", "Channel", "Slice": 1874 return val 1875 default: 1876 l.PogoComp().LogError("serializeKey", "haxe", errors.New("unsupported map key type: "+haxeTyp)) 1877 return "" 1878 } 1879 } 1880 1881 func (l langType) MapUpdate(Map, Key, Value interface{}, errorInfo string) string { 1882 skey := l.serializeKey(l.IndirectValue(Key, errorInfo), 1883 l.LangType(Key.(ssa.Value).Type().Underlying(), false, errorInfo)) 1884 ret := l.IndirectValue(Map, errorInfo) + ".set(" 1885 ret += skey + "," //+ l.IndirectValue(Key, errorInfo) + "," 1886 ret += l.IndirectValue(Value, errorInfo) + ");" 1887 return ret 1888 } 1889 1890 func (l langType) Lookup(reg string, Map, Key interface{}, commaOk bool, errorInfo string) string { 1891 if reg == "" { 1892 return "" 1893 } 1894 keyString := l.IndirectValue(Key, errorInfo) 1895 // check if we are looking up in a string 1896 if l.LangType(Map.(ssa.Value).Type().Underlying(), false, errorInfo) == "String" { 1897 keyString = wrapForceToUInt(keyString, Key.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 1898 valueCode := l.IndirectValue(Map, errorInfo) //+ ".charCodeAt(" + keyString + ")" 1899 if commaOk { 1900 return reg + "=Force.stringAtOK(" + valueCode + "," + keyString + ");" 1901 //return reg + "=(" + valueCode + "==null) ?" + 1902 // "{r0:0,r1:false}:{r0:Std.int(" + valueCode + "),r1:true};" 1903 } 1904 return reg + "=Force.stringAt(" + valueCode + "," + keyString + ");" 1905 //return reg + "=(" + valueCode + "==null) ?" + 1906 // "{Scheduler.ioor();0;}:Std.int(" + valueCode + ");" 1907 } 1908 // assume it is a Map 1909 keyString = l.serializeKey(keyString, l.LangType(Key.(ssa.Value).Type().Underlying(), false, errorInfo)) 1910 1911 isNull := l.IndirectValue(Map, errorInfo) + ";var _ks=" + keyString + ";_map==null?" 1912 1913 li := l.LangType(Map.(ssa.Value).Type().Underlying().(*types.Map).Elem().Underlying(), true, errorInfo) 1914 if strings.HasPrefix(li, "new ") { 1915 li = "null" // no need for a full object declaration in this context 1916 } 1917 returnValue := /*l.IndirectValue(Map, errorInfo) +*/ "_map.get(_ks)" //.val 1918 //ltEle := l.LangType(Map.(ssa.Value).Type().Underlying().(*types.Map).Elem().Underlying(), false, errorInfo) 1919 //switch ltEle { 1920 //case "GOint64", "Int", "Float", "Bool", "String", "Pointer", "Slice": 1921 // returnValue = "cast(" + returnValue + "," + ltEle + ")" 1922 //} 1923 eleExists := /*l.IndirectValue(Map, errorInfo) +*/ "_map.exists(_ks)" 1924 if commaOk { 1925 return reg + "=({var _map:GOmap=" + isNull + "{r0:" + li + ",r1:false}:{r0:" + returnValue + ",r1:" + eleExists + "};});" 1926 } 1927 return reg + "=({var _map:GOmap=" + isNull + li + ":" + returnValue + ";});" // the .get will check for existance and return the zero value if not 1928 } 1929 1930 func (l langType) Extract(reg string, tuple interface{}, index int, errorInfo string) string { 1931 tp := l.IndirectValue(tuple, errorInfo) 1932 if l.PogoComp().DebugFlag { 1933 tp = "Force.checkTuple(" + tp + ")" 1934 } 1935 return reg + "=" + tp + ".r" + fmt.Sprintf("%d", index) + ";" 1936 } 1937 1938 func (l langType) Range(reg string, v interface{}, errorInfo string) string { 1939 1940 switch l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) { 1941 case "String": 1942 return reg + "=new GOstringRange(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" 1943 //return reg + "={k:0,v:Force.toUTF8slice(this._goroutine," + l.IndirectValue(v, errorInfo) + ")" + "};" 1944 default: // assume it is a Map {k: key itterator,m: the map,z: zero value of an entry} 1945 return reg + "=({var _map=" + l.IndirectValue(v, errorInfo) + ";_map==null?null:cast(_map,GOmap).range();});" 1946 /* 1947 keyTyp := l.LangType(v.(ssa.Value).Type().Underlying().(*types.Map).Key().Underlying(), false, errorInfo) 1948 if keyTyp != "Int" { 1949 keyTyp = "String" 1950 } 1951 return reg + "={k:" + l.IndirectValue(v, errorInfo) + ".keys(),m:" + l.IndirectValue(v, errorInfo) + 1952 ",zk:" + l.LangType(v.(ssa.Value).Type().Underlying().(*types.Map).Key().Underlying(), true, errorInfo) + 1953 ",zv:" + l.LangType(v.(ssa.Value).Type().Underlying().(*types.Map).Elem().Underlying(), true, errorInfo) + 1954 1955 //`,fk:function(m:` + l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) + ",k:" + 1956 //keyTyp + "):" + 1957 //l.LangType(v.(ssa.Value).Type().Underlying().(*types.Map).Key().Underlying(), false, errorInfo) + 1958 //"{return m.get(" + "k" + ").key;}" + 1959 //`,fv:function(m:` + l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) + ",k:" + 1960 //keyTyp + "):" + 1961 //l.LangType(v.(ssa.Value).Type().Underlying().(*types.Map).Elem().Underlying(), false, errorInfo) + 1962 //"{return m.get(" + "k" + ").val;}" + 1963 1964 `};` 1965 */ 1966 } 1967 } 1968 func (l langType) Next(register string, v interface{}, isString bool, errorInfo string) string { 1969 if isString { 1970 return register + "=cast(" + l.IndirectValue(v, errorInfo) + ",GOstringRange).next();" 1971 /* 1972 return register + "={var _thisK:Int=" + l.IndirectValue(v, errorInfo) + ".k;" + 1973 "if(" + l.IndirectValue(v, errorInfo) + ".k>=" + l.IndirectValue(v, errorInfo) + ".v.len()){r0:false,r1:0,r2:0};" + 1974 "else {" + 1975 "var _dr:{r0:Int,r1:Int}=Go_utf8_DDecodeRRune.callFromRT(this._goroutine," + l.IndirectValue(v, errorInfo) + 1976 ".v.subSlice(_thisK,-1));" + 1977 l.IndirectValue(v, errorInfo) + ".k+=_dr.r1;" + 1978 "{r0:true,r1:cast(_thisK,Int),r2:cast(_dr.r0,Int)};}};" 1979 */ 1980 } 1981 // otherwise it is a map itterator 1982 return register + "=({var _map=" + l.IndirectValue(v, errorInfo) + ";_map==null?{r0:false,r1:null,r2:null}:cast(_map,GOmapRange).next();});" 1983 /* 1984 return register + "={var _hn:Bool=" + l.IndirectValue(v, errorInfo) + ".k.hasNext();\n" + 1985 "if(_hn){var _nxt=" + l.IndirectValue(v, errorInfo) + ".k.next();\n" + 1986 //"$type(" + l.IndirectValue(v, errorInfo) + ".m);\n" + 1987 "{r0:true,r1:" + l.IndirectValue(v, errorInfo) + ".m.get(_nxt).key," + 1988 "r2:" + l.IndirectValue(v, errorInfo) + ".m.get(_nxt).val};\n" + 1989 "}else{{r0:false,r1:" + l.IndirectValue(v, errorInfo) + ".zk,r2:" + l.IndirectValue(v, errorInfo) + ".zv};\n}};" 1990 */ 1991 } 1992 1993 func (l langType) MakeClosure(reg string, v interface{}, errorInfo string) string { 1994 // use a closure type 1995 ret := reg + "= new Closure(" + l.IndirectValue(v.(*ssa.MakeClosure).Fn, errorInfo) + ",[" 1996 for b := range v.(*ssa.MakeClosure).Bindings { 1997 if b != 0 { 1998 ret += "," 1999 } 2000 //ret += `` + v.(*ssa.MakeClosure).Fn.(*ssa.Function).FreeVars[b].Name() + `: ` 2001 ret += l.IndirectValue(v.(*ssa.MakeClosure).Bindings[b], errorInfo) 2002 } 2003 return ret + "]);" 2004 2005 //it does not work to try just returning the function, and let the invloking call do the binding 2006 //as in: return reg + "=" + l.IndirectValue(v.(*ssa.MakeClosure).Fn, errorInfo) + ";" 2007 } 2008 2009 func (l langType) EmitInvoke(register, path string, isGo, isDefer, usesGr bool, callCommon interface{}, errorInfo string) string { 2010 val := callCommon.(ssa.CallCommon).Value 2011 meth := callCommon.(ssa.CallCommon).Method.Name() 2012 ret := "" 2013 if l.PogoComp().DebugFlag { 2014 ret += l.IndirectValue(val, errorInfo) + "==null?Scheduler.unt():" 2015 } 2016 ret += "Interface.invoke(" + l.IndirectValue(val, errorInfo) + `,"` + 2017 path + `"` + `,"` + meth + `",[` 2018 if isGo { 2019 if isDefer { 2020 l.PogoComp().LogError(errorInfo, "Haxe", 2021 fmt.Errorf("calling a method (%s) using both 'go' and 'defer' is not supported", 2022 meth)) 2023 } 2024 ret += "Scheduler.makeGoroutine()" 2025 } else { 2026 ret += "this._goroutine" 2027 } 2028 ret += `,[],` + l.IndirectValue(val, errorInfo) + ".val" 2029 args := callCommon.(ssa.CallCommon).Args 2030 for arg := range args { 2031 ret += "," 2032 // SAME LOGIC AS SWITCH IN CALL - keep in line 2033 switch args[arg].Type().Underlying().(type) { // TODO this may be in need of further optimization 2034 case *types.Pointer, *types.Slice, *types.Chan: // must pass a reference, not a copy 2035 ret += l.IndirectValue(args[arg], errorInfo) 2036 case *types.Basic, *types.Interface: // NOTE Complex is an object as is Int64 (in java & cs), but copy does not seem to be required 2037 ret += l.IndirectValue(args[arg], errorInfo) 2038 default: // TODO review 2039 ret += l.IndirectValue(args[arg], errorInfo) 2040 } 2041 } 2042 if isGo { 2043 return ret + "]); " 2044 } 2045 if isDefer { 2046 return ret + "]);\nthis.defer(Scheduler.pop(this._goroutine));" 2047 } 2048 cc := callCommon.(ssa.CallCommon) 2049 return l.doCall(register, cc.Signature().Results(), ret+"]);", usesGr) 2050 } 2051 2052 func (l langType) deDupAssign(register, code string) string { 2053 if l.hc.deDupRHS != nil { 2054 prevReg, found := l.hc.deDupRHS[code] 2055 if found { 2056 code = prevReg 2057 } else { 2058 l.hc.deDupRHS[code] = register + "; // DE-DUP: " + code 2059 } 2060 } 2061 return register + "=" + code 2062 } 2063 2064 const alwaysStackdump = false 2065 2066 func (l langType) SubFnStart(id int, mustSplitCode bool, ins []ssa.Instruction) string { 2067 l.reset1useMap() 2068 l.hc.subFnInstrs = ins 2069 l.hc.deDupRHS = make(map[string]string) 2070 l.hc.tempVarList = []regToFree{} 2071 if mustSplitCode { 2072 l.hc.inMustSplitSubFn = true 2073 } else { 2074 if alwaysStackdump || l.PogoComp().DebugFlag { 2075 return "try {" 2076 } 2077 return "" 2078 } 2079 if alwaysStackdump || l.PogoComp().DebugFlag { 2080 return fmt.Sprintf("private "+"function SubFn%d():Void { try {", id) 2081 } 2082 return fmt.Sprintf("private "+"function SubFn%d():Void { ", id) 2083 } 2084 2085 func (l langType) SubFnEnd(id, pos int, mustSplitCode bool) string { 2086 l.hc.deDupRHS = nil 2087 l.hc.inMustSplitSubFn = false 2088 ret := "" 2089 ret += l.nullTempVars() 2090 if alwaysStackdump || l.PogoComp().DebugFlag { 2091 ret += fmt.Sprintf("} catch (c:Dynamic) {Scheduler.htc(c,%d);}", pos) 2092 } 2093 if mustSplitCode { 2094 ret += "}" 2095 } 2096 return ret 2097 } 2098 2099 func (l langType) SubFnCall(id int) string { 2100 return fmt.Sprintf("SubFn%d();", id) 2101 } 2102 2103 func (l langType) DeclareTempVar(v ssa.Value) string { 2104 if l.hc.useRegisterArray { 2105 return "" 2106 } 2107 if len(*(v.Referrers())) == 0 { 2108 return "" 2109 } 2110 if l.is1usePtr(v) { 2111 return "" // "// virtual oneUsePtr _" + v.Name() 2112 } 2113 typ := l.LangType(v.Type(), false, "temp var declaration") 2114 if typ == "" { 2115 return "" 2116 } 2117 if typ == "String" { 2118 l.hc.tempVarList = append(l.hc.tempVarList, regToFree{"_" + v.Name(), typ}) 2119 } 2120 init := l.LangType(v.Type(), true, "temp var declaration") 2121 if init == "null" || 2122 strings.HasPrefix(init, "new") || 2123 strings.HasPrefix(init, "{") || 2124 strings.HasPrefix(init, "Object.make") || 2125 strings.HasPrefix(init, "Pointer.make") || 2126 strings.HasPrefix(init, "GOint64") { 2127 init = "null" 2128 l.hc.tempVarList = append(l.hc.tempVarList, regToFree{"_" + v.Name(), typ}) 2129 } 2130 init = "#if jsinit =" + init + " #end " // to allow V8 optimisation? 2131 return "var _" + v.Name() + ":" + typ + " " + init + ";" 2132 } 2133 2134 func (l langType) nullTempVars() string { 2135 ret := " #if nulltempvars\n" 2136 ret += recycle(l.hc.tempVarList) // NOTE this helps GC for all targets, especially C++ 2137 ret += " #end\n" 2138 l.hc.tempVarList = []regToFree{} 2139 return ret 2140 }