github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/haxe/haxeRuntime.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 // Runtime Haxe code for Go, which may eventually become a haxe library when the system settles down. 8 // TODO All runtime class names are currently carried through if the haxe code uses "import tardis.Go;" and some are too generic, 9 // others, like Int64, will overload the Haxe standard library version for some platforms, which may cause other problems. 10 // So the haxe Class names eventually need to be prefaced with "Go" to ensure there are no name-clashes. 11 // TODO using a library would reduce some of the compilation overhead of always re-compiling this code. 12 // However, there are references to Go->Haxe generated classes, like "Go", that would need to be managed somehow. 13 // TODO consider merging and possibly renaming the Deep and Force classes as they both hold general utility code 14 15 func (l langType) haxeruntime() string { 16 17 l.PogoComp().WriteAsClass("Console", ` 18 19 class Console { 20 public static inline function naclWrite(v:String){ 21 #if ( cpp || cs || java || neko || php || python ) 22 Sys.print(v); 23 #else 24 haxe.Log.trace(v); 25 #end 26 } 27 public static inline function println(v:Array<Dynamic>) { 28 #if ( cpp || cs || java || neko || php || python ) 29 Sys.println(join(v)); 30 #else 31 haxe.Log.trace(join(v)); 32 #end 33 } 34 public static inline function print(v:Array<Dynamic>) { 35 #if ( cpp || cs || java || neko || php || python ) 36 Sys.print(join(v)); 37 #else 38 haxe.Log.trace(join(v)); 39 #end 40 } 41 static function join(v:Array<Dynamic>):String { 42 var s = ""; 43 if(v==null) return s; 44 for (i in 0...v.length) { 45 if(v[i]==null) 46 s += " "; 47 else 48 s += Std.string(v[i]) + " " ; 49 } 50 return Force.toHaxeString(s); 51 } 52 public static function readln():Null<String> { 53 #if (cpp || cs || java || neko || php ) 54 var s:String=""; 55 var ch:Int=0; 56 while(ch != 13 ){ // carrage return (mac) 57 ch = Sys.getChar(true); 58 //Sys.println(ch); 59 if(ch == 127){ // backspace (mac) 60 s = s.substr(0,s.length-1); 61 Sys.print("\n"+s); 62 }else{ 63 s += String.fromCharCode(ch); 64 } 65 } 66 s = s.substr(0,s.length-1); // loose final CR 67 Sys.print("\n"); 68 if(s.length==0) 69 return null; 70 else 71 return s; 72 #else 73 return null; 74 #end 75 } 76 } 77 78 `) 79 l.PogoComp().WriteAsClass("Force", ` 80 // TODO: consider putting these go-compatibiliy classes into a separate library for general Haxe use when calling Go 81 82 class Force { // TODO maybe this should not be a separate haxe class, as no non-Go code needs access to it 83 public static inline function toUint8(v:Int): #if cpp cpp.UInt8 #else Int #end 84 { 85 #if cpp 86 return v; 87 #elseif cs 88 return cast(cast(v,cs.StdTypes.UInt8),Int); 89 #else 90 return v & 0xFF; 91 #end 92 } 93 public static inline function toUint16(v:Int): #if cpp cpp.UInt16 #else Int #end { 94 #if cpp 95 return v; 96 #elseif cs 97 return cast(cast(v,cs.StdTypes.UInt16),Int); 98 #else 99 return v & 0xFFFF; 100 #end 101 } 102 public static inline function toUint32(v:Int):Int { 103 #if js 104 return v >>> untyped __js__("0"); // using GopherJS method (with workround to stop it being optimized away by Haxe) 105 #elseif php 106 return v & untyped __php__("0xffffffff"); 107 #else 108 return v; 109 #end 110 } 111 public static inline function toUint64(v:GOint64):GOint64 { 112 return v; 113 } 114 public static #if (cpp||java||cs) inline #end function toInt8(v:Int): #if cpp cpp.Int8 #else Int #end { 115 #if cpp 116 return v; 117 #elseif java 118 return cast(cast(v,java.StdTypes.Int8),Int); 119 #elseif cs 120 return cast(cast(v,cs.StdTypes.Int8),Int); 121 #else 122 var r:Int = v & 0xFF; 123 if ((r & 0x80) != 0){ // it should be -ve 124 return -1 - 0xFF + r; 125 } 126 return r; 127 #end 128 } 129 public static #if (cpp||java||cs) inline #end function toInt16(v:Int): #if cpp cpp.Int16 #else Int #end { 130 #if cpp 131 return v; 132 #elseif java 133 return cast(cast(v,java.StdTypes.Int16),Int); 134 #elseif cs 135 return cast(cast(v,cs.StdTypes.Int16),Int); 136 #else 137 var r:Int = v & 0xFFFF; 138 if ((r & 0x8000) != 0){ // it should be -ve 139 return -1 - 0xFFFF + r; 140 } 141 return r; 142 #end 143 } 144 public static inline function toInt32(v:Int):Int { 145 #if js 146 return v >> untyped __js__("0"); // using GopherJS method (with workround to stop it being optimized away by Haxe) 147 #elseif php 148 //see: http://stackoverflow.com/questions/300840/force-php-integer-overflow 149 v = (v & untyped __php__("0xFFFFFFFF")); 150 if( (v & untyped __php__("0x80000000")) != 0) 151 v = -((~v & untyped __php__("0xFFFFFFFF")) + 1); 152 return v; 153 #else 154 return v; 155 #end 156 } 157 public static inline function toInt64(v:GOint64):GOint64 { // this in case special handling is required for some platforms 158 return v; 159 } 160 public static function toInt(v:Dynamic):Int { // get an Int from a Dynamic variable (uintptr is stored as Dynamic) 161 if(v==null) return 0; 162 if (Reflect.isObject(v)) 163 if(Std.is(v,Interface)) { 164 v=v.val; // it is in an interface, so get the value 165 return toInt(v); // recurse to handle 64-bit or float or uintptr 166 } else // it should be an Int64 if not an interface 167 if(Std.is(v,Pointer)) { 168 //Scheduler.panicFromHaxe("attempt to do Pointer arithmetic"); 169 return v.hashInt(); 170 }else 171 return GOint64.toInt(v); // may never get here if GOint64 is an abstract 172 else 173 if(Std.is(v,Int)) 174 return v; 175 else 176 if(Std.is(v,Float)) 177 return v>=0?Math.floor(v):Math.ceil(v); 178 else 179 if(haxe.Int64.is(v)) // detect the abstract 180 return haxe.Int64.toInt(v); 181 else 182 return cast(v,Int); // default cast 183 } 184 public static inline function toFloat(v:Float):Float { 185 // neko target platform requires special handling because it auto-converts whole-number Float into Int without asking 186 // see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but was not fixed as at 2013.9.6 187 // NOTE: this issue means that the neko/--interp is useless for testing anything math-based 188 #if neko 189 if(Std.is(v,Int)) { 190 return v + 2.2251e-308; // add the smallest value possible for a 64-bit float to ensure neko doesn't still think it is an int 191 } else 192 return v; 193 #else 194 return v; 195 #end 196 } 197 #if (cpp || neko) 198 static public var f64byts = haxe.io.Bytes.alloc(8); 199 #elseif js // NOTE this code uses js dataview even when not in fullunsafe mode 200 static private var f32dView = new js.html.DataView(new js.html.ArrayBuffer(8),0,8); 201 #end 202 public static function toFloat32(v:Float):Float { 203 #if (cpp || neko) 204 f64byts.setFloat(0,v); 205 return f64byts.getFloat(0); 206 #elseif js 207 f32dView.setFloat32(0,v); 208 return f32dView.getFloat32(0); 209 #elseif cs 210 return untyped __cs__("(double)((float)v)"); 211 #elseif java 212 return untyped __java__("(double)((float)v)"); 213 #else 214 if(Go.haxegoruntime_IInFF32fb.load_bool()) { // in the Float32frombits() function so don't recurse 215 return v; 216 } else { 217 return Go_haxegoruntime_FFloat32frombits.callFromRT(0,Go_haxegoruntime_FFloat32bits.callFromRT(0,v)); 218 } 219 #end 220 } 221 // TODO implement speed improvements when code below is correct 222 //START POTENTIAL SPEED-UP CODE 223 /* 224 public static function Float32bits(v:Float):Int { 225 #if (cpp || neko) 226 f64byts.setFloat(0,v); 227 return toUint32( f64byts.get(0) | (f64byts.get(1)<<8) | (f64byts.get(2)<<16) | (f64byts.get(3)<<24) ); //little-endian 228 #elseif js 229 f32dView.setFloat32(0,v); 230 return toUint32(f32dView.getUint32(0)); 231 #else 232 Scheduler.panicFromHaxe("Force.Float32bits unreachable code"); 233 return 0; 234 #end 235 } 236 public static function Float32frombits(v:Int):Float { 237 #if (cpp || neko) 238 f64byts.set(0,v&0xff); 239 f64byts.set(1,(v>>8)&0xff); 240 f64byts.set(2,(v>>16)&0xff); 241 f64byts.set(3,(v>>24)&0xff); //little-endian 242 return f64byts.getFloat(0); 243 #elseif js 244 f32dView.setUint32(0,v); 245 return f32dView.getFloat32(0); 246 #else 247 Scheduler.panicFromHaxe("Force.Float32frombits unreachable code"); 248 return 0; 249 #end 250 } 251 */ 252 public static function Float64bits(v:Float):GOint64 { 253 #if cs 254 var rv:haxe.Int64 = untyped __cs__("System.BitConverter.DoubleToInt64Bits(v)"); 255 return rv; 256 //#elseif (cpp || neko) 257 // f64byts.setDouble(0,v); 258 // return GOint64.make( 259 // f64byts.get(4) | (f64byts.get(5)<<8) | (f64byts.get(6)<<16) | (f64byts.get(7)<<24) , 260 // f64byts.get(0) | (f64byts.get(1)<<8) | (f64byts.get(2)<<16) | (f64byts.get(3)<<24) ); //little-endian 261 //#elseif js 262 // f32dView.setFloat64(0,v); 263 // return GOint64.make(f32dView.getUint32(4),f32dView.getUint32(0)); 264 #else 265 Scheduler.panicFromHaxe("Force.Float64bits unreachable code"); 266 return GOint64.ofInt(0); 267 #end 268 } 269 public static function Float64frombits(v:GOint64):Float { 270 #if cs 271 var hv:haxe.Int64=v; 272 return untyped __cs__("System.BitConverter.Int64BitsToDouble(hv)"); 273 //#elseif (cpp || neko) 274 // var v0 = GOint64.getLow(v); 275 // var v1 = GOint64.getHigh(v); 276 // f64byts.set(0,v0&0xff); 277 // f64byts.set(1,(v0>>8)&0xff); 278 // f64byts.set(2,(v0>>16)&0xff); 279 // f64byts.set(3,(v0>>24)&0xff); //little-endian 280 // f64byts.set(4,v1&0xff); 281 // f64byts.set(5,(v1>>8)&0xff); 282 // f64byts.set(6,(v1>>16)&0xff); 283 // f64byts.set(7,(v1>>24)&0xff); //little-endian 284 // return f64byts.getDouble(0); 285 //#elseif js 286 // f32dView.setUint32(0,GOint64.getLow(v)); 287 // f32dView.setUint32(4,GOint64.getHigh(v)); 288 // return f32dView.getFloat64(0); 289 #else 290 Scheduler.panicFromHaxe("Force.Float64frombits unreachable code"); 291 return 0; 292 #end 293 } 294 //END POTENTIAL SPEED-UP CODE 295 // 296 public static function uintCompare(x:Int,y:Int):Int { // +ve if uint(x)>unint(y), 0 equal, else -ve 297 #if js x=x>>>untyped __js__("0");y=y>>>untyped __js__("0"); #end 298 if(x==y) return 0; // simple case first for speed TODO is it faster with this in or out? 299 if(x>=0) { 300 if(y>=0){ // both +ve so normal comparison 301 return (x-y); 302 }else{ // y -ve and so larger than x 303 return -1; 304 } 305 }else { // x -ve 306 if(y>=0){ // -ve x larger than +ve y 307 return 1; 308 }else{ // both are -ve so the normal comparison 309 return (x-y); //eg -1::-7 gives -1--7 = +6 meaning -1 > -7 310 } 311 } 312 } 313 private static function checkIntDiv(x:Int,y:Int,byts:Int):Int { // implement the special processing required by Go 314 var r:Int=y; 315 switch(y) { 316 case 0: 317 Scheduler.panicFromHaxe("attempt to divide integer value by 0"); 318 case -1: 319 switch (byts) { 320 case 1: 321 if(x== -128) r=1; // special case in the Go spec 322 case 2: 323 if(x== -32768) r=1; // special case in the Go spec 324 case 4: 325 if(x== -2147483648) r=1; // special case in the Go spec 326 default: 327 // noOp - 0 => unsigned 328 } 329 } 330 return r; 331 } 332 //TODO maybe optimize by not passing the special value and having multiple versions of functions 333 public static function intDiv(x:Int,y:Int,sv:Int):Int { 334 y = checkIntDiv(x,y,sv); 335 if(y==1) return x; // x div 1 is x 336 if((sv>0)||((x>0)&&(y>0))){ // signed division will work (even though it may be unsigned) 337 var f:Float= cast(x,Float) / cast(y,Float); 338 return f>=0?Math.floor(f):Math.ceil(f); 339 } else { // unsigned division 340 return toUint32(GOint64.toInt(GOint64.div(GOint64.make(0x0,x),GOint64.make(0x0,y),false))); 341 } 342 } 343 public static function intMod(x:Int,y:Int,sv:Int):Int { 344 y = checkIntDiv(x,y,sv); 345 if(y==1) return 0; // x mod 1 is 0 346 if((sv>0)||((x>0)&&(y>0))){ // signed mod will work (even though it may be unsigned) 347 return x % y; 348 } else { // unsigned mod (do it in 64 bits to ensure unsigned) 349 return toUint32(GOint64.toInt(GOint64.mod(GOint64.make(0x0,x),GOint64.make(0x0,y),false))); 350 } 351 } 352 public static inline function intMul(x:Int,y:Int,sv:Int):Int { // TODO optimize away sv 353 #if (js || php) 354 if(sv>0){ // signed mul 355 return x*y; //toInt32(GOint64.toInt(GOint64.mul(GOint64.ofInt(x),GOint64.ofInt(y)))); // TODO review if this required 356 } else { // unsigned mul 357 return toUint32(GOint64.toInt(GOint64.mul(GOint64.ofUInt(x),GOint64.ofUInt(y)))); // required for overflowing mul 358 } 359 #else 360 return x * y; 361 #end 362 } 363 public static var minusZero:Float= 1.0 / Math.NEGATIVE_INFINITY ; 364 private static var zero:Float=0.0; 365 private static var MinFloat64:Float = -1.797693134862315708145274237317043567981e+308; // 2**1023 * (2**53 - 1) / 2**52 366 public static #if !php inline #end function floatDiv(x:Float,y:Float):Float { 367 #if ( php ) 368 // NOTE for php 0 != 0.0 !!! 369 if(y==0) // divide by zero gives +/- infinity - so valid ... TODO check back to Go spec 370 if(x==0) 371 return Math.NaN; // NaN +/- 372 else 373 if(x>0) return Math.POSITIVE_INFINITY; 374 else return Math.NEGATIVE_INFINITY; 375 if(x==0) 376 if(y>0) return 0; // x==y==0.0 already handled above 377 else return minusZero; // should be -0 378 #end 379 return x/y; 380 } 381 public static function floatMod(x:Float,y:Float):Float { 382 if(y==0.0) 383 Scheduler.panicFromHaxe("attempt to modulo float value by 0"); 384 #if ( php ) 385 if(x==0) 386 if(y>=0) return x; // to allow for -0 387 else return return zero * -1; // should be -0 388 #end 389 return x%y; 390 } 391 392 public static inline function toUTF8length(gr:Int,s:String):Int { 393 return s.length; 394 } 395 // return the UTF8 version of a string in a Slice 396 public static function toUTF8slice(gr:Int,s:String):Slice { // TODO remove gr param 397 var sl=s.length; 398 var obj = Object.make(sl); 399 for(i in 0...sl) { 400 #if (js || php || neko ) // nullable targets 401 var t:Null<Int>=s.charCodeAt(i); 402 if(t==null) t=0; 403 obj.set_uint8(i,t); 404 #else 405 obj.set_uint8(i,s.charCodeAt(i)); 406 #end 407 } 408 var ptr = Pointer.make(obj); 409 var ret = new Slice(ptr,0,-1,sl,1); 410 #if nulltempvars 411 ptr=null; obj=null; // for GC 412 #end 413 return ret; 414 } 415 public static function toRawString(gr:Int,sl:Slice):String { // TODO remove gr param 416 if(sl==null) return ""; 417 var sll=sl.len(); 418 if(sll==0) return ""; 419 var ptr = sl.itemAddr(0); // pointer to the start of the slice 420 var obj = ptr.obj; // the object containing the slice data 421 var off = ptr.off; // the offset to the start of that data 422 var end = sll+off; 423 #if cpp 424 var buf=haxe.io.Bytes.alloc(sll); 425 for( i in off...end) { 426 buf.set(i-off,obj.get_uint8(i)); 427 } 428 var ret=buf.getString(0,sll); 429 #if nulltempvars 430 buf=null; 431 ptr=null; 432 obj=null; 433 #end 434 return ret; 435 #else 436 // very slow for cpp: 437 var ret = new StringBuf(); // use StringBuf for speed 438 for( i in off...end ) { 439 ret.addChar( obj.get_uint8(i) ); 440 } 441 var s=ret.toString(); 442 #if nulltempvars 443 ptr=null; 444 obj=null; 445 ret=null; 446 #end 447 return s; 448 #end 449 } 450 451 public static function toHaxeParam(v:Dynamic):Dynamic { // TODO optimize if we know it is a function or string 452 if(v==null) return null; 453 if(Std.is(v,Interface)){ 454 if(Std.is(v.val,Closure) && v.typ!=-1){ // a closure not made by hx.CallbackFunc 455 return v.val.buildCallbackFn(); 456 }else{ 457 v = v.val; 458 return toHaxeParam(v); 459 } 460 } 461 if(Std.is(v,String)){ 462 v=toHaxeString(v); 463 } 464 // TODO ! 465 //if(GOint64.is(v)) { 466 // v=cast(v,haxe.Int64); 467 //} 468 return v; 469 } 470 471 public static #if (cpp || neko || php) inline #end function toHaxeString(v:String):String { 472 #if !( cpp || neko || php ) // need to translate back to UTF16 when passing back to Haxe 473 #if js if(v==null) return ""; #end 474 if(v.length==0) return ""; 475 var sli:Slice=new Slice(Pointer.make(Object.make(v.length)),0,-1,v.length,1); 476 var ptr:Pointer=null; 477 var ch:Int=0; 478 for(i in 0...v.length){ 479 //if(v.charCodeAt(i)>0xff) return v; // probably already encoded as UTF-16 480 ptr=sli.itemAddr(i); 481 ch=v.charCodeAt(i); 482 //trace("DEBUG toHaxeString utf8 in=",i,ch); 483 ptr.store_uint8(ch); 484 } 485 //trace("DEBUG sli=",sli); 486 var slr = Go_haxegoruntime_UUTTFF8toRRunes.callFromRT(0,sli); 487 //trace("DEBUG slr=",slr); 488 var slo = Go_haxegoruntime_RRunesTToUUTTFF16.callFromRT(0,slr); 489 //trace("DEBUG slo=",slo); 490 v=""; 491 for(i in 0...slo.len()) { 492 ptr = slo.itemAddr(i); 493 ch = ptr.load_uint16(); 494 //trace("DEBUG toHaxeString utf16 out=",i,ptr,ch); 495 v += String.fromCharCode( ch ); 496 } 497 #if nulltempvars 498 ptr=null; 499 slr=null; 500 slo=null; 501 #end 502 #end 503 return v; 504 } 505 506 public static #if (cpp || neko || php) inline #end function fromHaxeString(v:String):String { 507 #if !( cpp || neko || php ) // need to translate from UTF16 to UTF8 when passing back to Go 508 #if (js || php) if(v==null) return ""; #end 509 var sli:Slice=new Slice(Pointer.make(Object.make(v.length<<1)),0,-1,v.length,2); 510 var ptr:Pointer; 511 for(i in 0...v.length){ 512 ptr=sli.itemAddr(i); 513 ptr.store_uint16(v.charCodeAt(i)); 514 } 515 var slr = Go_haxegoruntime_UUTTFF16toRRunes.callFromRT(0,sli); 516 var slo = Go_haxegoruntime_RRunesTToUUTTFF8.callFromRT(0,slr); 517 v=""; 518 for(i in 0...slo.len()) { 519 ptr=slo.itemAddr(i); 520 v += String.fromCharCode( ptr.load_uint8() ); 521 } 522 #if nulltempvars 523 ptr=null; 524 slr=null; 525 slo=null; 526 #end 527 #end 528 return v; 529 } 530 531 public static function checkTuple(t:Dynamic):Dynamic { 532 if(t==null) Scheduler.panicFromHaxe("tuple returned from function or range is null"); 533 return t; 534 } 535 536 public static function stringAt(s:String,i:Int):Int{ 537 var c = s.charCodeAt(i); 538 if(c==null) 539 Scheduler.panicFromHaxe("string index out of range"); 540 return toUint8(c); 541 } 542 public static function stringAtOK(s:String,i:Int):Dynamic { 543 var c = s.charCodeAt(i); 544 if(c==null) 545 return {r0:0,r1:false}; 546 else 547 return {r0:toUint8(c),r1:true}; 548 } 549 public static function isEqualDynamic(a:Dynamic,b:Dynamic):Bool{ 550 if(a==b) 551 return true; // simple equality 552 if(Reflect.isObject(a) && Reflect.isObject(b)){ 553 if(Std.is(a,String) && Std.is(b,String)) 554 return a==b; 555 if(Std.is(a,Pointer) && Std.is(b,Pointer)) 556 return Pointer.isEqual(a,b); // could still be equal within a Pointer type 557 if(Std.is(a,Complex) && Std.is(b,Complex)) 558 return Complex.eq(a,b); 559 if(Std.is(a,Interface) && Std.is(b,Interface)) 560 return Interface.isEqual(a,b); // interfaces 561 #if !abstractobjects 562 if(Std.is(a,Object) && Std.is(b,Object)) 563 return a.isEqual(0,b,0); 564 #end 565 566 if(Std.is(a,GOmap)||Std.is(a,Closure)||Std.is(a,Slice)||Std.is(a,Channel)) { 567 Scheduler.panicFromHaxe("Unsupported isEqualDynamic haxe type:"+a); 568 return false; // never equal 569 } 570 571 // assume GOint64 - Std.is() does not work for abstract types 572 return GOint64.compare(a,b)==0; 573 } 574 if(Std.is(a,Float)&&Std.is(b,Float)){ 575 return GOint64.compare( 576 Go_haxegoruntime_FFloat64bits.callFromRT(0,a), 577 Go_haxegoruntime_FFloat64bits.callFromRT(0,b))==0; 578 } 579 return false; 580 } 581 public static function stringFromRune(rune:Int):String{ 582 var _ret:String=""; 583 var _r:Slice=Go_haxegoruntime_RRune2RRaw.callFromRT(0,rune); 584 var _ptr:Pointer; 585 var rl=_r.len(); 586 for(_i in 0...rl){ 587 _ptr=_r.itemAddr(_i); 588 _ret+=String.fromCharCode(_ptr.load_int32()); 589 } 590 #if nulltempvars 591 _ptr=null; 592 _r=null; 593 #end 594 return _ret; 595 } 596 } 597 `) 598 objClass := ` 599 600 // Object code 601 // a single type of Go object 602 @:keep 603 #if abstractobjects 604 abstract Object (haxe.ds.Vector<Dynamic>) to haxe.ds.Vector<Dynamic> from haxe.ds.Vector<Dynamic> { 605 #else 606 class Object { 607 #end 608 public static inline function make(size:Int,?byts:haxe.io.Bytes):Object { 609 #if abstractobjects 610 var ret = new haxe.ds.Vector<Dynamic>(size); 611 if(byts!=null){ 612 for(i in 0 ... byts.length) ret[i] = byts.get(i); 613 } 614 return ret; 615 #else 616 return new Object(size,byts); 617 #end 618 } 619 620 #if ((js || cpp || neko) && fullunsafe) 621 public static var nativeFloats:Bool=true; 622 #else 623 public static var nativeFloats:Bool=false; 624 #end 625 626 #if abstractobjects 627 public var length(get, never):Int; 628 function get_length() return this.length; 629 public inline function len():Int { 630 return length; 631 } 632 public inline function uniqueRef():Int { 633 Console.naclWrite("uniqueRef!\n"); 634 return 0; // TODO 635 } 636 #else 637 private var dVec4:haxe.ds.Vector<Dynamic>; // on 4-byte boundaries 638 #if (js && fullunsafe) // native memory access (nearly) 639 private var arrayBuffer:js.html.ArrayBuffer; 640 private var dView:js.html.DataView; 641 #elseif !fullunsafe // Simple! 1 address per byte, non-Int types are always on 4-byte 642 private var iVec:haxe.ds.Vector<Int>; 643 #else // fullunsafe position is to allow unsafe pointers, and therefore run slowly... 644 private var byts:haxe.io.Bytes; 645 #end 646 public inline function len():Int { 647 return length; 648 } 649 private var length:Int; 650 public inline function uniqueRef():Int { 651 return uRef; 652 } 653 private var uRef:Int; // to give pointers a unique numerical value 654 #end 655 private static var uniqueCount:Int=0; 656 #if godebug 657 public static var memory = new Map<Int,Object>(); 658 #end 659 660 #if abstractobjects 661 inline public function new(v:haxe.ds.Vector<Dynamic>) { 662 this = v; 663 } 664 #else 665 public function new(byteSize:Int,?bytes:haxe.io.Bytes){ // size is in bytes 666 dVec4 = new haxe.ds.Vector<Dynamic>(1+(byteSize>>2)); // +1 to make sure non-zero 667 if(bytes!=null) byteSize = bytes.length; 668 #if (js && fullunsafe) 669 arrayBuffer = new js.html.ArrayBuffer(byteSize); 670 if(byteSize>0) 671 dView = new js.html.DataView(arrayBuffer,0,byteSize); // complains if size is 0, TODO review 672 if(bytes!=null) 673 for(i in 0 ... byteSize) 674 set_uint8(i, bytes.get(i)); 675 #elseif !fullunsafe 676 iVec = new haxe.ds.Vector<Int>(byteSize); 677 if(bytes!=null) 678 for(i in 0 ... byteSize) 679 iVec[i] = bytes.get(i); 680 #else 681 if(bytes==null) { 682 byts = haxe.io.Bytes.alloc(byteSize); 683 //byts.fill(0,byteSize,0); 684 } else byts = bytes; 685 #end 686 length = byteSize; 687 uniqueCount += 1; 688 uRef = uniqueCount; 689 #if godebug 690 memory.set(uniqueRef(),this); 691 #end 692 #if nonulltests 693 #if (js || php || neko ) 694 for(i in 0...length) 695 set_uint8(i,0); 696 #end 697 #end 698 } 699 #end 700 public function getBytes():haxe.io.Bytes { 701 #if (js && fullunsafe) 702 var byts = haxe.io.Bytes.alloc(length); 703 for(i in 0 ... length) 704 byts.set(i,get_uint8(i)); 705 #elseif abstractobjects 706 var byts = haxe.io.Bytes.alloc(this.length); 707 for(i in 0 ... this.length) 708 byts.set(i,this[i]); 709 #elseif !fullunsafe 710 var byts = haxe.io.Bytes.alloc(length); 711 for(i in 0 ... length) 712 byts.set(i,iVec[i]); 713 #else 714 // the byts field already exists 715 #end 716 return byts; 717 } 718 public function clear():Object { 719 for(i in 0...this.length){ 720 set_uint8(i,0); 721 if(i&3==0) set(i,null); 722 } 723 return this; // to allow use without a temp var 724 } 725 public function isEqual(off:Int,target:Object,tgtOff:Int):Bool { // TODO check if correct, used by interface{} value comparison 726 if((this.length-off)!=(target.len()-tgtOff)) return false; 727 for(i in 0...(this.length-off)) { 728 if((i+off)&3==0){ 729 var a:Dynamic=this.get(i+off); 730 var b:Dynamic=target.get(i+tgtOff); 731 if(!Force.isEqualDynamic(a,b)) return false; 732 } 733 #if fullunsafe 734 if(this.get_uint8(i+off)!=target.get_uint8(i+tgtOff)) 735 return false; 736 #elseif abstractobjects 737 var ths=this.get(i+off); 738 var tgt=target.get(i+tgtOff); 739 if(ths!=tgt) 740 if(ths==null) 741 if(Std.is(tgt,Int)) 742 return cast(tgt,Int)!=0; 743 else 744 if(Std.is(tgt,Float)) 745 return cast(tgt,Float)!=0; 746 else 747 if(Std.is(tgt,Bool)) 748 return cast(tgt,Bool)!=false; 749 else 750 return false; 751 else 752 if(tgt==null) 753 if(Std.is(ths,Int)) 754 return cast(ths,Int)!=0; 755 else 756 if(Std.is(ths,Float)) 757 return cast(ths,Float)!=0; 758 else 759 if(Std.is(ths,Bool)) 760 return cast(ths,Bool)!=false; 761 else 762 return false; 763 else 764 return false; 765 #else 766 if(this.get_uint32(i+off)!=target.get_uint32(i+tgtOff)) 767 return false; 768 #end 769 } 770 return true; 771 } 772 public static function objBlit(src:Object,srcPos:Int,dest:Object,destPos:Int,size:Int):Void{ 773 if(size>0&&src!=null) { 774 ` 775 if l.PogoComp().DebugFlag { 776 objClass += ` 777 #if !abstractobjects 778 if(!Std.is(src,Object)) { 779 Scheduler.panicFromHaxe("Object.objBlt() src parameter is not an Object - Value: "+Std.string(src)+" Type: "+Type.typeof(src)); 780 return; 781 } 782 if(!Std.is(dest,Object)) { 783 Scheduler.panicFromHaxe("Object.objBlt() dest parameter is not an Object - Value: "+Std.string(dest)+" Type: "+Type.typeof(dest)); 784 return; 785 } 786 #end 787 if(srcPos<0 || srcPos>=src.length){ 788 Scheduler.panicFromHaxe("Object.objBlt() srcPos out-of-range - Value= "+Std.string(srcPos)+ 789 " src.length= "+Std.string(src.length)); 790 return; 791 } 792 if(destPos<0 || destPos>=dest.length){ 793 Scheduler.panicFromHaxe("Object.objBlt() destPos out-of-range - Value= "+Std.string(destPos)+ 794 " dest.length= "+Std.string(dest.length)); 795 return; 796 } 797 //if(size>(src.length-srcPos) ) size = src.length-srcPos ; // TODO review why this defensive code is needed 798 if(size<0 || size > (dest.length-destPos) || size>(src.length-srcPos) ) { 799 Scheduler.panicFromHaxe("Object.objBlt() size out-of-range - Value: "+Std.string(size)+ 800 " DestSize: "+Std.string(dest.length-destPos)+ 801 " SrcSize: "+Std.string(src.length-srcPos)); 802 return; 803 } 804 ` 805 } else { 806 /*objClass += ` 807 if(size>(src.length-srcPos) ) size = src.length-srcPos ; // TODO review why this defensive code is needed 808 `*/ 809 } 810 objClass += ` 811 #if fullunsafe //(js && fullunsafe) 812 if((size&3==0)&&(srcPos&3==0)&&(destPos&3==0)) { 813 var i:Int=0; 814 var s:Int=srcPos; 815 var d:Int=destPos; 816 while(i<size){ 817 dest.set_uint32(d,src.get_uint32(s)); 818 dest.set(d,src.get(s)); 819 i+=4; 820 s+=4; 821 d+=4; 822 } 823 } 824 else{ 825 var s:Int=srcPos; 826 var d:Int=destPos; 827 for(i in 0...size) { 828 dest.set_uint8(d,src.get_uint8(s)); 829 if(((size-i)>3)&&(s&3)==0){ 830 dest.set(d,src.get(s)); 831 } 832 s+=1; 833 d+=1; 834 } 835 } 836 #elseif abstractobjects 837 haxe.ds.Vector.blit(src,srcPos, dest, destPos, size); 838 #else //if !fullunsafe 839 if((size>>2)>0) 840 haxe.ds.Vector.blit(src.dVec4,srcPos>>2, dest.dVec4, destPos>>2, size>>2); 841 haxe.ds.Vector.blit(src.iVec,srcPos, dest.iVec, destPos, size); 842 #end 843 } // end of: if(size>0&&src!=null) { 844 } 845 public inline function get_object(size:Int,from:Int):Object { // TODO SubObj class that is effectively a pointer? 846 var so:Object = make(size); 847 objBlit(this,from, so, 0, size); 848 return so; 849 } 850 public inline function set_object(size:Int, to:Int, from:Object):Void { 851 //#if php 852 // if(!Std.is(from,Object)) { 853 // Scheduler.panicFromHaxe("Object.set_object() from parameter is not an Object - Value: "+Std.string(from)+" Type: "+Type.typeof(from)); 854 // return; // treat as null object (seen examples have been integer 0) 855 // } 856 //#end 857 objBlit(from,0,this,to,size); 858 } 859 public inline function copy():Object{ 860 return get_object(len(),0); 861 } 862 public inline function get(i:Int):Dynamic { 863 #if abstractobjects 864 return this[i]; 865 #else 866 return dVec4[i>>2]; 867 #end 868 } 869 public inline function get_bool(i:Int):Bool { 870 #if (js && fullunsafe) 871 return dView.getUint8(i)==0?false:true; 872 #elseif abstractobjects 873 if(this[i]==null) return false; 874 return this[i]; 875 #elseif !fullunsafe 876 var r:Int=iVec[i]; 877 #if (js || php || neko ) 878 return r==null?false:(r==0?false:true); 879 #else 880 return r==0?false:true; 881 #end 882 #else 883 return byts.get(i)==0?false:true; 884 #end 885 } 886 public inline function get_int8(i:Int):Int { 887 #if (js && fullunsafe) 888 return dView.getInt8(i); 889 #elseif abstractobjects 890 #if (js || php || neko ) return this[i]==null?0:0|this[i]; #else return this[i]; #end 891 #elseif !fullunsafe 892 #if ((js || php || neko )&&!nonulltests) return iVec[i]==null?0:0|iVec[i]; #else return iVec[i]; #end 893 #else 894 return Force.toInt8(byts.get(i)); 895 #end 896 } 897 public inline function get_int16(i:Int):Int { 898 #if (js && fullunsafe) 899 return dView.getInt16(i,true); // little-endian 900 #elseif abstractobjects 901 #if (js || php || neko ) return this[i]==null?0:0|this[i]; #else return this[i]; #end 902 #elseif !fullunsafe 903 #if ((js || php || neko )&&!nonulltests) return iVec[i]==null?0:0|iVec[i]; #else return iVec[i]; #end 904 #else 905 return Force.toInt16((get_uint8(i+1)<<8)|get_uint8(i)); // little end 1st 906 #end 907 } 908 public inline function get_int32(i:Int):Int { 909 #if (js && fullunsafe) 910 return dView.getInt32(i,true); // little-endian 911 #elseif abstractobjects 912 #if (js || php || neko ) return this[i]==null?0:0|this[i]; #else return this[i]; #end 913 #elseif !fullunsafe 914 #if ((js || php || neko )&&!nonulltests) return iVec[i]==null?0:0|iVec[i]; #else return iVec[i]; #end 915 #else 916 return Force.toInt32((get_uint16(i+2)<<16)|get_uint16(i)); // little end 1st 917 #end 918 } 919 public inline function get_int64(i:Int):GOint64 { 920 #if !fullunsafe 921 if(get(i)==null) return GOint64.ofInt(0); 922 return get(i); 923 #else 924 return Force.toInt64(GOint64.make(get_uint32(i+4),get_uint32(i))); 925 #end 926 } 927 public inline function get_uint8(i:Int):Int { 928 #if (js && fullunsafe) 929 return dView.getUint8(i); 930 #elseif abstractobjects 931 #if (js || php || neko ) return this[i]==null?0:0|this[i]; #else return this[i]; #end 932 #elseif !fullunsafe 933 #if ((js || php || neko )&&!nonulltests) return iVec[i]==null?0:0|iVec[i]; #else return iVec[i]; #end 934 #else 935 return Force.toUint8(byts.get(i)); 936 #end 937 } 938 public inline function get_uint16(i:Int):Int { 939 #if (js && fullunsafe) 940 return dView.getUint16(i,true); // little-endian 941 #elseif abstractobjects 942 #if (js || php || neko ) return this[i]==null?0:0|this[i]; #else return this[i]; #end 943 #elseif !fullunsafe 944 #if ((js || php || neko )&&!nonulltests) return iVec[i]==null?0:0|iVec[i]; #else return iVec[i]; #end 945 #else 946 return Force.toUint16((get_uint8(i+1)<<8)|get_uint8(i)); // little end 1st 947 #end 948 } 949 public inline function get_uint32(i:Int):Int { 950 #if (js && fullunsafe) 951 return dView.getUint32(i,true); // little-endian 952 #elseif abstractobjects 953 #if (js || php || neko ) return this[i]==null?0:0|this[i]; #else return this[i]; #end 954 #elseif !fullunsafe 955 #if ((js || php || neko )&&!nonulltests) return iVec[i]==null?0:0|iVec[i]; #else return iVec[i]; #end 956 #else 957 return Force.toUint32((get_uint16(i+2)<<16)|get_uint16(i)); // little end 1st 958 #end 959 } 960 public inline function get_uint64(i:Int):GOint64 { 961 #if !fullunsafe 962 if(get(i)==null) return GOint64.ofInt(0); 963 return get(i); 964 #else 965 return Force.toUint64(GOint64.make(get_uint32(i+4),get_uint32(i))); 966 #end 967 } 968 public inline function get_uintptr(i:Int):Dynamic { // uintptr holds Haxe objects 969 // TODO consider some type of read-from-mem if Dynamic type is Int 970 return get(i); 971 } 972 public inline function get_float32(i:Int):Float { 973 #if (js && fullunsafe) 974 return dView.getFloat32(i,true); // little-endian 975 #elseif !fullunsafe 976 return get(i)==null?0.0:get(i); 977 #else 978 return byts.getFloat(i); // Go_haxegoruntime_FFloat32frombits.callFromRT(0,get_uint32(i)); 979 #end 980 } 981 public inline function get_float64(i:Int):Float { 982 #if (js && fullunsafe) 983 return dView.getFloat64(i,true); // little-endian 984 #elseif !fullunsafe 985 return get(i)==null?0.0:get(i); 986 #else 987 return byts.getDouble(i); // Go_haxegoruntime_FFloat64frombits.callFromRT(0,get_uint64(i)); 988 #end 989 } 990 public inline function get_complex64(i:Int):Complex { 991 // TODO optimize for dataview & unsafe 992 var r:Complex=get(i); 993 return r==null?new Complex(0.0,0.0):r; 994 } 995 public inline function get_complex128(i:Int):Complex { 996 // TODO optimize for dataview & unsafe 997 var r:Complex=get(i); 998 return r==null?new Complex(0.0,0.0):r; 999 } 1000 public inline function get_string(i:Int):String { 1001 var r=get(i); 1002 return r==null?"":Std.string(r); 1003 } 1004 public inline function set(i:Int,v:Dynamic):Void { 1005 #if abstractobjects 1006 this[i]=v; 1007 #else 1008 dVec4[i>>2]=v; 1009 #end 1010 } 1011 public inline function set_bool(i:Int,v:Bool):Void { 1012 #if (js && fullunsafe) 1013 dView.setUint8(i,v?1:0); 1014 #elseif abstractobjects 1015 set(i,v);//this[i]=v?1:null; 1016 #elseif !fullunsafe 1017 iVec[i]=v?1:0; 1018 #if ((js || php || neko ) &&!nonulltests) 1019 if(iVec[i]==0) iVec[i]=null; 1020 #end 1021 #else 1022 byts.set(i,v?1:0); 1023 #end 1024 } 1025 public inline function set_int8(i:Int,v:Int):Void { 1026 #if (js && fullunsafe) 1027 dView.setInt8(i,v); 1028 #elseif abstractobjects 1029 set(i,v);//this[i]=v==0?null:v; 1030 #elseif !fullunsafe 1031 iVec[i]=v; 1032 #if ((js || php || neko ) &&!nonulltests) 1033 if(iVec[i]==0) iVec[i]=null; 1034 #end 1035 #else 1036 byts.set(i,v&0xff); 1037 #end 1038 } 1039 public inline function set_int16(i:Int,v:Int):Void { 1040 #if (js && fullunsafe) 1041 dView.setInt16(i,v,true); // little-endian 1042 #elseif abstractobjects 1043 set(i,v);//this[i]=v==0?null:v; 1044 #elseif !fullunsafe 1045 iVec[i]=v; 1046 #if ((js || php || neko ) &&!nonulltests) 1047 if(iVec[i]==0) iVec[i]=null; 1048 #end 1049 #else 1050 set_int8(i,v); 1051 set_int8(i+1,v>>8); 1052 #end 1053 } 1054 public inline function set_int32(i:Int,v:Int):Void { 1055 #if (js && fullunsafe) 1056 dView.setInt32(i,v,true); // little-endian 1057 #elseif abstractobjects 1058 set(i,v);//this[i]=v==0?null:v; 1059 #elseif !fullunsafe 1060 #if ((js || php || neko ) &&!nonulltests) 1061 iVec[i]=v==0?null:v; 1062 #else 1063 iVec[i]=v; 1064 #end 1065 #else 1066 set_int16(i,v); 1067 set_int16(i+2,v>>16); 1068 #end 1069 } 1070 public inline function set_int64(i:Int,v:GOint64):Void { 1071 #if !fullunsafe 1072 if(GOint64.isZero(v)) set(i,null); 1073 else set(i,v); 1074 #else 1075 set_uint32(i,GOint64.getLow(v)); 1076 set_uint32(i+4,GOint64.getHigh(v)); 1077 #end 1078 } 1079 public inline function set_uint8(i:Int,v:Int):Void { 1080 #if (js && fullunsafe) 1081 dView.setUint8(i,v); 1082 #elseif abstractobjects 1083 set(i,v);//this[i]=v==0?null:v; 1084 #elseif !fullunsafe 1085 iVec[i]=v; 1086 #if ((js || php || neko ) &&!nonulltests) 1087 if(iVec[i]==0) iVec[i]=null; 1088 #end 1089 #else 1090 byts.set(i,v&0xff); 1091 #end 1092 } 1093 public inline function set_uint16(i:Int,v:Int):Void { 1094 #if (js && fullunsafe) 1095 dView.setUint16(i,v,true); // little-endian 1096 #elseif abstractobjects 1097 set(i,v);//this[i]=v==0?null:v; 1098 #elseif !fullunsafe 1099 iVec[i]=v; 1100 #if ((js || php || neko ) &&!nonulltests) 1101 if(iVec[i]==0) iVec[i]=null; 1102 #end 1103 #else 1104 set_uint8(i,v); 1105 set_uint8(i+1,v>>8); 1106 #end 1107 } 1108 public inline function set_uint32(i:Int,v:Int):Void { 1109 #if (js && fullunsafe) 1110 dView.setUint32(i,v,true); // little-endian 1111 #elseif abstractobjects 1112 set(i,v);//this[i]=v==0?null:v; 1113 #elseif !fullunsafe 1114 iVec[i]=v; 1115 #if ((js || php || neko ) &&!nonulltests) 1116 if(iVec[i]==0) iVec[i]=null; 1117 #end 1118 #else 1119 set_uint16(i,v); 1120 set_uint16(i+2,v>>16); 1121 #end 1122 } 1123 public inline function set_uint64(i:Int,v:GOint64):Void { 1124 #if !fullunsafe 1125 if(GOint64.isZero(v)) set(i,null); 1126 else set(i,v); 1127 #else 1128 set_uint32(i,GOint64.getLow(v)); 1129 set_uint32(i+4,GOint64.getHigh(v)); 1130 #end 1131 } 1132 public inline function set_uintptr(i:Int,v:Dynamic):Void { 1133 if(Std.is(v,Int)) { 1134 set(i,Force.toUint32(v)); // make sure we only store 32 bits if int 1135 #if !abstractobjects 1136 set_uint32(i,v); // also write through to ordinary memory if the type is Int 1137 #end 1138 return; 1139 } 1140 set(i,v); 1141 #if !abstractobjects 1142 set_uint32(i,0); // value overwritten 1143 #end 1144 } 1145 public static var MinFloat64:Float = -1.797693134862315708145274237317043567981e+308; // 2**1023 * (2**53 - 1) / 2**52 1146 public inline function set_float32(i:Int,v:Float):Void { 1147 #if (js && fullunsafe) 1148 dView.setFloat32(i,v,true); // little-endian 1149 #elseif !fullunsafe 1150 v=Force.toFloat32(v); 1151 #if (js || php || neko ) 1152 if(v==0.0) { 1153 #if !php 1154 var t:Float=1/v; // result is +/- infinity 1155 if(t>MinFloat64) // ie not -0 1156 #end 1157 v=null; 1158 } 1159 #end 1160 set(i,v); 1161 #else 1162 #if (cpp||neko) 1163 byts.setFloat(i,v); 1164 #else 1165 set_uint32(i,Go_haxegoruntime_FFloat32bits.callFromRT(0,v)); 1166 #end 1167 #end 1168 } 1169 public inline function set_float64(i:Int,v:Float):Void { 1170 #if (js && fullunsafe) 1171 dView.setFloat64(i,v,true); // little-endian 1172 #elseif !fullunsafe 1173 #if (js || php || neko ) 1174 if(v==0.0) { 1175 #if !php 1176 var t:Float=1/v; // result is +/- infinity 1177 if(t>MinFloat64) // ie not -0 1178 #end 1179 v=null; 1180 } 1181 #end 1182 set(i,v); 1183 #else 1184 #if (cpp||neko) 1185 byts.setDouble(i,v); 1186 #else 1187 set_uint64(i,Go_haxegoruntime_FFloat64bits.callFromRT(0,v)); 1188 #end 1189 #end 1190 } 1191 1192 public inline function set_complex64(i:Int,v:Complex):Void { 1193 if(v.real==0 && v.imag==0) set(i,null); 1194 else set(i,v); // TODO review 1195 } 1196 public inline function set_complex128(i:Int,v:Complex):Void { 1197 if(v.real==0 && v.imag==0) set(i,null); 1198 else set(i,v); // TODO review 1199 } 1200 public inline function set_string(i:Int,v:String):Void { 1201 if(v=="") set(i,null); 1202 else set(i,v); 1203 } 1204 private static function str(v:Dynamic):String{ 1205 return v==null?"nil":Std.is(v,Pointer)?v.toUniqueVal():Std.string(v); 1206 } 1207 public function toString(addr:Int=0,count:Int=-1):String{ 1208 if(count==-1) count=this.length; 1209 if(addr<0) addr=0; 1210 if(count<0 || count>(this.length-addr)) count = this.length-addr; 1211 var ret:String = "{"; 1212 for(i in 0...count){ 1213 if(i>0) ret = ret + ","; 1214 #if abstractobjects 1215 ret += str(get(addr)); 1216 #else 1217 if((addr)&3==0) ret += str(get(addr)); 1218 ret = ret+"<"+Std.string(get_uint8(addr))+">"; 1219 #end 1220 addr = addr+1; 1221 } 1222 return ret+"}"; 1223 } 1224 } 1225 ` 1226 l.PogoComp().WriteAsClass("Object", objClass) 1227 1228 ptrClass := ` 1229 @:keep 1230 class Pointer { 1231 public var obj:Object; // reference to the object holding the value 1232 public var off:Int; // the offset into the object, if any 1233 ` 1234 if l.PogoComp().DebugFlag { 1235 ptrClass += ` 1236 public function new(from:Object,offset:Int){ 1237 if(from==null) Scheduler.panicFromHaxe("attempt to make a new Pointer from a nil object"); 1238 ` 1239 } else { 1240 ptrClass += ` 1241 public #if inlinepointers inline #end function new(from:Object,offset:Int){ 1242 ` 1243 } 1244 ptrClass += ` obj = from; 1245 off = offset; 1246 } 1247 public function len(){ 1248 if(obj==null) return 0; 1249 return obj.len()-off; 1250 } 1251 public function hashInt():Int { 1252 var ur:Int=obj.uniqueRef(); 1253 var r = ((ur&0xffff)<<16) | (off&0xffff); // hash value for a pointer 1254 //trace("DEBUG Pointer.hashInt="+Std.string(r)+" this="+this.toUniqueVal()); 1255 return r; 1256 } 1257 ` 1258 if l.PogoComp().DebugFlag { 1259 ptrClass += ` public static function check(p:Dynamic):Pointer { 1260 if(p==null) { 1261 Scheduler.panicFromHaxe("nil pointer de-reference"); 1262 return null; 1263 } 1264 if(Std.is(p,Pointer)) return p; 1265 if(Std.is(p,Int)) 1266 Scheduler.panicFromHaxe("TARDISgo/Haxe implementation cannot convert from uintptr to pointer"); 1267 Scheduler.panicFromHaxe("non-Pointer cannot be used as a pointer"); 1268 return null; 1269 } 1270 ` 1271 } else { // TODO null test could be removed in some future NoChecking mode maybe? 1272 ptrClass += ` public inline static function check(p:Pointer):Pointer { 1273 return p; 1274 }` 1275 } 1276 l.PogoComp().WriteAsClass("Pointer", ptrClass+ 1277 ` public static function isEqual(p1:Pointer,p2:Pointer):Bool { 1278 if(p1==p2) return true; // simple case of being the same haxe object 1279 if(p1==null || p2==null) return false; // one of them is null (if above handles both null) 1280 if(p1.obj.uniqueRef()==p2.obj.uniqueRef() && p1.off==p2.off) return true; // point to same object & offset 1281 return false; 1282 } 1283 public static inline function make(from:Object):Pointer { 1284 return new Pointer(from,0); 1285 } 1286 public #if inlinepointers inline #end function addr(byteOffset:Int):Pointer { 1287 return byteOffset==0?this:new Pointer(obj,off+byteOffset); 1288 } 1289 public inline function fieldAddr(byteOffset:Int):Pointer { 1290 return addr(byteOffset); 1291 } 1292 public inline function copy():Pointer { 1293 return this; 1294 } 1295 public #if inlinepointers inline #end function load_object(sz:Int):Object { 1296 return obj.get_object(sz,off); 1297 } 1298 public #if inlinepointers inline #end function load():Dynamic { 1299 return obj.get(off); 1300 } 1301 public #if inlinepointers inline #end function load_bool():Bool { 1302 return obj.get_bool(off); 1303 } 1304 public #if inlinepointers inline #end function load_int8():Int { 1305 return obj.get_int8(off); 1306 } 1307 public #if inlinepointers inline #end function load_int16():Int { 1308 return obj.get_int16(off); 1309 } 1310 public #if inlinepointers inline #end function load_int32():Int { 1311 return obj.get_int32(off); 1312 } 1313 public #if inlinepointers inline #end function load_int64():GOint64 { 1314 return obj.get_int64(off); 1315 } 1316 public #if inlinepointers inline #end function load_uint8():Int { 1317 return obj.get_uint8(off); 1318 } 1319 public #if inlinepointers inline #end function load_uint16():Int { 1320 return obj.get_uint16(off); 1321 } 1322 public #if inlinepointers inline #end function load_uint32():Int { 1323 return obj.get_uint32(off); 1324 } 1325 public #if inlinepointers inline #end function load_uint64():GOint64 { 1326 return obj.get_uint64(off); 1327 } 1328 public #if inlinepointers inline #end function load_uintptr():Dynamic { 1329 return obj.get_uintptr(off); 1330 } 1331 public #if inlinepointers inline #end function load_float32():Float { 1332 return obj.get_float32(off); 1333 } 1334 public #if inlinepointers inline #end function load_float64():Float { 1335 return obj.get_float64(off); 1336 } 1337 public #if inlinepointers inline #end function load_complex64():Complex { 1338 return obj.get_complex64(off); 1339 } 1340 public #if inlinepointers inline #end function load_complex128():Complex { 1341 return obj.get_complex128(off); 1342 } 1343 public #if inlinepointers inline #end function load_string():String { 1344 return obj.get_string(off); 1345 } 1346 public #if inlinepointers inline #end function store_object(sz:Int,v:Object):Void { 1347 obj.set_object(sz,off,v); 1348 } 1349 public #if inlinepointers inline #end function store(v:Dynamic):Void { 1350 obj.set(off,v); 1351 } 1352 public #if inlinepointers inline #end function store_bool(v:Bool):Void { obj.set_bool(off,v); } 1353 public #if inlinepointers inline #end function store_int8(v:Int):Void { obj.set_int8(off,v); } 1354 public #if inlinepointers inline #end function store_int16(v:Int):Void { obj.set_int16(off,v); } 1355 public #if inlinepointers inline #end function store_int32(v:Int):Void { obj.set_int32(off,v); } 1356 public #if inlinepointers inline #end function store_int64(v:GOint64):Void { obj.set_int64(off,v); } 1357 public #if inlinepointers inline #end function store_uint8(v:Int):Void { obj.set_uint8(off,v); } 1358 public #if inlinepointers inline #end function store_uint16(v:Int):Void { obj.set_uint16(off,v); } 1359 public #if inlinepointers inline #end function store_uint32(v:Int):Void { obj.set_uint32(off,v); } 1360 public #if inlinepointers inline #end function store_uint64(v:GOint64):Void { obj.set_uint64(off,v); } 1361 public #if inlinepointers inline #end function store_uintptr(v:Dynamic):Void { obj.set_uintptr(off,v); } 1362 public #if inlinepointers inline #end function store_float32(v:Float):Void { obj.set_float32(off,v); } 1363 public #if inlinepointers inline #end function store_float64(v:Float):Void { obj.set_float64(off,v); } 1364 public #if inlinepointers inline #end function store_complex64(v:Complex):Void { obj.set_complex64(off,v); } 1365 public #if inlinepointers inline #end function store_complex128(v:Complex):Void { obj.set_complex128(off,v); } 1366 public #if inlinepointers inline #end function store_string(v:String):Void { obj.set_string(off,v); } 1367 public #if inlinepointers inline #end function toString(sz:Int=-1):String { 1368 return " &{ "+obj.toString(off,sz)+" } "; 1369 } 1370 public function toUniqueVal():String { 1371 return "&<"+Std.string(obj.uniqueRef())+":"+Std.string(off)+">"; 1372 } 1373 } 1374 `) 1375 sliceClass := ` 1376 @:keep 1377 class Slice { 1378 public var baseArray:Pointer; 1379 public var itemSize:Int; // for the size of each item in bytes 1380 private var start:Int; 1381 private var end:Int; 1382 private var capacity:Int; // of the array, in items 1383 1384 public var length:Int; 1385 inline function setLength() { 1386 length=end-start; 1387 } 1388 public static #if inlinepointers inline #end function nullLen(s:Slice):Int{ 1389 if(s==null) return 0; 1390 else return s.length; 1391 } 1392 public function new(fromArray:Pointer, low:Int, high:Int, ularraysz:Int, isz:Int) { 1393 baseArray = fromArray; 1394 itemSize = isz; 1395 if(baseArray==null) { 1396 start = 0; 1397 end = 0; 1398 capacity = 0; 1399 } else { 1400 if( low<0 ) Scheduler.panicFromHaxe( "new Slice() low bound -ve"); 1401 var ulCap = Math.floor(baseArray.len()/itemSize); 1402 if( ulCap < ularraysz) { 1403 ularraysz = ulCap; // ignore the given size & use the actual rather than panic TODO review+tidy 1404 // Scheduler.panicFromHaxe("new Slice() internal error: underlying array capacity="+ulCap+ 1405 // " less than stated slice capacity="+ularraysz); // slices of existing data will have ulCap greater 1406 } 1407 capacity = ularraysz; // the capacity of the array 1408 if(high==-1) high = ularraysz; //default upper bound is the capacity of the underlying array 1409 if( high > ularraysz ) Scheduler.panicFromHaxe("new Slice() high bound exceeds underlying array length"); 1410 if( low>high ) Scheduler.panicFromHaxe("new Slice() low bound exceeds high bound"); 1411 start = low; 1412 end = high; 1413 } 1414 setLength(); 1415 } 1416 public static function fromResource(name:String):Slice { 1417 return fromBytes(haxe.Resource.getBytes(name)); 1418 } 1419 public static function fromBytes(res:haxe.io.Bytes):Slice { 1420 var obj = res==null?Object.make(0):Object.make(res.length,res); 1421 var ptr = Pointer.make(obj); 1422 var ret = new Slice(ptr,0,-1,res==null?0:res.length,1); // []byte 1423 #if nulltempvars 1424 obj=null;ptr=null; 1425 #end 1426 return ret; 1427 } 1428 public static function toBytes(sl:Slice):haxe.io.Bytes { 1429 var wdSz = 4; 1430 var szAdj = (wdSz - (sl.length % wdSz)) % wdSz; // get to a wdSz-byte boundary 1431 var byts = haxe.io.Bytes.alloc(sl.length+szAdj); 1432 for(i in 0...sl.length) 1433 byts.set(i,sl.itemAddr(i).load_uint8()); 1434 return byts; 1435 } 1436 public function subSlice(low:Int, high:Int):Slice { 1437 if(high==-1) high = length; //default upper bound is the length of the current slice 1438 return new Slice(baseArray,low+start,high+start,capacity,itemSize); 1439 } 1440 public static function append(oldEnt:Slice,newEnt:Slice):Slice{ // TODO optimize further - heavily used 1441 if(oldEnt==null && newEnt==null) return null; 1442 if(newEnt==null || newEnt.len()==0) { 1443 return oldEnt; // NOTE not a clone as with the line below 1444 //return new Slice(oldEnt.baseArray.addr(oldEnt.start*oldEnt.itemSize),0,oldEnt.len(),oldEnt.cap(),oldEnt.itemSize); 1445 } 1446 if(oldEnt==null) { // must create a copy rather than just return the new one 1447 oldEnt=new Slice(Pointer.make(Object.make(0)),0,0,0,newEnt.itemSize); // trigger newObj code below 1448 } 1449 if(oldEnt.itemSize!=newEnt.itemSize) 1450 Scheduler.panicFromHaxe("new Slice() internal error: itemSizes do not match"); 1451 if(oldEnt.cap()>=(oldEnt.len()+newEnt.len())){ 1452 var retEnt=new Slice(oldEnt.baseArray,oldEnt.start,oldEnt.end,oldEnt.capacity,oldEnt.itemSize); 1453 var offset=retEnt.len(); 1454 for(i in 0...newEnt.len()){ 1455 retEnt.end++; 1456 //retEnt.itemAddr(offset+i).store_object(oldEnt.itemSize,newEnt.itemAddr(i).load_object(newEnt.itemSize)); 1457 Object.objBlit(newEnt.baseArray.obj,newEnt.itemOff(i)+newEnt.baseArray.off, 1458 retEnt.baseArray.obj,retEnt.itemOff(offset+i)+retEnt.baseArray.off,oldEnt.itemSize); 1459 } 1460 #if nulltempvars 1461 oldEnt=null;newEnt=null; 1462 #end 1463 retEnt.setLength(); 1464 return retEnt; 1465 }else{ 1466 var newLen = oldEnt.length+newEnt.len(); 1467 var newCap = newLen+(newLen>>2); // NOTE auto-create 50pc new capacity 1468 var newObj:Object = Object.make(newCap*oldEnt.itemSize); 1469 for(i in 0...oldEnt.length) { 1470 //newObj.set_object(oldEnt.itemSize,i*oldEnt.itemSize,oldEnt.itemAddr(i).load_object(oldEnt.itemSize)); 1471 Object.objBlit(oldEnt.baseArray.obj,oldEnt.itemOff(i)+oldEnt.baseArray.off, 1472 newObj,i*oldEnt.itemSize,oldEnt.itemSize); 1473 } 1474 for(i in 0...newEnt.len()){ 1475 //newObj.set_object(oldEnt.itemSize, 1476 // oldEnt.length*oldEnt.itemSize+i*oldEnt.itemSize,newEnt.itemAddr(i).load_object(oldEnt.itemSize)); 1477 Object.objBlit(newEnt.baseArray.obj,newEnt.itemOff(i)+newEnt.baseArray.off, 1478 newObj,(oldEnt.length*oldEnt.itemSize)+(i*oldEnt.itemSize),oldEnt.itemSize); 1479 } 1480 var ptr = Pointer.make(newObj); 1481 var ret = new Slice(ptr,0,newLen,newCap,oldEnt.itemSize); 1482 #if nulltempvars 1483 oldEnt=null;newEnt=null;newObj=null;ptr=null; 1484 #end 1485 ret.setLength(); 1486 return ret; 1487 } 1488 } 1489 public static function copy(target:Slice,source:Slice):Int{ 1490 if(target==null) return 0; 1491 if(source==null) return 0; 1492 var copySize:Int=target.len(); 1493 if(source.len()<copySize) 1494 copySize=source.len(); 1495 if(copySize==0) return 0; 1496 // Optimise not to create any temporary objects 1497 if(target.baseArray==source.baseArray){ // copy within the same slice 1498 if(target.start<=source.start){ 1499 for(i in 0...copySize){ 1500 //target.itemAddr(i).store_object(target.itemSize,source.itemAddr(i).load_object(target.itemSize)); 1501 Object.objBlit(source.baseArray.obj,source.itemOff(i)+source.baseArray.off, 1502 target.baseArray.obj,target.itemOff(i)+target.baseArray.off, 1503 target.itemSize); 1504 } 1505 }else{ 1506 var i = copySize-1; 1507 while(i>=0){ 1508 //target.itemAddr(i).store_object(target.itemSize,source.itemAddr(i).load_object(target.itemSize)); 1509 Object.objBlit(source.baseArray.obj,source.itemOff(i)+source.baseArray.off, 1510 target.baseArray.obj,target.itemOff(i)+target.baseArray.off, 1511 target.itemSize); 1512 i-=1; 1513 } 1514 } 1515 }else{ 1516 for(i in 0...copySize){ 1517 //target.itemAddr(i).store_object(target.itemSize,source.itemAddr(i).load_object(target.itemSize)); 1518 Object.objBlit(source.baseArray.obj,source.itemOff(i)+source.baseArray.off, 1519 target.baseArray.obj,target.itemOff(i)+target.baseArray.off, 1520 target.itemSize); 1521 } 1522 } 1523 target.setLength(); 1524 return copySize; 1525 } 1526 public function param(idx:Int):Dynamic { // special case for .hx pseudo functions 1527 var ptr=itemAddr(idx); 1528 var ret=ptr.load(); 1529 #if nulltempvars 1530 ptr=null; 1531 #end 1532 return ret; 1533 } 1534 //public inline function getAt(idx:Int):Dynamic { 1535 // //if (idx<0 || idx>=(end-start)) Scheduler.panicFromHaxe("Slice index out of range for getAt()"); 1536 // return baseArray.addr(idx+start).load(); 1537 //} 1538 //public inline function setAt(idx:Int,v:Dynamic) { 1539 // //if (idx<0 || idx>=(end-start)) Scheduler.panicFromHaxe("Slice index out of range for setAt()"); 1540 // baseArray.addr(idx+start).store(v); // this code relies on the object reference passing back 1541 //} 1542 public function len():Int { 1543 if(length!=end-start) Scheduler.panicFromHaxe("Slice internal error: length!=end-start"); 1544 return length; 1545 } 1546 public function setLen(n:Int) { 1547 if(n<0||n>this.cap()) Scheduler.panicFromHaxe("Slice setLen invalid:"+n); 1548 end = start+n; 1549 setLength(); 1550 } 1551 public function cap():Int { 1552 // TODO remove null and capacity test when stable 1553 if(baseArray==null){ 1554 if(capacity!=0) Scheduler.panicFromHaxe("Slice interal error: BaseArray==null but capacity="+capacity); 1555 }else{ 1556 var ulCap = Math.floor(baseArray.len()/itemSize); 1557 if(capacity>ulCap) // slices of existing data will have ulCap greater 1558 Scheduler.panicFromHaxe("Slice interal error: capacity="+capacity+" but underlying capacity="+ulCap); 1559 } 1560 return capacity-start; 1561 } 1562 ` 1563 if l.PogoComp().DebugFlag { // Normal range checking should cover this, so only in debug mode 1564 sliceClass += ` 1565 public function itemAddr(idx:Int):Pointer { 1566 if (idx<0 || idx>=len()) 1567 Scheduler.panicFromHaxe( 1568 "Slice index "+Std.string(idx)+" out of range 0 <= index < "+Std.string(len())+ 1569 "\nSlice itemSize,capacity,start,end,baseArray: "+ 1570 Std.string(itemSize)+","+Std.string(capacity)+","+ 1571 Std.string(start)+","+Std.string(end)+","+Std.string(baseArray)); 1572 ` 1573 } else { // TODO should this function be inline? 1574 sliceClass += ` 1575 public #if inlinepointers inline #end function itemAddr(idx:Int):Pointer { 1576 ` 1577 } 1578 sliceClass += ` 1579 return new Pointer(baseArray.obj,baseArray.off+itemOff(idx)); 1580 } 1581 public inline function itemOff(idx:Int):Int { 1582 return (idx+start)*itemSize; 1583 } 1584 public function toString():String { 1585 var ret:String = "Slice{["; 1586 var ptr:Pointer; 1587 if(baseArray!=null) 1588 for(i in start...end) { 1589 if(i!=start) ret += ","; 1590 ptr=baseArray.addr(i*itemSize); 1591 ret+=ptr.toString(itemSize); // only works for basic types 1592 } 1593 #if nulltempvars 1594 ptr=null; 1595 #end 1596 return ret+"]}"; 1597 } 1598 } 1599 ` 1600 l.PogoComp().WriteAsClass("Slice", sliceClass) 1601 l.PogoComp().WriteAsClass("Closure", ` 1602 1603 @:keep 1604 class Closure { // "closure" is a keyword in PHP but solved using compiler flag --php-prefix go //TODO tidy names 1605 public var fn:Dynamic; 1606 public var bds:Array<Dynamic>; 1607 1608 public function new(f:Dynamic,b:Array<Dynamic>) { 1609 if(Std.is(f,Closure)) { 1610 if(!Reflect.isFunction(f.fn)) Scheduler.panicFromHaxe( "invalid function reference in existing Closure passed to make Closure(): "+f.fn); 1611 fn=f.fn; 1612 } else{ 1613 if(!Reflect.isFunction(f)) Scheduler.panicFromHaxe("invalid function reference passed to make Closure(): "+f); 1614 fn=f; 1615 } 1616 if(fn==null) Scheduler.panicFromHaxe("new Closure() function has become null!"); // error test for flash/cpp TODO remove when issue resolved 1617 bds=b; 1618 } 1619 public function toString():String { 1620 var ret:String = "Closure{"+fn+","; 1621 if(bds!=null) 1622 for(i in 0...bds.length) { 1623 if(i!=0) ret += ","; 1624 ret+= bds[i]; 1625 } 1626 return ret+"}"; 1627 } 1628 public function methVal(t:Dynamic,v:Dynamic):Dynamic{ 1629 return Reflect.callMethod(null, fn, [[],t,v]); 1630 } 1631 public static function callFn(cl:Closure,params:Dynamic):Dynamic { 1632 if(cl==null) { 1633 Scheduler.panicFromHaxe("attempt to call via null closure in Closure.callFn()"); 1634 return null; 1635 } 1636 if(cl.fn==null) { 1637 Scheduler.panicFromHaxe("attempt to call null function reference in Closure.callFn()"); 1638 return null; 1639 } 1640 if(!Reflect.isFunction(cl.fn)) { 1641 Scheduler.panicFromHaxe("invalid function reference in Closure(): "+cl.fn); 1642 return null; 1643 } 1644 return Reflect.callMethod(null, cl.fn, params); 1645 } 1646 // This technique is used to create callback functions 1647 public function buildCallbackFn():Dynamic { 1648 //trace("buildCallbackFn"); 1649 function bcf(params:Array<Dynamic>):Dynamic { 1650 //trace("bcf"); 1651 if(!Go.doneInit) Go.init(); 1652 params.insert(0,bds); // the variables bound in the closure (at final index 1) 1653 params.insert(0,0); // use goroutine 0 (at final index 0) 1654 var SF:StackFrame=Reflect.callMethod(null, fn, params); 1655 while(SF._incomplete) Scheduler.runAll(); 1656 return SF.res(); 1657 } 1658 return Reflect.makeVarArgs(bcf); 1659 } 1660 } 1661 `) 1662 l.PogoComp().WriteAsClass("Interface", ` 1663 1664 class Interface { // "interface" is a keyword in PHP but solved using compiler flag --php-prefix tgo //TODO tidy names 1665 public var typ:Int; // the possibly interface type that has been cast to 1666 public var val:Dynamic; 1667 1668 public inline function new(t:Int,v:Dynamic){ 1669 typ=t; 1670 val=v; 1671 } 1672 public function toString():String { 1673 var nam:String; 1674 #if (js || neko || php) 1675 if(typ==null) 1676 nam="nil"; 1677 else 1678 nam=TypeInfo.getName(typ); 1679 #else 1680 nam=TypeInfo.getName(typ); 1681 #end 1682 if(val==null) 1683 return "Interface{nil:"+nam+"}"; 1684 else 1685 if(Std.is(val,Pointer)) 1686 return "interface{"+val.toUniqueVal()+":"+nam+"}"; // To stop recursion 1687 else 1688 return "Interface{"+Std.string(val)+":"+nam+"}"; 1689 } 1690 public static function toDynamic(v:Interface):Dynamic { 1691 if(v==null) 1692 return null; 1693 return v.val; 1694 } 1695 public static function fromDynamic(v:Dynamic):Interface { 1696 if(v==null) 1697 return null; 1698 if(Std.is(v,Bool)) 1699 return new Interface(TypeInfo.getId("bool"),v); 1700 if(Std.is(v,Int)) 1701 return new Interface(TypeInfo.getId("int"),v); 1702 if(Std.is(v,Float)) 1703 return new Interface(TypeInfo.getId("float64"),v); 1704 if(Std.is(v,String)) 1705 return new Interface(TypeInfo.getId("string"),v); 1706 // TODO consider testing for other types here? 1707 return new Interface(TypeInfo.getId("uintptr"),v); 1708 } 1709 public static function change(t:Int,i:Interface):Interface { 1710 if(i==null) 1711 if(TypeInfo.isConcrete(t)) 1712 return new Interface(t,TypeZero.zeroValue(t)); 1713 else { 1714 return null; // e.g. error(nil) 1715 } 1716 else 1717 if(Std.is(i,Interface)) 1718 if(TypeInfo.isConcrete(t)) 1719 return new Interface(t,i.val); 1720 else 1721 return new Interface(i.typ,i.val); // do not allow non-concrete types for Interfaces 1722 else { 1723 Scheduler.panicFromHaxe( "Can't change the Interface of a non-Interface type:"+i+" to: "+TypeInfo.getName(t)); 1724 return new Interface(t,TypeZero.zeroValue(t)); //dummy value as we have hit the panic button 1725 } 1726 } 1727 public static function isEqual(a:Interface,b:Interface):Bool { 1728 // TODO ensure this very wide definition of equality is OK 1729 // TODO is another special case required for Slice/Object? 1730 if(a==null) 1731 if(b==null) return true; 1732 else return false; 1733 if(b==null) 1734 return false; 1735 if(! (TypeInfo.isIdentical(a.typ,b.typ)||TypeAssign.isAssignableTo(a.typ,b.typ)||TypeAssign.isAssignableTo(b.typ,a.typ)) ) 1736 return false; 1737 return Force.isEqualDynamic(a.val,b.val); 1738 } 1739 /* from the SSA documentation: 1740 If AssertedType is a concrete type, TypeAssert checks whether the dynamic type in Interface X is equal to it, and if so, 1741 the result of the conversion is a copy of the value in the Interface. 1742 If AssertedType is an Interface, TypeAssert checks whether the dynamic type of the Interface is assignable to it, 1743 and if so, the result of the conversion is a copy of the Interface value X. If AssertedType is a superInterface of X.Type(), 1744 the operation will fail iff the operand is nil. (Contrast with ChangeInterface, which performs no nil-check.) 1745 */ 1746 public static function assert(assTyp:Int,ifce:Interface):Dynamic{ 1747 // TODO add code to deal with overloaded types? i.e. those created by reflect 1748 if(ifce==null) { 1749 Scheduler.panicFromHaxe( "Interface.assert null Interface"); 1750 } else { 1751 if(TypeInfo.isConcrete(assTyp)) { 1752 if(ifce.typ==assTyp) 1753 return ifce.val; 1754 else 1755 Scheduler.panicFromHaxe( "concrete type assert failed: expected "+TypeInfo.getName(assTyp)+", got "+TypeInfo.getName(ifce.typ) ); 1756 } else { 1757 if(assertCache(ifce.typ,assTyp) /*ifce.typ==assTyp||Go_haxegoruntime_assertableTTo.callFromRT(0,ifce.typ,assTyp)*/ ){ 1758 //was:TypeAssert.assertableTo(ifce.typ,assTyp)){ 1759 return new Interface(ifce.typ,ifce.val); 1760 } else { 1761 Scheduler.panicFromHaxe( "interface type assert failed: cannot assert to "+TypeInfo.getName(assTyp)+" from "+TypeInfo.getName(ifce.typ) ); 1762 } 1763 } 1764 } 1765 return null; 1766 } 1767 public static function assertOk(assTyp:Int,ifce:Interface):{r0:Dynamic,r1:Bool} { 1768 if(ifce==null) 1769 return {r0:TypeZero.zeroValue(assTyp),r1:false}; 1770 if(!assertCache(ifce.typ,assTyp) /*(ifce.typ==assTyp||Go_haxegoruntime_assertableTTo.callFromRT(0,ifce.typ,assTyp))*/ ) //was:TypeAssert.assertableTo(ifce.typ,assTyp))) 1771 return {r0:TypeZero.zeroValue(assTyp),r1:false}; 1772 if(TypeInfo.isConcrete(assTyp)) 1773 return {r0:ifce.val,r1:true}; 1774 else 1775 return {r0:new Interface(ifce.typ,ifce.val),r1:true}; 1776 } 1777 static var assertCacheMap = new Map<Int,Bool>(); 1778 public static function assertCache(ifceTyp:Int,assTyp:Int):Bool { 1779 var key:Int= Force.toUint16(ifceTyp)<<16 | Force.toUint16(assTyp) ; // more than 65k types and we hava a problem... 1780 var ret:Bool; 1781 if(assertCacheMap.exists(key)){ 1782 ret=assertCacheMap.get(key); 1783 }else{ 1784 ret=(ifceTyp==assTyp||Go_haxegoruntime_assertableTTo.callFromRT(0,ifceTyp,assTyp)); 1785 assertCacheMap.set(key,ret); 1786 } 1787 return ret; 1788 } 1789 static var methodCache = new Map<String,Dynamic>(); 1790 public static function invoke(ifce:Interface,path:String,meth:String,args:Array<Dynamic>):Dynamic { 1791 if(ifce==null) 1792 Scheduler.panicFromHaxe( "Interface.invoke null Interface"); 1793 if(!Std.is(ifce,Interface)) 1794 Scheduler.panicFromHaxe( "Interface.invoke on non-Interface value"); 1795 var key=Std.string(ifce.typ)+":"+path+":"+meth; 1796 var fn:Dynamic=methodCache.get(key); 1797 if(fn==null) { 1798 fn=Go_haxegoruntime_getMMethod.callFromRT(0,ifce.typ,path,meth); //MethodTypeInfo.method(ifce.typ,meth); 1799 methodCache.set(key,fn); 1800 } 1801 var ret=Reflect.callMethod(null, fn, args); 1802 #if nulltempvars 1803 // set created objects to null for GC 1804 key=null; 1805 fn=null; 1806 #end 1807 // return what was asked for 1808 return ret; 1809 } 1810 } 1811 `) 1812 l.PogoComp().WriteAsClass("Channel", ` 1813 1814 class Channel { // NOTE single-threaded implementation, no locking 1815 var entries:Array<Dynamic>; 1816 var max_entries:Int; 1817 var num_entries:Int; 1818 var oldest_entry:Int; 1819 var closed:Bool; 1820 var capa:Int; 1821 var uniqueId:Int; 1822 1823 static var nextId:Int=0; 1824 1825 public function new(how_many_entries:Int) { 1826 capa = how_many_entries; 1827 if(how_many_entries<=0) 1828 how_many_entries=1; 1829 entries = new Array<Dynamic>(); 1830 max_entries = how_many_entries; 1831 oldest_entry = 0; 1832 num_entries = 0; 1833 closed = false; 1834 uniqueId = nextId; 1835 nextId++; 1836 } 1837 public static function hasSpace(ch:Channel):Bool { 1838 if(ch==null) return false; // non-existant channels never have space 1839 if(ch.closed) return false; // closed channels don't have space 1840 return ch.num_entries < ch.max_entries; 1841 } 1842 public function send(source:Dynamic):Bool { 1843 if(closed) 1844 Scheduler.panicFromHaxe( "attempt to send to closed channel"); 1845 if (hasSpace(this)) { 1846 var next_element:Int; 1847 next_element = (oldest_entry + num_entries) % max_entries; 1848 num_entries++; 1849 entries[next_element]=source; 1850 return true; 1851 } 1852 return false; 1853 } 1854 public static function hasNoContents(ch:Channel):Bool { // used by channel read 1855 if (ch==null) return true; // spec: "Receiving from a nil channel blocks forever." 1856 if (ch.closed) return false; // spec: "Receiving from a closed channel always succeeds..." 1857 else return ch.num_entries == 0; 1858 } 1859 public static function hasContents(ch:Channel):Bool { // used by select 1860 if (ch==null) return false; // spec: "Receiving from a nil channel blocks forever." 1861 if (ch.closed) return true; // spec: "Receiving from a closed channel always succeeds..." 1862 return ch.num_entries != 0; 1863 } 1864 public function receive(zero:Dynamic):{r0:Dynamic ,r1:Bool} { 1865 var ret:Dynamic=zero; 1866 if (num_entries > 0) { 1867 ret=entries[oldest_entry]; 1868 oldest_entry = (oldest_entry + 1) % max_entries; 1869 num_entries--; 1870 return {r0:ret,r1:true}; 1871 } else 1872 if(closed) 1873 return {r0:ret,r1:false}; // spec: "Receiving from a closed channel always succeeds, immediately returning the element type's zero value." 1874 else { 1875 Scheduler.panicFromHaxe( "channel receive unreachable code!"); 1876 return {r0:ret,r1:false}; //dummy value as we have hit the panic button 1877 } 1878 } 1879 public inline function len():Int { 1880 return num_entries; 1881 } 1882 public inline function cap():Int { 1883 return capa; // give back the cap we were told 1884 } 1885 public function close() { 1886 if(this==null) Scheduler.panicFromHaxe( "attempt to close a nil channel" ); 1887 closed = true; 1888 } 1889 public function toString():String{ 1890 return "<ChanId:"+Std.string(uniqueId)+">"; 1891 } 1892 } 1893 `) 1894 l.PogoComp().WriteAsClass("Complex", ` 1895 1896 class Complex { 1897 public var real:Float; 1898 public var imag:Float; 1899 public function new(r:Float, i:Float) { 1900 real = r; 1901 imag = i; 1902 } 1903 public static function neg(x:Complex):Complex { 1904 return new Complex(0.0-x.real,0.0-x.imag); 1905 } 1906 public static function add(x:Complex,y:Complex):Complex { 1907 return new Complex(x.real+y.real,x.imag+y.imag); 1908 } 1909 public static function sub(x:Complex,y:Complex):Complex { 1910 return new Complex(x.real-y.real,x.imag-y.imag); 1911 } 1912 public static function mul(x:Complex,y:Complex):Complex { 1913 return new Complex( (x.real * y.real) - (x.imag * y.imag), (x.imag * y.real) + (x.real * y.imag)); 1914 } 1915 public static function div(x:Complex,y:Complex):Complex { 1916 if( (y.real == 0.0) && (y.imag == 0.0) ){ 1917 Scheduler.panicFromHaxe( "complex divide by zero"); 1918 return new Complex(0.0,0.0); //dummy value as we have hit the panic button 1919 } else { 1920 return new Complex( 1921 ((x.real * y.real) + (x.imag * y.imag)) / ((y.real * y.real) + (y.imag * y.imag)) , 1922 ((x.imag * y.real) - (x.real * y.imag)) / ((y.real * y.real) + (y.imag * y.imag)) ); 1923 } 1924 } 1925 public static function eq(x:Complex,y:Complex):Bool { // "==" 1926 return (x.real == y.real) && (x.imag == y.imag); 1927 } 1928 public static function neq(x:Complex,y:Complex):Bool { // "!=" 1929 return (x.real != y.real) || (x.imag != y.imag); 1930 } 1931 public static function toString(x:Complex):String { 1932 return Std.string(x.real)+"+"+Std.string(x.imag)+"i"; 1933 } 1934 } 1935 1936 `) 1937 l.PogoComp().WriteAsClass("GOint64", ` 1938 1939 #if ( neko || cpp || cs || java ) 1940 typedef HaxeInt64Typedef = haxe.Int64; // these implementations are using native types 1941 #else 1942 typedef HaxeInt64Typedef = Int64; // use the copied and modified version of the standard library class below 1943 // TODO revert to haxe.Int64 when the version below (or better) reaches the released libray 1944 #end 1945 1946 // this abstract type to enable correct handling for Go of HaxeInt64Typedef 1947 abstract HaxeInt64abs(HaxeInt64Typedef) 1948 from HaxeInt64Typedef to HaxeInt64Typedef 1949 { 1950 public inline function new(v:HaxeInt64Typedef) this=v; 1951 1952 #if !( neko || cpp || cs || java ) // allow casting to/from haxe.Int64 if using own version 1953 @:from 1954 static public function fromHI64(v:haxe.Int64) { 1955 return HaxeInt64abs.make(v.high,v.low); 1956 } 1957 @:to 1958 public inline function toHI64():haxe.Int64 { 1959 return haxe.Int64.make(Int64.getHigh(this),Int64.getLow(this)); 1960 } 1961 #end 1962 1963 public static inline function getLow(v:HaxeInt64Typedef):Int { 1964 #if ( neko || cpp || cs || java ) 1965 return v.low; 1966 #else 1967 return HaxeInt64Typedef.getLow(v); 1968 #end 1969 } 1970 public static inline function getHigh(v:HaxeInt64Typedef):Int { 1971 #if ( neko || cpp || cs || java ) 1972 return v.high; 1973 #else 1974 return HaxeInt64Typedef.getHigh(v); 1975 #end 1976 } 1977 1978 public static inline function toInt(v:HaxeInt64abs):Int { 1979 return HaxeInt64abs.getLow(v); // NOTE: does not throw an error if value overflows Int 1980 } 1981 public static inline function ofInt(v:Int):HaxeInt64abs { 1982 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(v)); 1983 } 1984 public static inline function ofUInt(v:Int):HaxeInt64abs { 1985 return make(0,v); 1986 } 1987 public static function toFloat(vp:HaxeInt64abs):Float{ // signed int64 to float (TODO auto-cast of Unsigned pos problem) 1988 //TODO native versions for java & cs 1989 var v:HaxeInt64Typedef=vp; 1990 var isNegVal:Bool=false; 1991 if(isNeg(v)) { 1992 if(compare(v,make(0x80000000,0))==0) return -9223372036854775808.0; // most -ve value can't be made +ve 1993 isNegVal=true; 1994 v=neg(v); 1995 } 1996 var ret:Float=toUFloat(v); 1997 if(isNegVal) return -ret; 1998 return ret; 1999 } 2000 public static function toUFloat(vp:HaxeInt64abs):Float{ // unsigned int64 to float 2001 //TODO native versions for java & cs 2002 var v:HaxeInt64Typedef=vp; 2003 var ret:Float=0.0; 2004 var multiplier:Float=1.0; 2005 var one:HaxeInt64abs=make(0,1); 2006 for(i in 0...64) { 2007 if(!isZero(and(v,one))) 2008 ret += multiplier; 2009 multiplier *= 2.0; 2010 v=ushr(v,1); 2011 } 2012 return ret; 2013 } 2014 public static function ofFloat(v:Float):HaxeInt64abs { // float to signed int64 (TODO auto-cast of Unsigned is a posible problem) 2015 //TODO native versions for java & cs 2016 if(v==0.0) return make(0,0); 2017 if(Math.isNaN(v)) return make(0x80000000,0); // largest -ve number is returned by Go in this situation 2018 var isNegVal:Bool=false; 2019 if(v<0.0){ 2020 isNegVal=true; 2021 v = -v; 2022 } 2023 if(v<2147483647.0) { // optimization: if just a small integer, don't do the full conversion code below 2024 if(isNegVal) return new HaxeInt64abs(HaxeInt64Typedef.neg(HaxeInt64Typedef.ofInt(Math.floor(v)))); // ceil? 2025 else return new HaxeInt64abs(HaxeInt64Typedef.ofInt(Math.floor(v))); 2026 } 2027 if(v>9223372036854775807.0) { // number too big to encode in 63 bits 2028 if(isNegVal) return new HaxeInt64abs(HaxeInt64Typedef.make(0x80000000,0)); // largest -ve number 2029 else return new HaxeInt64abs(HaxeInt64Typedef.make(0x7fffffff,0xffffffff)); // largest +ve number 2030 } 2031 var res:HaxeInt64Typedef = ofUFloat(v); 2032 if(isNegVal) return new HaxeInt64abs(HaxeInt64Typedef.neg(res)); 2033 return new HaxeInt64abs(res); 2034 } 2035 public static function ofUFloat(v:Float):HaxeInt64abs { // float to un-signed int64 2036 //TODO native versions for java & cs 2037 if(v<0.0){ 2038 //Scheduler.panicFromHaxe("-ve value passed to internal haxe function ofUFloat()"); 2039 return make(0,0); // -ve values are invalid here, so return 0 2040 } 2041 if(Math.isNaN(v)) return make(0x80000000,0); // largest -ve number is returned by Go in this situation 2042 if(v<2147483647.0) { // optimization: if just a small integer, don't do the full conversion code below 2043 return ofInt(Math.floor(v)); 2044 } 2045 if(v>18446744073709551615.0) { // number too big to encode in 64 bits 2046 return new HaxeInt64abs(HaxeInt64Typedef.make(0xffffffff,0xffffffff)); // largest unsigned number 2047 } 2048 var f32:Float = 4294967296.0 ; // the number of combinations in 32-bits 2049 var f16:Float = 65536.0; // the number of combinations in 16-bits 2050 v = Math.ffloor(v); // remove any fractional part 2051 var high:Float = Math.ffloor(v/f32); 2052 var highTop16:Float = Math.ffloor(high/f16); 2053 var highBot16:Float = high-(highTop16*f16); 2054 var highBits:Int = Math.floor(highTop16)<<16 | Math.floor(highBot16); 2055 var low:Float = v-(high*f32); 2056 var lowTop16:Float = Math.ffloor(low/f16); 2057 var lowBot16:Float = low-(lowTop16*f16); 2058 var lowBits:Int = Math.floor(lowTop16)<<16 | Math.floor(lowBot16); 2059 return HaxeInt64Typedef.make(highBits,lowBits); 2060 } 2061 public static #if !(cs||java) inline #end function make(h:Int,l:Int):HaxeInt64abs { 2062 // NOTE cs & java have problems inlining the '0xffffffffL' constant 2063 return new HaxeInt64abs(HaxeInt64Typedef.make(h,l)); 2064 } 2065 public static inline function toString(v:HaxeInt64abs):String { 2066 return HaxeInt64Typedef.toStr(v); 2067 } 2068 public static inline function toStr(v:HaxeInt64abs):String { 2069 return HaxeInt64Typedef.toStr(v); 2070 } 2071 public static inline function neg(v:HaxeInt64abs):HaxeInt64abs { 2072 return new HaxeInt64abs(HaxeInt64Typedef.neg(v)); 2073 } 2074 public static inline function isZero(v:HaxeInt64abs):Bool { 2075 return HaxeInt64Typedef.isZero(v); 2076 } 2077 public static inline function isNeg(v:HaxeInt64abs):Bool { 2078 return HaxeInt64Typedef.isNeg(v); 2079 } 2080 public static inline function add(x:HaxeInt64abs,y:HaxeInt64abs):HaxeInt64abs { 2081 return new HaxeInt64abs(HaxeInt64Typedef.add(x,y)); 2082 } 2083 public static inline function and(x:HaxeInt64abs,y:HaxeInt64abs):HaxeInt64abs { 2084 return new HaxeInt64abs(HaxeInt64Typedef.and(x,y)); 2085 } 2086 private static function checkDiv(x:HaxeInt64abs,y:HaxeInt64abs,isSigned:Bool):HaxeInt64abs { 2087 if(HaxeInt64Typedef.isZero(y)) 2088 Scheduler.panicFromHaxe( "attempt to divide 64-bit value by 0"); 2089 if(isSigned && (HaxeInt64Typedef.compare(y,HaxeInt64Typedef.ofInt(-1))==0) && (HaxeInt64Typedef.compare(x,HaxeInt64Typedef.make(0x80000000,0))==0) ) 2090 { 2091 //trace("checkDiv 64-bit special case"); 2092 y=HaxeInt64Typedef.ofInt(1); // special case in the Go spec 2093 } 2094 return new HaxeInt64abs(y); 2095 } 2096 public static function div(x:HaxeInt64abs,y:HaxeInt64abs,isSigned:Bool):HaxeInt64abs { 2097 y=checkDiv(x,y,isSigned); 2098 if(HaxeInt64Typedef.compare(y,HaxeInt64Typedef.ofInt(1))==0) return new HaxeInt64abs(x); 2099 if(isSigned || (!HaxeInt64Typedef.isNeg(x) && !HaxeInt64Typedef.isNeg(y))) 2100 return new HaxeInt64abs(HaxeInt64Typedef.div(x,y)); 2101 else { 2102 if( HaxeInt64Typedef.isNeg(x) ) { 2103 if( HaxeInt64Typedef.isNeg(y) ){ // both x and y are "-ve"" 2104 if( HaxeInt64Typedef.compare(x,y) < 0 ) { // x is more "-ve" than y, so the smaller uint 2105 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(0)); 2106 } else { 2107 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(1)); // both have top bit set & uint(x)>uint(y) 2108 } 2109 } else { // only x is -ve 2110 var pt1:HaxeInt64Typedef = HaxeInt64Typedef.make(0x7FFFFFFF,0xFFFFFFFF); // the largest part of the numerator 2111 var pt2:HaxeInt64Typedef = HaxeInt64Typedef.and(x,pt1); // the smaller part of the numerator 2112 var rem:HaxeInt64Typedef = HaxeInt64Typedef.make(0,1); // the left-over bit 2113 rem = HaxeInt64Typedef.add(rem,HaxeInt64Typedef.mod(pt1,y)); 2114 rem = HaxeInt64Typedef.add(rem,HaxeInt64Typedef.mod(pt2,y)); 2115 if( HaxeInt64Typedef.ucompare(rem,y) >= 0 ) { // the remainder is >= divisor 2116 rem = HaxeInt64Typedef.ofInt(1); 2117 } else { 2118 rem = HaxeInt64Typedef.ofInt(0); 2119 } 2120 pt1 = HaxeInt64Typedef.div(pt1,y); 2121 pt2 = HaxeInt64Typedef.div(pt2,y); 2122 return new HaxeInt64abs(HaxeInt64Typedef.add(pt1,HaxeInt64Typedef.add(pt2,rem))); 2123 } 2124 }else{ // logically, y is "-ve"" but x is "+ve" so y>x , so any integer divide will yeild 0 2125 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(0)); 2126 } 2127 } 2128 } 2129 public static function mod(x:HaxeInt64abs,y:HaxeInt64abs,isSigned:Bool):HaxeInt64abs { 2130 y=checkDiv(x,y,isSigned); 2131 if(HaxeInt64Typedef.compare(y,HaxeInt64Typedef.ofInt(1))==0) return new HaxeInt64abs(HaxeInt64Typedef.ofInt(0)); 2132 if(isSigned) 2133 return new HaxeInt64abs(HaxeInt64Typedef.mod(x,y)); 2134 else { 2135 return new HaxeInt64abs(sub(x,mul(div(x,y,false),y))); 2136 } 2137 } 2138 public static inline function mul(x:HaxeInt64abs,y:HaxeInt64abs):HaxeInt64abs { 2139 return new HaxeInt64abs(HaxeInt64Typedef.mul(x,y)); 2140 } 2141 public static inline function or(x:HaxeInt64abs,y:HaxeInt64abs):HaxeInt64abs { 2142 return new HaxeInt64abs(HaxeInt64Typedef.or(x,y)); 2143 } 2144 public static function shl(x:HaxeInt64abs,y:Int):HaxeInt64abs { 2145 if(y==0) return new HaxeInt64abs(x); 2146 if(y<0 || y>=64) // this amount of shl is not handled correcty by the underlying code 2147 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(0)); 2148 else 2149 return new HaxeInt64abs(HaxeInt64Typedef.shl(x,y)); 2150 } 2151 public static function shr(x:HaxeInt64abs,y:Int):HaxeInt64abs { // note, not inline 2152 if(y==0) return new HaxeInt64abs(x); 2153 if(y<0 || y>=64) 2154 if(isNeg(x)) 2155 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(-1)); 2156 else 2157 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(0)); 2158 return new HaxeInt64abs(HaxeInt64Typedef.shr(x,y)); 2159 } 2160 public static function ushr(x:HaxeInt64abs,y:Int):HaxeInt64abs { // note, not inline 2161 if(y==0) return new HaxeInt64abs(x); 2162 if(y<0 || y>=64) 2163 return new HaxeInt64abs(HaxeInt64Typedef.ofInt(0)); 2164 #if php 2165 if(y==32){ // error with php on 32 bit right shift for uint64, so do 2x16 2166 var ret:HaxeInt64Typedef = HaxeInt64Typedef.ushr(x,16); 2167 return new HaxeInt64abs(HaxeInt64Typedef.ushr(ret,16)); 2168 } 2169 #end 2170 return new HaxeInt64abs(HaxeInt64Typedef.ushr(x,y)); 2171 } 2172 public static inline function sub(x:HaxeInt64abs,y:HaxeInt64abs):HaxeInt64abs { 2173 return new HaxeInt64abs(HaxeInt64Typedef.sub(x,y)); 2174 } 2175 public static inline function xor(x:HaxeInt64abs,y:HaxeInt64abs):HaxeInt64abs { 2176 return new HaxeInt64abs(HaxeInt64Typedef.xor(x,y)); 2177 } 2178 public static inline function compare(x:HaxeInt64abs,y:HaxeInt64abs):Int { 2179 return HaxeInt64Typedef.compare(x,y); 2180 } 2181 public static function ucompare(x:HaxeInt64abs,y:HaxeInt64abs):Int { 2182 //#if cpp 2183 return HaxeInt64Typedef.ucompare(x,y); 2184 //#else 2185 // unsigned compare library code does not work properly for all platforms 2186 /*was: 2187 if(HaxeInt64Typedef.isZero(x)) { 2188 if(HaxeInt64Typedef.isZero(y)) { 2189 return 0; 2190 } else { 2191 return -1; // any value is larger than x 2192 } 2193 } 2194 if(HaxeInt64Typedef.isZero(y)) { // if we are here, we know that x is non-zero 2195 return 1; // any value of x is larger than y 2196 } 2197 if(!HaxeInt64Typedef.isNeg(x)) { // x +ve 2198 if(!HaxeInt64Typedef.isNeg(y)){ // both +ve so normal comparison 2199 return HaxeInt64Typedef.compare(x,y); 2200 }else{ // y -ve and so larger than x 2201 return -1; 2202 } 2203 }else { // x -ve 2204 if(!HaxeInt64Typedef.isNeg(y)){ // -ve x larger than +ve y 2205 return 1; 2206 }else{ // both are -ve so the normal comparison works ok 2207 return HaxeInt64Typedef.compare(x,y); 2208 } 2209 } 2210 */ 2211 //#end 2212 } 2213 } 2214 2215 typedef GOint64 = HaxeInt64abs; 2216 2217 //**************** rewrite of std Haxe library function haxe.Int64 for PHP integer overflow an other errors 2218 /* 2219 Modify haxe.Int64.hx to work on php and fix other errors 2220 - php integer overflow and ushr are incorrect (for 32-bits Int), 2221 special functions now correct for these faults for Int64. 2222 - both div and mod now have the sign correct when double-negative. 2223 - special cases of div or mod by 0 or 1 now correct. 2224 */ 2225 /* 2226 * Copyright (C)2005-2012 Haxe Foundation 2227 * 2228 * Permission is hereby granted, free of charge, to any person obtaining a 2229 * copy of this software and associated documentation files (the "Software"), 2230 * to deal in the Software without restriction, including without limitation 2231 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 2232 * and/or sell copies of the Software, and to permit persons to whom the 2233 * Software is furnished to do so, subject to the following conditions: 2234 * 2235 * The above copyright notice and this permission notice shall be included in 2236 * all copies or substantial portions of the Software. 2237 * 2238 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2239 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2240 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2241 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2242 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2243 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2244 * DEALINGS IN THE SOFTWARE. 2245 */ 2246 class Int64 { 2247 2248 var high : Int; 2249 var low : Int; 2250 2251 inline function new(high, low) { 2252 this.high = i32(high); 2253 this.low = i32(low); 2254 } 2255 2256 #if php 2257 /* 2258 private function to correctly handle 32-bit integer overflow on php 2259 see: http://stackoverflow.com/questions/300840/force-php-integer-overflow 2260 */ 2261 private static function i32php(value:Int):Int { 2262 value = (value & untyped __php__("0xFFFFFFFF")); 2263 if ( (value & untyped __php__("0x80000000"))!=0 ) 2264 value = -(((~value) & untyped __php__("0xFFFFFFFF")) + 1); 2265 return value; 2266 } 2267 #end 2268 2269 /* 2270 private function to correctly handle 32-bit ushr on php 2271 see: https://github.com/HaxeFoundation/haxe/commit/1a878aa90708040a41b0dd59f518d83b09ede209 2272 */ 2273 private static inline function ushr32(v:Int,n:Int):Int { 2274 #if php 2275 return (v >> n) & (untyped __php__("0x7fffffff") >> (n-1)); 2276 #else 2277 return v>>>n; 2278 #end 2279 } 2280 2281 @:extern static inline function i32(i) { 2282 return 2283 #if !(cpp || java || cs || flash) 2284 i==null?0: 2285 #end 2286 #if (js || flash8) 2287 i | 0; 2288 #elseif php 2289 i32php(i); // handle overflow of 32-bit integers correctly 2290 #else 2291 i; 2292 #end 2293 } 2294 2295 @:extern static inline function i32mul(a:Int,b:Int) { 2296 #if (php || js || flash8) 2297 /* 2298 We can't simply use i32(a*b) since we might overflow (52 bits precision in doubles) 2299 */ 2300 return i32(i32((a * (b >>> 16)) << 16) + (a * (b&0xFFFF))); 2301 #else 2302 return a * b; 2303 #end 2304 } 2305 2306 #if as3 public #end function toString() { 2307 if ((high|low) == 0 ) 2308 return "0"; 2309 var str = ""; 2310 var neg = false; 2311 var i = this; 2312 if( isNeg(i) ) { 2313 neg = true; 2314 i = Int64.neg(i); 2315 } 2316 var ten = ofInt(10); 2317 while( !isZero(i) ) { 2318 var r = divMod(i, ten); 2319 str = r.modulus.low + str; 2320 i = r.quotient; 2321 } 2322 if( neg ) str = "-" + str; 2323 return str; 2324 } 2325 2326 public static inline function make( high : Int, low : Int ) : Int64 { 2327 return new Int64(high, low); 2328 } 2329 2330 public static inline function ofInt( x : Int ) : Int64 { 2331 return new Int64(x >> 31,x); 2332 } 2333 2334 public static function toInt( x : Int64 ) : Int { 2335 if( x.high != 0 ) { 2336 if( x.high < 0 ) 2337 return -toInt(neg(x)); 2338 throw "Overflow"; //NOTE go panic not used here as it is in the Haxe libary code 2339 } 2340 return x.low; 2341 } 2342 2343 public static function getLow( x : Int64 ) : Int { 2344 return x.low; 2345 } 2346 2347 public static function getHigh( x : Int64 ) : Int { 2348 return x.high; 2349 } 2350 2351 public static function add( a : Int64, b : Int64 ) : Int64 { 2352 var high = i32(a.high + b.high); 2353 var low = i32(a.low + b.low); 2354 if( uicompare(low,a.low) < 0 ) 2355 high++; 2356 return new Int64(high, low); 2357 } 2358 2359 public static function sub( a : Int64, b : Int64 ) : Int64 { 2360 var high = i32(a.high - b.high); // i32() call required to match add 2361 var low = i32(a.low - b.low); // i32() call required to match add 2362 if( uicompare(a.low,b.low) < 0 ) 2363 high--; 2364 return new Int64(high, low); 2365 } 2366 2367 public static function mul( a : Int64, b : Int64 ) : Int64 { 2368 var mask = 0xFFFF; 2369 var al = a.low & mask, ah = ushr32(a.low , 16); 2370 var bl = b.low & mask, bh = ushr32(b.low , 16); 2371 var p00 = al * bl; 2372 var p10 = ah * bl; 2373 var p01 = al * bh; 2374 var p11 = ah * bh; 2375 var low = p00; 2376 var high = i32(p11 + ushr32(p01 , 16) + ushr32(p10 , 16)); 2377 p01 = i32(p01 << 16); low = i32(low + p01); if( uicompare(low, p01) < 0 ) high = i32(high + 1); 2378 p10 = i32(p10 << 16); low = i32(low + p10); if( uicompare(low, p10) < 0 ) high = i32(high + 1); 2379 high = i32(high + i32mul(a.low,b.high)); 2380 high = i32(high + i32mul(a.high,b.low)); 2381 return new Int64(high, low); 2382 } 2383 2384 static function divMod( modulus : Int64, divisor : Int64 ) { 2385 var quotient = new Int64(0, 0); 2386 var mask = new Int64(0, 1); 2387 divisor = new Int64(divisor.high, divisor.low); 2388 while( divisor.high >= 0 ) { 2389 var cmp = ucompare(divisor, modulus); 2390 divisor.high = i32( i32(divisor.high << 1) | ushr32(divisor.low , 31) ); 2391 divisor.low = i32(divisor.low << 1); 2392 mask.high = i32( i32(mask.high << 1) | ushr32(mask.low , 31) ); 2393 mask.low = i32(mask.low << 1); 2394 if( cmp >= 0 ) break; 2395 } 2396 while( i32(mask.low | mask.high) != 0 ) { 2397 if( ucompare(modulus, divisor) >= 0 ) { 2398 quotient.high= i32(quotient.high | mask.high); 2399 quotient.low= i32(quotient.low | mask.low); 2400 modulus = sub(modulus,divisor); 2401 } 2402 mask.low = i32( ushr32(mask.low , 1) | i32(mask.high << 31) ); 2403 mask.high = ushr32(mask.high , 1); 2404 2405 divisor.low = i32( ushr32(divisor.low , 1) | i32(divisor.high << 31) ); 2406 divisor.high = ushr32(divisor.high , 1); 2407 } 2408 return { quotient : quotient, modulus : modulus }; 2409 } 2410 2411 public static function div( a : Int64, b : Int64 ) : Int64 { 2412 if(b.high==0) // handle special cases of 0 and 1 2413 switch(b.low) { 2414 case 0: throw "divide by zero"; //NOTE go panic not used here as it is in the Haxe libary code 2415 case 1: return new Int64(a.high,a.low); 2416 } 2417 var sign = ((a.high<0) || (b.high<0)) && (!( (a.high<0) && (b.high<0))); // make sure we get the correct sign 2418 if( a.high < 0 ) a = neg(a); 2419 if( b.high < 0 ) b = neg(b); 2420 var q = divMod(a, b).quotient; 2421 return sign ? neg(q) : q; 2422 } 2423 2424 public static function mod( a : Int64, b : Int64 ) : Int64 { 2425 if(b.high==0) // handle special cases of 0 and 1 2426 switch(b.low) { 2427 case 0: throw "modulus by zero"; //NOTE go panic not used here as it is in the Haxe libary code 2428 case 1: return ofInt(0); 2429 } 2430 var sign = a.high<0; // the sign of a modulus is the sign of the value being mod'ed 2431 if( a.high < 0 ) a = neg(a); 2432 if( b.high < 0 ) b = neg(b); 2433 var m = divMod(a, b).modulus; 2434 return sign ? neg(m) : m; 2435 } 2436 2437 public static inline function shl( a : Int64, b : Int ) : Int64 { 2438 return if( b & 63 == 0 ) a else if( b & 63 < 32 ) new Int64( (a.high << b) | ushr32(a.low, i32(32-(b&63))), a.low << b ) else new Int64( a.low << i32(b - 32), 0 ); 2439 } 2440 2441 public static inline function shr( a : Int64, b : Int ) : Int64 { 2442 return if( b & 63 == 0 ) a else if( b & 63 < 32 ) new Int64( a.high >> b, ushr32(a.low,b) | (a.high << i32(32 - (b&63))) ) else new Int64( a.high >> 31, a.high >> i32(b - 32) ); 2443 } 2444 2445 public static inline function ushr( a : Int64, b : Int ) : Int64 { 2446 return if( b & 63 == 0 ) a else if( b & 63 < 32 ) new Int64( ushr32(a.high, b), ushr32(a.low, b) | (a.high << i32(32 - (b&63))) ) else new Int64( 0, ushr32(a.high, i32(b - 32)) ); 2447 } 2448 2449 public static inline function and( a : Int64, b : Int64 ) : Int64 { 2450 return new Int64( a.high & b.high, a.low & b.low ); 2451 } 2452 2453 public static inline function or( a : Int64, b : Int64 ) : Int64 { 2454 return new Int64( a.high | b.high, a.low | b.low ); 2455 } 2456 2457 public static inline function xor( a : Int64, b : Int64 ) : Int64 { 2458 return new Int64( a.high ^ b.high, a.low ^ b.low ); 2459 } 2460 2461 public static inline function neg( a : Int64 ) : Int64 { 2462 var high = i32(~a.high); 2463 var low = i32(-a.low); 2464 if( low == 0 ) 2465 high++; 2466 return new Int64(high,low); 2467 } 2468 2469 public static inline function isNeg( a : Int64 ) : Bool { 2470 return a.high < 0; 2471 } 2472 2473 public static inline function isZero( a : Int64 ) : Bool { 2474 return (a.high | a.low) == 0; 2475 } 2476 2477 static function uicompare( a : Int, b : Int ) { 2478 return a < 0 ? (b < 0 ? i32(~b - ~a) : 1) : (b < 0 ? -1 : i32(a - b)); 2479 } 2480 2481 public static inline function compare( a : Int64, b : Int64 ) : Int { 2482 var v = i32(i32(a.high) - i32(b.high)); 2483 return if( v != 0 ) v else uicompare(a.low,b.low); 2484 } 2485 2486 /** 2487 Compare two Int64 in unsigned mode. 2488 **/ 2489 public static inline function ucompare( a : Int64, b : Int64 ) : Int { 2490 var v = uicompare(a.high,b.high); 2491 return if( v != 0 ) v else uicompare(a.low, b.low); 2492 } 2493 2494 public static inline function toStr( a : Int64 ) : String { 2495 return a.toString(); 2496 } 2497 2498 } 2499 //**************** END REWRITE of haxe.Int64 for php and to correct errors 2500 2501 `) 2502 l.PogoComp().WriteAsClass("StackFrameBasis", ` 2503 2504 // GoRoutine 2505 class StackFrameBasis 2506 { 2507 public var _Next:Int=0; 2508 public var _recoverNext:Null<Int>=null; 2509 public var _incomplete:Bool=true; 2510 public var _latestPH:Int=0; 2511 public var _latestBlock:Int=0; 2512 public var _functionPH:Int; 2513 public var _functionName:String; 2514 public var _goroutine(default,null):Int; 2515 public var _bds:Array<Dynamic>; // bindings for closures 2516 public var _deferStack:List<StackFrame>; 2517 public var _debugVars:Map<String,Dynamic>; 2518 #if godebug 2519 var _debugVarsLast:Map<String,Dynamic>; 2520 static var _debugBP:Map<Int,Bool>; 2521 #end 2522 2523 public function new(gr:Int,ph:Int,name:String){ 2524 _goroutine=gr; 2525 _functionPH=ph; 2526 _functionName=name; 2527 #if godebug 2528 _debugVars=new Map<String,Dynamic>(); 2529 _debugVarsLast= new Map<String,Dynamic>(); 2530 #end 2531 this.setPH(ph); // so that we call the debugger, if it is enabled 2532 // TODO optionally profile function entry here 2533 } 2534 2535 public inline function nullOnExitSF(){ 2536 #if nulltempvars 2537 _functionName=null; 2538 // TODO the next three items could be optimized to only be set to null on exit if they are used in a Go func 2539 _bds=null; 2540 _deferStack=null; 2541 _debugVars=null; 2542 #if godebug 2543 _debugVarsLast=null; 2544 #end 2545 #end 2546 } 2547 2548 public function setDebugVar(name:String,value:Dynamic){ 2549 if(_debugVars==null) 2550 _debugVars=new Map<String,Dynamic>(); 2551 _debugVars.set(name,value); 2552 } 2553 2554 public function setLatest(ph:Int,blk:Int){ // this can be done inline, but generates too much code 2555 _latestBlock=blk; 2556 this.setPH(ph); 2557 // TODO optionally profile block entry here 2558 } 2559 2560 public function breakpoint(){ 2561 #if (godebug && (cpp || neko)) 2562 trace("GODEBUG: runtime.Breakpoint()"); 2563 _debugBP.set(_latestPH,true); // set a constant debug trap 2564 setPH(_latestPH); // run the debugger 2565 #else 2566 //trace("GODEBUG: runtime.Breakpoint() to run debugger (cpp/neko only) use: haxe -D godebug"); 2567 #end 2568 } 2569 2570 public function setPH(ph:Int){ 2571 _latestPH=ph; 2572 // TODO optionally profile instruction line entry here 2573 // optionally add debugger code here, if the target supports Console.readln() 2574 #if (godebug && (cpp || neko)) 2575 // TODO add support for: cs || java || php 2576 if(_debugBP==null||_debugBP.exists(ph)){ 2577 var stay=true; 2578 var ln:Null<String>; 2579 while(stay){ 2580 printDebugState(); 2581 ln=Console.readln(); 2582 if(ln==null) 2583 stay=false; // effectively step a line 2584 else { 2585 // debugger commands 2586 var fb=new Array<Dynamic>(); 2587 var bits=ln.split(" "); 2588 switch(ln.charAt(0)){ 2589 case "S","s","R","r": 2590 if(bits.length<3) 2591 fb[0]="please use the format: S/R filename linenumber"; 2592 else{ 2593 if(_debugBP==null){ 2594 _debugBP=new Map<Int,Bool>(); 2595 } 2596 var base=Go.getStartCPos(bits[1]); 2597 if(base==-1) 2598 fb[0]="sorry, can't find file: "+bits[1]; 2599 else{ 2600 var off=Std.parseInt(bits[2]); 2601 if(off==null) 2602 fb[0]="sorry, can't parseInt: "+bits[2]; 2603 else{ 2604 fb[0]="break-point "; 2605 switch(ln.charAt(0)){ 2606 case "S","s": 2607 fb[1]="set"; 2608 _debugBP.set(base+off,true); 2609 case "R","r": 2610 fb[1]="removed"; 2611 if(_debugBP.exists(base+off)) 2612 _debugBP.remove(base+off); 2613 } 2614 fb[2]=" at: "+Go.CPos(base+off); 2615 } 2616 } 2617 } 2618 case "B","b": 2619 if(_debugBP==null){ 2620 fb[0]="no break-points set"; 2621 } else { 2622 fb[0]="break-points:\n"; 2623 var ent=1; 2624 for(b in _debugBP.keys()){ 2625 fb[ent]="\t"+Go.CPos(b)+"\n"; 2626 ent+=1; 2627 } 2628 } 2629 case "C","c": 2630 _debugBP=null; 2631 fb[0]="all break-points cleared"; 2632 case "L","l": 2633 if(bits.length>=2) 2634 if(_debugVars.exists(bits[1])){ 2635 var v:String; 2636 if(bits[1].indexOf(".")!=-1) // global 2637 v="global: "+_debugVars.get(bits[1]).toString(); 2638 else 2639 v=Std.string(_debugVars.get(bits[1])); 2640 fb[0]="Local assignment to: "+bits[1]+" = "+v.substr(0,500); 2641 } else 2642 fb[0]="Can't find local assignment: "+bits[1]; 2643 else{ 2644 fb[0]="Local assignments:\n"; 2645 var ent=1; 2646 for(b in _debugVars.keys()){ 2647 if(b.indexOf(".")==-1) { // local 2648 fb[ent]="\t"+b+" = "+Std.string(_debugVars.get(b)).substr(0,500)+"\n"; 2649 ent+=1; 2650 } 2651 } 2652 } 2653 case "G","g": 2654 if(bits.length<2) 2655 fb[0]="please use the format: G globalname "; 2656 else 2657 fb[0]="Global: "+Go.getGlobal(bits[1]).substr(0,500); 2658 case "M","m": 2659 if(bits.length<3) 2660 fb[0]="please use the format: M objectID offset "; 2661 else { 2662 var id=Std.parseInt(bits[1]); 2663 if(id==null) 2664 fb[0]="sorry, can't parseInt: "+bits[1]; 2665 else{ 2666 var off=Std.parseInt(bits[2]); 2667 if(off==null) 2668 fb[0]="sorry, can't parseInt: "+bits[2]; 2669 else 2670 fb[0]="Memory: "+Object.memory.get(id).toString(off).substr(0,500); 2671 } 2672 } 2673 case "D","d": 2674 fb[0]=Scheduler.stackDump(); 2675 case "P","p": 2676 Scheduler.panicFromHaxe("panic from debugger"); 2677 fb[0]="Panicing from debugger to exit program"; 2678 _debugBP=new Map<Int,Bool>(); 2679 _debugBP.set(-1,true); // unreachable break-point 2680 stay=false; 2681 case "X","x": 2682 //_debugBP=new Map<Int,Bool>(); 2683 //_debugBP.set(-1,true); // unreachable break-point 2684 fb[0]="eXecute program"; 2685 stay=false; 2686 default: 2687 fb[0]="commands: blank=step, B=BrakePointList, S/R=Set/RemoveBP name line, C=ClearAllBP, L=Local name, G=Global name, M=Memory id offset, D=stackDump, X=eXecute program, P=Panic (^C does not work)"; 2688 } 2689 Console.println(fb); 2690 } 2691 } 2692 } 2693 #end 2694 } 2695 2696 #if godebug 2697 public function printDebugState():Void{ 2698 var guf=new Array<Dynamic>(); 2699 var gc=1; 2700 guf[0]="GR:"+_goroutine+" - "+_functionName+" @ "+Go.CPos(_latestPH); 2701 for(k in _debugVars.keys()){ 2702 if(_debugVars.get(k)!=_debugVarsLast.get(k)){ 2703 if(k.indexOf(".")!=-1) // global 2704 guf[gc]="\n"+k+" = "+cast(_debugVars.get(k),Pointer).toString().substr(0,500); 2705 else 2706 guf[gc]="\n"+k+" = "+Std.string(_debugVars.get(k)).substr(0,500); 2707 gc+=1; 2708 _debugVarsLast.set(k,_debugVars.get(k)); 2709 } 2710 } 2711 Console.println(guf); 2712 } 2713 #end 2714 2715 public function defer(fn:StackFrame){ 2716 if(_deferStack==null) 2717 _deferStack=new List<StackFrame>(); 2718 _deferStack.add(fn); // add to the end of the list, so that runDefers() get them in the right order 2719 } 2720 2721 public function runDefers(){ 2722 if(_deferStack!=null) 2723 while(!_deferStack.isEmpty()){ 2724 Scheduler.push(_goroutine,_deferStack.pop()); 2725 } 2726 } 2727 2728 } 2729 `) 2730 l.PogoComp().WriteAsClass("StackFrame", ` 2731 2732 interface StackFrame 2733 { 2734 public var _Next:Int; 2735 public var _recoverNext:Null<Int>; 2736 public var _incomplete:Bool; 2737 public var _latestPH:Int; 2738 public var _latestBlock:Int; 2739 public var _functionPH:Int; 2740 public var _functionName:String; 2741 public var _goroutine(default,null):Int; 2742 public var _bds:Array<Dynamic>; // bindings for closures 2743 public var _deferStack:List<StackFrame>; 2744 public var _debugVars:Map<String,Dynamic>; 2745 function run():StackFrame; // function state machine (set up by each Go function Haxe class) 2746 function res():Dynamic; // function result (set up by each Go function Haxe class) 2747 function nullOnExitSF():Void; // call this when exiting the function 2748 function setDebugVar(name:String,value:Dynamic):Void; 2749 } 2750 `) 2751 l.PogoComp().WriteAsClass("Scheduler", ` 2752 2753 @:cppFileCode('extern "C" int tardisgo_timereventhandler(int rl) { tardis::Scheduler_obj::runLimit=rl; tardis::Scheduler_obj::timerEventHandler(0); return 0; }') 2754 2755 @:keep 2756 class Scheduler { // NOTE this code requires a single-thread, as there is no locking TODO detect deadlocks 2757 // public 2758 public static var doneInit:Bool=false; // flag to limit go-routines to 1 during the init() processing phase 2759 // private 2760 static var grStacks:Array<Array<StackFrame>>=new Array<Array<StackFrame>>(); 2761 static var grInPanic:Array<Bool>=new Array<Bool>(); 2762 static var grPanicMsg:Array<Interface>=new Array<Interface>(); 2763 static var panicStackDump:String=""; 2764 static var entryCount:Int=0; // this to be able to monitor the re-entrys into this routine for debug 2765 static var currentGR:Int=0; // the current goroutine, used by Scheduler.panicFromHaxe(), NOTE this requires a single thread 2766 2767 // if the scheduler is being run from a timer, this is where it comes to 2768 public static var runLimit:Int=0; 2769 public static function timerEventHandler(dummy:Dynamic) { 2770 if(runLimit<2) 2771 runAll(); 2772 else 2773 runToStasis(runLimit); 2774 } 2775 2776 static inline function runToStasis(cyclesLimit:Int) { 2777 var lastHash:Int=0; 2778 var thisHash:Int=makeStateHash(); 2779 var cycles:Int=0; 2780 while( lastHash!=thisHash && cycles<cyclesLimit ){ 2781 lastHash = thisHash; 2782 runAll(); 2783 thisHash = makeStateHash(); 2784 cycles += 1; 2785 } 2786 //if(cycles<cyclesLimit) 2787 // trace("Stasis achieved after "+cycles+" cycles"); 2788 //else 2789 // trace("Stasis not achieved"); 2790 } 2791 2792 static inline function makeStateHash():Int { // TODO improve by checking for change on the fly? 2793 var numGR=grStacks.length; 2794 var hash:Int=numGR; 2795 for( gr in 0 ... numGR ){ 2796 var thisGR=grStacks[gr]; 2797 var stacklen=thisGR.length; 2798 if( stacklen>0 ) { 2799 var top=thisGR[stacklen-1]; 2800 hash+=stacklen+top._functionPH+top._Next; 2801 } 2802 } 2803 //trace("makeStateHash()="+hash); 2804 return hash; 2805 } 2806 2807 public static function runAll() { // this must be re-entrant, in order to allow Haxe->Go->Haxe->Go for some runtime functions 2808 var cg:Int=0; // reentrant current goroutine 2809 entryCount++; 2810 if(entryCount>2) { // this is the simple limit to runtime recursion 2811 throw "Scheduler.runAll() entryCount exceeded - "+stackDump(); 2812 } 2813 2814 var thisStack:Array<StackFrame>; 2815 var thisStackLen:Int; 2816 2817 // special handling for goroutine 0, which is used in the initialisation phase and re-entrantly, where only one goroutine may operate 2818 thisStack=grStacks[0]; 2819 thisStackLen=thisStack.length; 2820 if(thisStackLen==0) { // check if there is ever likley to be anything to do 2821 if(grStacks.length<=1) { 2822 //throw "Scheduler: there is only one goroutine and its stack is empty\n"+stackDump(); 2823 return; // nothing to do... 2824 } 2825 } else { // run goroutine zero 2826 runOne(0,entryCount,thisStack,thisStackLen); 2827 } 2828 2829 if(doneInit && entryCount==1 ) { // don't run extra goroutines when we are re-entrant or have not finished initialistion 2830 // NOTE this means that Haxe->Go->Haxe->Go code cannot run goroutines 2831 var grStacksLen=grStacks.length; 2832 for(cg in 1...grStacksLen) { // length may grow during a run through, NOTE goroutine 0 not run again 2833 thisStack=grStacks[cg]; 2834 thisStackLen=thisStack.length; 2835 if(thisStackLen>0) { 2836 runOne(cg,entryCount,thisStack,thisStackLen); 2837 } 2838 } 2839 2840 // prune the list of goroutines only at the end (goroutine numbers are in the stack frames, so can't be altered) 2841 grStacksLen=grStacks.length;// there may be more goroutines than we started with 2842 if(grStacksLen>1) // we must always have goroutine 0 2843 if(grStacks[grStacksLen-1].length==0) 2844 grStacks.pop(); 2845 } 2846 #if nulltempvars 2847 thisStack=null; // for GC 2848 #end 2849 entryCount--; 2850 } 2851 static inline function runOne(gr:Int,entryCount:Int,thisStack:Array<StackFrame>,thisStackLen:Int){ // called from above to call individual goroutines TODO: Review for multi-threading 2852 if(grInPanic[gr]) { 2853 if(entryCount!=1) { // we are in re-entrant code, so we can't panic again, as this may be part of the panic handling... 2854 // NOTE this means that Haxe->Go->Haxe->Go code cannot use panic() reliably 2855 run1a(gr,thisStack,thisStackLen); 2856 } else { 2857 while(grInPanic[gr]){ 2858 if(grStacks[gr].length==0){ 2859 Console.naclWrite("Panic in goroutine "+gr+"\n"+panicStackDump); // use stored stack dump 2860 throw "Go panic"; 2861 } else { 2862 var sf:StackFrame=grStacks[gr].pop(); 2863 if(sf._deferStack!=null) 2864 while(!sf._deferStack.isEmpty() && grInPanic[gr]) { 2865 // NOTE this will run all of the defered code for a function, 2866 // NOTE if recover() is encountered it should set grInPanic[gr] to false. 2867 // TODO consider merging code with RunDefers() 2868 var def:StackFrame=sf._deferStack.pop(); 2869 //trace("DEBUG runOne panic defer:",def._functionName); 2870 Scheduler.push(gr,def); 2871 while(def._incomplete) 2872 runAll(); // with entryCount >1, so run as above 2873 } 2874 if(!grInPanic[gr]){ 2875 //trace("DEBUG runOne panic - recovered"); 2876 if(sf._recoverNext != null) { 2877 //trace("DEBUG runOne panic - running recovery code"); 2878 sf._Next = sf._recoverNext; // set the re-entry point 2879 } 2880 grStacks[gr].push(sf); // now run the recovery code 2881 } 2882 #if nulltempvars 2883 sf=null; // for GC 2884 #end 2885 } 2886 } 2887 } 2888 } else { 2889 run1a(gr,thisStack,thisStackLen); 2890 } 2891 } 2892 public static inline function run1a(gr:Int,thisStack:Array<StackFrame>,thisStackLen:Int){ 2893 currentGR=gr; 2894 thisStack[thisStackLen-1].run(); 2895 } 2896 public static inline function run1(gr:Int){ // used by callFromRT() for every go function 2897 run1a(gr,grStacks[gr],grStacks[gr].length); // run() may call haxe which calls these routines recursively 2898 } 2899 public static function makeGoroutine():Int { 2900 for (r in 1 ... grStacks.length) // goroutine zero is reserved for init activities, main.main() and Haxe call-backs 2901 if(grStacks[r].length==0) 2902 { 2903 grInPanic[r]=false; 2904 grPanicMsg[r]=null; 2905 return r; // reuse a previous goroutine number if possible 2906 } 2907 var l:Int=grStacks.length; 2908 grStacks[l]=new Array<StackFrame>(); 2909 grInPanic[l]=false; 2910 grPanicMsg[l]=null; 2911 return l; 2912 } 2913 public static inline function pop(gr:Int):StackFrame { 2914 return grStacks[gr].pop(); // NOTE removing old object pointer does not improve GC (tested 3 times) 2915 } 2916 public static inline function push(gr:Int,sf:StackFrame){ 2917 grStacks[gr].push(sf); 2918 } 2919 public static inline function NumGoroutine():Int { 2920 return grStacks.length; 2921 } 2922 public static inline function ThisGoroutine():Int { 2923 return currentGR; 2924 } 2925 2926 public static function stackDump():String { 2927 var ret:String = ""; 2928 var gr:Int; 2929 ret += "runAll() entryCount="+entryCount+"\n"; 2930 for(gr in 0...grStacks.length) { 2931 ret += "---\nGoroutine " + gr + " "+grPanicMsg[gr]+"\n"; //may need to unpack the interface 2932 if(grStacks[gr].length==0) { 2933 ret += "Stack is empty\n"; 2934 } else { 2935 ret += "Stack has " +grStacks[gr].length+ " entries:\n"; 2936 var e = grStacks[gr].length -1; 2937 while( e >= 0){ 2938 var ent = grStacks[gr][e]; 2939 if(ent==null) { 2940 ret += "\tStack entry is null\n"; 2941 } else { 2942 ret += "\t"+ent._functionName+" starting at "+Go.CPos(ent._functionPH); 2943 ret += " latest position "+Go.CPos(ent._latestPH); 2944 ret += " latest block "+ent._latestBlock+"\n"; 2945 if(ent._debugVars!=null){ 2946 for(k in ent._debugVars.keys()) { 2947 if(k.indexOf(".")==-1){ // not a global assignment, so showing only locals 2948 var t:Dynamic=ent._debugVars.get(k); 2949 if(t==null) t="nil"; 2950 if(Std.is(t,Pointer)) t=t.toUniqueVal(); 2951 ret += "\t\tvar "+k+" = "+t+"\n"; 2952 #if nulltempvars 2953 t=null; // for GC 2954 #end 2955 } 2956 } 2957 } 2958 } 2959 #if nulltempvars 2960 ent=null; // for GC 2961 #end 2962 e -= 1; 2963 } 2964 } 2965 } 2966 return ret; 2967 } 2968 2969 public static function getNumCallers(gr:Int):Int { 2970 if(grStacks[gr].length==0) { 2971 return 0; 2972 } else { 2973 return grStacks[gr].length; 2974 } 2975 } 2976 2977 public static function getCallerX(gr:Int,x:Int):Int { 2978 if(grStacks[gr].length==0) { 2979 return 0; // error 2980 } else { 2981 var e = grStacks[gr].length -1; 2982 while(e >= 0){ 2983 var ent=grStacks[gr][e]; 2984 if(x==0) { 2985 if(ent==null) { 2986 return 0; // this is an error 2987 } else { 2988 return ent._latestPH; 2989 } 2990 } 2991 #if nulltempvars 2992 ent=null; // for GC 2993 #end 2994 x -= 1; 2995 e -= 1; 2996 } 2997 } 2998 return 0; // error 2999 } 3000 3001 public static function traceStackDump() {trace(stackDump());} 3002 3003 public static function panic(gr:Int,err:Interface){ 3004 if(gr>=grStacks.length||gr<0) 3005 throw "Scheduler.panic() invalid goroutine"; 3006 if(grInPanic[gr]) { // if we are already in a panic, not much we can do... 3007 //trace("Scheduler.panic() panic within panic for goroutine "+Std.string(gr)+" message: "+err.toString()); 3008 }else{ 3009 grInPanic[gr]=true; 3010 grPanicMsg[gr]=err; 3011 panicStackDump=stackDump(); 3012 #if godebug 3013 trace("GODEBUG: panic in goroutine "+Std.string(gr)+" message: "+err.toString()); 3014 var top = grStacks[gr][grStacks[gr].length-1] //grStacks[gr].first(); 3015 if(top!=null) 3016 cast(top,StackFrameBasis).breakpoint(); 3017 #end 3018 } 3019 } 3020 public static function recover(gr:Int):Interface{ 3021 if(gr>=grStacks.length||gr<0) 3022 throw "Scheduler.recover() invalid goroutine"; 3023 if(grInPanic[gr]==false) 3024 return null; 3025 #if godebug 3026 trace("GODEBUG: recover in goroutine "+Std.string(gr)+" message: "+grPanicMsg[gr]); 3027 var top = grStacks[gr][grStacks[gr].length-1] //grStacks[gr].first(); 3028 if(top!=null) 3029 cast(top,StackFrameBasis).breakpoint(); 3030 #end 3031 grInPanic[gr]=false; 3032 var t = grPanicMsg[gr]; 3033 grPanicMsg[gr]=null; 3034 return t; 3035 } 3036 public static function panicFromHaxe(err:String) { 3037 if(currentGR>=grStacks.length||currentGR<0) 3038 // if current goroutine is -ve, or out of range, always panics in goroutine 0 3039 panic(0,new Interface(TypeInfo.getId("string"),"Runtime panic, unknown goroutine, "+err+" ")); 3040 else 3041 panic(currentGR,new Interface(TypeInfo.getId("string"),"Runtime panic, "+err+" ")); 3042 Console.naclWrite(panicStackDump); 3043 throw "Haxe panic"; // NOTE can't be recovered! 3044 } 3045 public static function bbi() { 3046 panicFromHaxe("bad block ID (internal phi error)"); 3047 } 3048 public static function ioor() { 3049 panicFromHaxe("index out of range"); 3050 } 3051 public static function htc(c:Dynamic,pos:Int) { 3052 panicFromHaxe("Haxe try-catch exception <"+Std.string(c)+"> position "+Std.string(pos)+ 3053 " at or before: "+Go.CPos(pos)); 3054 } 3055 public static #if inlinepointers inline #end function wraprangechk(val:Int,sz:Int) { 3056 if((val<0)||(val>=sz)) ioor(); 3057 } 3058 public static function unt():Dynamic { 3059 panicFromHaxe("nil interface target for method"); 3060 return null; 3061 } 3062 static function unp() { 3063 panicFromHaxe("unexpected nil pointer (ssa:wrapnilchk)"); 3064 } 3065 public static function wrapnilchk(p:Pointer):Pointer { 3066 if(p==null) unp(); 3067 return p; 3068 } 3069 } 3070 `) 3071 l.PogoComp().WriteAsClass("GOmap", ` 3072 3073 class GOmap { 3074 // TODO write a more sophisticated (and hopefully faster) version of this code 3075 // TODO in Haxe, the keys can be Int, String or "object" (by reference) 3076 // TODO there is also a very sophisticated go implementation in runtime 3077 3078 public var baseMap:Map<String,{key:Dynamic,val:Dynamic}>; 3079 public var kz:Dynamic; 3080 public var vz:Dynamic; 3081 3082 public function new (kDef:Dynamic,vDef:Dynamic) { 3083 //trace("DEBUG new",kDef,vDef); 3084 baseMap = new Map<String,{key:Dynamic,val:Dynamic}>(); 3085 kz = kDef; 3086 vz = vDef; 3087 } 3088 3089 #if cpp 3090 static var setDefaultFormat:Bool=true; 3091 #end 3092 public static function makeKey(a:Dynamic):String{ 3093 #if cpp 3094 if(setDefaultFormat){ // TODO rewrite this so that we don't check every time 3095 cpp.Lib.setFloatFormat("%.17g"); 3096 setDefaultFormat=false; 3097 } 3098 #end 3099 //trace("DEBUG makeKey",a); 3100 if(a==null) return "<<<<NULL>>>>"; // TODO how can this be more unique? 3101 if(Reflect.isObject(a)){ 3102 if(Std.is(a,String)) 3103 return a; 3104 if(Std.is(a,Pointer)) 3105 return cast(a,Pointer).toUniqueVal(); 3106 // NOTE Object could be an abstract 3107 #if !abstractobjects 3108 if(Std.is(a,Object)){ 3109 //trace("DEBUG makeKey Object found"); 3110 var r=cast(a,Object).toString(); 3111 //trace("DEBUG makeKey Object="+r); 3112 return r; 3113 } 3114 #end 3115 if(Std.is(a,Complex)){ 3116 return Complex.toString(a); 3117 } 3118 if(Std.is(a,Interface)) 3119 return cast(a,Interface).toString(); 3120 if(Std.is(a,Slice)) 3121 return a.toString(); 3122 if(Std.is(a,Channel)) 3123 return a.toString(); 3124 if(Std.is(a,GOmap)||Std.is(a,Closure)) { 3125 Scheduler.panicFromHaxe("haxeruntime.GOmap.makeKey() unsupported haxe type: "+a); 3126 return ""; 3127 } 3128 #if abstractobjects 3129 return a.toString(); // must be an Object or Int64 3130 #else 3131 return GOint64.toString(a); 3132 #end 3133 } 3134 #if cs 3135 if(Std.is(a,Float)) { 3136 // in cpp & cs, Std.string(1.9999999999999998) => "2" 3137 // TODO consider how to deal with this issue in the compound types above 3138 return GOint64.toString(Go_haxegoruntime_FFloat64bits.callFromRT(0,a)); 3139 } 3140 #end 3141 return Std.string(a); 3142 } 3143 3144 public function set(realKey:Dynamic,value:Dynamic){ 3145 var sKey = makeKey(realKey); 3146 //trace("DEBUG set",sKey,realKey); 3147 if(baseMap.exists(sKey)){ 3148 if(!Force.isEqualDynamic(baseMap.get(sKey).key,realKey)) 3149 Scheduler.panicFromHaxe("haxeruntime.GOmap non-unique key for: "+sKey); 3150 } 3151 baseMap.set(sKey,{key:realKey,val:value}); 3152 } 3153 3154 public function get(rKey:Dynamic):Dynamic { 3155 var sKey = makeKey(rKey); 3156 //trace("DEBUG get",sKey,rKey); 3157 if(baseMap.exists(sKey)) return baseMap.get(sKey).val; 3158 else return vz; // the zero value 3159 } 3160 3161 public function exists(rKey:Dynamic):Bool { 3162 var sKey = makeKey(rKey); 3163 //trace("DEBUG exists",sKey,rKey); 3164 return baseMap.exists(sKey); 3165 } 3166 3167 public function remove(r:Dynamic){ 3168 var s = makeKey(r); 3169 //trace("DEBUG remove",s,r); 3170 baseMap.remove(s); 3171 } 3172 3173 public function len():Int { 3174 var _l:Int=0; 3175 var _it=baseMap.iterator(); 3176 while(_it.hasNext()) {_l++; _it.next();}; 3177 //trace("DEBUG len",_l); 3178 return _l; 3179 } 3180 3181 public function range():GOmapRange { 3182 var keys = new Array<String>(); 3183 var k = baseMap.keys(); // in C# and Java, this may not work if new items are added to the map 3184 while(k.hasNext()) 3185 keys.push(k.next()); 3186 return new GOmapRange(keys,this); 3187 } 3188 3189 } 3190 `) 3191 l.PogoComp().WriteAsClass("GOmapRange", ` 3192 3193 class GOmapRange { 3194 private var k:Array<String>; 3195 private var m:GOmap; 3196 3197 public function new(kv:Array<String>, mv:GOmap){ 3198 k=kv; 3199 m=mv; 3200 } 3201 3202 public function next():{r0:Bool,r1:Dynamic,r2:Dynamic} { 3203 var _hn:Bool=k.length>0; 3204 if(_hn){ 3205 var _nxt=k.pop(); 3206 if(m.baseMap.exists(_nxt)) 3207 return {r0:true,r1:m.baseMap.get(_nxt).key,r2:m.baseMap.get(_nxt).val}; 3208 else 3209 return next(); // recurse if this key is not found (deleted in-between?) 3210 }else{ 3211 return {r0:false,r1:m.kz,r2:m.vz}; 3212 } 3213 } 3214 } 3215 `) 3216 l.PogoComp().WriteAsClass("GOstringRange", ` 3217 3218 class GOstringRange { 3219 private var g:Int; 3220 private var k:Int; 3221 private var v:Slice; 3222 3223 public function new(gr:Int,s:String){ 3224 g=gr; 3225 k=0; 3226 v=Force.toUTF8slice(gr,s); 3227 } 3228 3229 public function next():{r0:Bool,r1:Int,r2:Int} { 3230 var _thisK:Int=k; 3231 if(k>=v.len()) 3232 return {r0:false,r1:0,r2:0}; 3233 else { 3234 var _dr:{r0:Int,r1:Int}=Go_unicode_slsh_utf8_DDecodeRRune.callFromRT(g,v.subSlice(_thisK,-1)); 3235 k+=_dr.r1; 3236 return {r0:true,r1:_thisK,r2:_dr.r0}; 3237 } 3238 } 3239 } 3240 3241 3242 `) 3243 3244 return "" 3245 }