github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/haxe/types.go (about) 1 // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors 2 // Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package haxe 6 7 import ( 8 "fmt" 9 "reflect" 10 "sort" 11 "strconv" 12 "strings" 13 "unicode" 14 15 "github.com/tardisgo/tardisgo/pogo" 16 "github.com/tardisgo/tardisgo/tgoutil" 17 18 "go/types" 19 "golang.org/x/tools/go/ssa" 20 //"golang.org/x/tools/go/types/typeutil" 21 ) 22 23 func (l langType) LangType(t types.Type, retInitVal bool, errorInfo string) string { 24 if l.PogoComp().IsValidInPogo(t, errorInfo) { 25 switch t.(type) { 26 case *types.Basic: 27 switch t.(*types.Basic).Kind() { 28 case types.Bool, types.UntypedBool: 29 if retInitVal { 30 return "false" 31 } 32 return "Bool" 33 case types.String, types.UntypedString: 34 if retInitVal { 35 return `""` 36 } 37 return "String" 38 case types.Float64, types.Float32, types.UntypedFloat: 39 if retInitVal { 40 return "0.0" 41 } 42 return "Float" 43 case types.Complex64, types.Complex128, types.UntypedComplex: 44 if retInitVal { 45 return "new Complex(0.0,0.0)" 46 } 47 return "Complex" 48 case types.Int, types.Int8, types.Int16, types.Int32, types.UntypedRune, 49 types.Uint8, types.Uint16, types.Uint, types.Uint32: // NOTE: untyped runes default to Int without a warning 50 if retInitVal { 51 return "0" 52 } 53 return "Int" 54 case types.Int64, types.Uint64: 55 if retInitVal { 56 return "GOint64.ofInt(0)" 57 } 58 return "GOint64" 59 case types.UntypedInt: // TODO: investigate further the situations in which this warning is generated 60 if retInitVal { 61 return "0" 62 } 63 return "UNTYPED_INT" // NOTE: if this value were ever to be used, it would cause a Haxe compilation error 64 case types.UnsafePointer: 65 if retInitVal { 66 return "null" // NOTE ALL pointers are unsafe 67 } 68 return "Pointer" 69 case types.Uintptr: // Uintptr sometimes used as an integer type, sometimes as a container for another type 70 if retInitVal { 71 return "null" 72 } 73 return "Dynamic" 74 default: 75 l.PogoComp().LogWarning(errorInfo, "Haxe", fmt.Errorf("haxe.LangType() unrecognised basic type, Dynamic assumed")) 76 if retInitVal { 77 return "null" 78 } 79 return "Dynamic" 80 } 81 case *types.Interface: 82 if retInitVal { 83 return `null` 84 } 85 return "Interface" 86 case *types.Named: 87 haxeName := getHaxeClass(t.(*types.Named).String()) 88 //fmt.Println("DEBUG Go named type -> Haxe type :", t.(*types.Named).String(), "->", haxeName) 89 if haxeName != "" { 90 if retInitVal { 91 return `null` // NOTE code to the right does not work in openfl/flash: `Type.createEmptyInstance(` + haxeName + ")" 92 } 93 return haxeName 94 } 95 return l.LangType(t.(*types.Named).Underlying(), retInitVal, errorInfo) 96 case *types.Chan: 97 if retInitVal { 98 return "new Channel(1)" //waa: <" + l.LangType(t.(*types.Chan).Elem(), false, errorInfo) + ">(1)" 99 } 100 return "Channel" //was: <" + l.LangType(t.(*types.Chan).Elem(), false, errorInfo) + ">" 101 case *types.Map: 102 if retInitVal { 103 k := t.(*types.Map).Key().Underlying() 104 kv := l.LangType(k, true, errorInfo) 105 e := t.(*types.Map).Elem().Underlying() 106 ev := "null" // TODO review, required for encode/gob to stop recursion 107 if _, isMap := e.(*types.Map); !isMap { 108 ev = l.LangType(e, true, errorInfo) 109 } 110 return "new GOmap(" + kv + "," + ev + ")" 111 } 112 return "GOmap" 113 case *types.Slice: 114 if retInitVal { 115 return "new Slice(Pointer.make(" + 116 "Object.make(0)" + 117 "),0,0,0," + "1" + arrayOffsetCalc(t.(*types.Slice).Elem().Underlying()) + ")" 118 } 119 return "Slice" 120 case *types.Array: 121 if retInitVal { 122 return fmt.Sprintf("Object.make(%d)", haxeStdSizes.Sizeof(t)) 123 } 124 return "Object" 125 case *types.Struct: 126 if retInitVal { 127 return fmt.Sprintf("Object.make(%d)", haxeStdSizes.Sizeof(t.(*types.Struct).Underlying())) 128 } 129 return "Object" 130 case *types.Tuple: // what is returned by a call and some other instructions, not in the Go language spec! 131 tup := t.(*types.Tuple) 132 switch tup.Len() { 133 case 0: 134 return "" 135 case 1: 136 return l.LangType(tup.At(0).Type().Underlying(), retInitVal, errorInfo) 137 default: 138 ret := "{" 139 for ele := 0; ele < tup.Len(); ele++ { 140 if ele != 0 { 141 ret += "," 142 } 143 ret += tgoutil.MakeID("r"+fmt.Sprintf("%d", ele)) + ":" 144 if !retInitVal { 145 ret += "Null<" 146 } 147 ret += l.LangType(tup.At(ele).Type().Underlying(), retInitVal, errorInfo) 148 if !retInitVal { 149 ret += ">" 150 } 151 } 152 return ret + "}" 153 } 154 case *types.Pointer: 155 if retInitVal { 156 // NOTE pointer declarations create endless recursion for self-referencing structures unless initialized with null 157 return "null" //rather than: + l.LangType(t.(*types.Pointer).Elem(), retInitVal, errorInfo) + ")" 158 } 159 return "Pointer" 160 case *types.Signature: 161 if retInitVal { 162 return "null" 163 } 164 ret := "Closure" 165 return ret 166 default: 167 rTyp := reflect.TypeOf(t).String() 168 if rTyp == "*ssa.opaqueType" { // NOTE the type for map itterators, not in the Go language spec! 169 if retInitVal { // use dynamic type, brief tests seem OK, but may not always work on static hosts 170 return "null" 171 } 172 return "Dynamic" 173 } 174 l.PogoComp().LogError(errorInfo, "Haxe", 175 fmt.Errorf("haxe.LangType() internal error, unhandled non-basic type: %s", rTyp)) 176 } 177 } 178 return "UNKNOWN_LANGTYPE" // this should generate a Haxe compiler error 179 } 180 181 func (l langType) Convert(register, langType string, destType types.Type, v interface{}, errorInfo string) string { 182 srcTyp := l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) 183 if srcTyp == langType && langType != "Float" && langType != "Int" { // no cast required because the Haxe type is the same 184 return register + "=" + l.IndirectValue(v, errorInfo) + ";" 185 } 186 switch langType { // target Haxe type 187 case "Dynamic": // no cast allowed for dynamic variables 188 vInt := l.IndirectValue(v, errorInfo) 189 // but some Go code uses uintptr as just another integer, so ensure it is unsigned 190 switch srcTyp { 191 case "GOint64": 192 vInt = "Force.toUint32(GOint64.toInt(" + vInt + "))" 193 case "Float": 194 vInt = "Force.toUint32({var _f:Float=" + vInt + ";_f>=0?Math.floor(_f):Math.ceil(_f);})" // same as signed 195 case "Int": 196 vInt = "Force.toUint32(" + vInt + ")" 197 } 198 return register + "=" + vInt + ";" 199 case "Pointer": 200 if srcTyp == "Dynamic" { 201 _ptr := "_ptr" 202 if l.PogoComp().DebugFlag { 203 _ptr = "Pointer.check(_ptr)" 204 } 205 return register + "=({var _ptr=" + l.IndirectValue(v, errorInfo) + ";_ptr==null?null:" + 206 _ptr + ";});" 207 } 208 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - can only convert uintptr to unsafe.Pointer")) 209 return "" 210 case "String": 211 switch srcTyp { 212 case "Slice": 213 switch v.(ssa.Value).Type().Underlying().(*types.Slice).Elem().Underlying().(*types.Basic).Kind() { 214 case types.Rune: // []rune 215 return register + 216 "=Force.toRawString(this._goroutine,Go_haxegoruntime_RRunesTToUUTTFF8.callFromRT(this._goroutine," + 217 l.IndirectValue(v, errorInfo) + "));" 218 case types.Byte: // []byte 219 return register + "=Force.toRawString(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" 220 default: 221 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected slice type to convert to String")) 222 return "" 223 } 224 case "Int": // make a string from a single rune 225 //return register + "=({var _ret:String;var _r:Slice=Go_haxegoruntime_RRune2RRaw.callFromRT(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" + 226 // "_ret=\"\";for(_i in 0..._r.len())" + 227 // "_ret+=String.fromCharCode(_r.itemAddr(_i).load_int32(" + "));_ret;});" 228 return register + "=Force.stringFromRune(" + l.IndirectValue(v, errorInfo) + ");" 229 case "GOint64": // make a string from a single rune (held in 64 bits) 230 //return register + "=({var _ret:String;var _r:Slice=Go_haxegoruntime_RRune2RRaw.callFromRT(this._goroutine,GOint64.toInt(" + l.IndirectValue(v, errorInfo) + "));" + 231 // "_ret=\"\";for(_i in 0..._r.len())" + 232 // "_ret+=String.fromCharCode(_r.itemAddr(_i).load_int32(" + "));_ret;});" 233 return register + "=Force.stringFromRune(GOint64.toInt(" + l.IndirectValue(v, errorInfo) + "));" 234 case "Dynamic": 235 return register + "=cast(" + l.IndirectValue(v, errorInfo) + ",String);" 236 default: 237 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected type to convert to String: %s", srcTyp)) 238 return "" 239 } 240 case "Slice": // []rune or []byte 241 if srcTyp != "String" { 242 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected type to convert to %s ([]rune or []byte): %s", 243 langType, srcTyp)) 244 return "" 245 } 246 switch destType.Underlying().(*types.Slice).Elem().Underlying().(*types.Basic).Kind() { 247 case types.Rune: 248 //return register + "=" + newSliceCode("Int", "0", 249 // l.IndirectValue(v, errorInfo)+".length", 250 // l.IndirectValue(v, errorInfo)+".length", errorInfo, "4 /*len(rune)*/") + ";" + 251 // "for(_i in 0..." + l.IndirectValue(v, errorInfo) + ".length)" + 252 // register + ".itemAddr(_i).store_int32(({var _c:Null<Int>=" + l.IndirectValue(v, errorInfo) + 253 // `.charCodeAt(_i);(_c==null)?0:Std.int(_c)&0xff;})` + ");" + 254 // register + "=Go_haxegoruntime_Raw2Runes.callFromRT(this._goroutine," + register + ");" 255 return register + 256 "=Go_haxegoruntime_UUTTFF8toRRunes.callFromRT(this._goroutine,Force.toUTF8slice(this._goroutine," + 257 l.IndirectValue(v, errorInfo) + "));" 258 case types.Byte: 259 return register + "=Force.toUTF8slice(this._goroutine," + l.IndirectValue(v, errorInfo) + ");" 260 default: 261 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unexpected slice elementto convert to %s ([]rune/[]byte): %s", 262 langType, srcTyp)) 263 return "" 264 } 265 case "Int": 266 vInt := "" 267 switch srcTyp { 268 case "Int": 269 vInt = l.IndirectValue(v, errorInfo) // to get the type coercion below 270 case "GOint64": 271 vInt = "GOint64.toInt(" + l.IndirectValue(v, errorInfo) + ")" // un/signed OK as just truncates 272 case "Float": 273 vInt = "{var _f:Float=" + l.IndirectValue(v, errorInfo) + ";_f>=0?Math.floor(_f):Math.ceil(_f);}" 274 case "Dynamic": 275 vInt = "Force.toInt(" + l.IndirectValue(v, errorInfo) + ")" // Dynamic == uintptr 276 default: 277 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - unhandled convert to u/int from: %s", srcTyp)) 278 return "" 279 } 280 return register + "=" + l.intTypeCoersion(destType, vInt, errorInfo) + ";" 281 case "GOint64": 282 switch srcTyp { 283 case "Int": 284 if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 { 285 return register + "=GOint64.ofUInt(" + l.IndirectValue(v, errorInfo) + ");" 286 } 287 return register + "=GOint64.ofInt(" + l.IndirectValue(v, errorInfo) + ");" 288 case "Float": 289 if destType.Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 { 290 return register + "=GOint64.ofUFloat(" + l.IndirectValue(v, errorInfo) + ");" 291 } 292 return register + "=GOint64.ofFloat(" + l.IndirectValue(v, errorInfo) + ");" 293 case "Dynamic": // uintptr 294 return register + "=GOint64.ofUInt(Force.toInt(" + l.IndirectValue(v, errorInfo) + "));" // let Haxe work out how to do the cast 295 default: 296 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - unhandled convert to u/int64 from: %s", srcTyp)) 297 return "" 298 } 299 case "Float": 300 switch srcTyp { 301 case "GOint64": 302 if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 { 303 return register + "=GOint64.toUFloat(" + l.IndirectValue(v, errorInfo) + ");" 304 } 305 return register + "=GOint64.toFloat(" + l.IndirectValue(v, errorInfo) + ");" 306 case "Int": 307 if v.(ssa.Value).Type().Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 { 308 return register + "=GOint64.toUFloat(GOint64.make(0," + l.IndirectValue(v, errorInfo) + "));" 309 } 310 return register + "=Force.toFloat(" + l.IndirectValue(v, errorInfo) + ");" // just the default conversion to float required 311 case "Dynamic": 312 return register + "=GOint64.toUFloat(GOint64.ofUInt(Force.toInt(" + l.IndirectValue(v, errorInfo) + ")));" 313 case "Float": 314 if destType.Underlying().(*types.Basic).Kind() == types.Float32 { 315 return register + "=Force.toFloat32(" + 316 l.IndirectValue(v, errorInfo) + ");" // need to truncate to float32 317 } 318 return register + "=Force.toFloat(" + l.IndirectValue(v, errorInfo) + ");" 319 default: 320 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - unhandled convert to float from: %s", srcTyp)) 321 return "" 322 } 323 case "UnsafePointer": 324 //pogo.LogWarning(errorInfo, "Haxe", fmt.Errorf("converting a pointer to an Unsafe Pointer")) 325 return register + "=" + l.IndirectValue(v, errorInfo) + ";" // ALL Pointers are unsafe ? 326 default: 327 if strings.HasPrefix(srcTyp, "Array<") { 328 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - No way to convert to %s from %s ", langType, srcTyp)) 329 return "" 330 } 331 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("haxe.Convert() - Unhandled convert to %s from %s ", langType, srcTyp)) 332 //return register + "=cast(" + l.IndirectValue(v, errorInfo) + "," + langType + ");" 333 return "" 334 } 335 } 336 337 func (l langType) MakeInterface(register string, regTyp types.Type, v interface{}, errorInfo string) string { 338 ret := `new Interface(` + l.PogoComp().LogTypeUse(v.(ssa.Value).Type() /*NOT underlying()*/) + `,` + 339 l.IndirectValue(v, errorInfo) + ")" 340 if getHaxeClass(regTyp.String()) != "" { 341 ret = "Force.toHaxeParam(" + ret + ")" // as interfaces are not native to haxe, so need to convert 342 // TODO optimize when stable 343 } 344 return register + `=` + ret + ";" 345 } 346 347 func (l langType) ChangeInterface(register string, regTyp types.Type, v interface{}, errorInfo string) string { 348 l.PogoComp().LogTypeUse(regTyp) // make sure it is in the DB 349 return register + `=Interface.change(` + l.PogoComp().LogTypeUse(v.(ssa.Value).Type() /*NOT underlying()*/) + `,` + 350 l.IndirectValue(v, errorInfo) + ");" 351 } 352 353 /* from the SSA documentation: 354 The ChangeType instruction applies to X a value-preserving type change to Type(). 355 356 Type changes are permitted: 357 358 - between a named type and its underlying type. 359 - between two named types of the same underlying type. 360 - between (possibly named) pointers to identical base types. 361 - between f(T) functions and (T) func f() methods. 362 - from a bidirectional channel to a read- or write-channel, 363 optionally adding/removing a name. 364 */ 365 func (l langType) ChangeType(register string, regTyp interface{}, v interface{}, errorInfo string) string { 366 //fmt.Printf("DEBUG CHANGE TYPE: %v -- %v\n", regTyp, v) 367 switch v.(ssa.Value).(type) { 368 case *ssa.Function: 369 return register + "=" + 370 "new Closure(Go_" + l.LangName(l.PogoComp().FuncPathName(v.(*ssa.Function))) + ".call,[]);" 371 default: 372 hType := getHaxeClass(regTyp.(types.Type).String()) 373 if hType != "" { 374 switch v.(ssa.Value).Type().Underlying().(type) { 375 case *types.Interface: 376 return register + "=" + l.IndirectValue(v, errorInfo) + ".val;" 377 default: 378 return register + "=cast " + l.IndirectValue(v, errorInfo) + ";" // unsafe cast! 379 } 380 } 381 switch v.(ssa.Value).Type().Underlying().(type) { 382 case *types.Basic: 383 if v.(ssa.Value).Type().Underlying().(*types.Basic).Kind() == types.UnsafePointer { 384 /* from https://groups.google.com/forum/#!topic/golang-dev/6eDTDZPWvoM 385 Treat unsafe.Pointer -> *T conversions by returning new(T). 386 This is incorrect but at least preserves type-safety... 387 TODO decide if the above method is better than just copying the value as below 388 */ 389 return register + "=" + l.LangType(regTyp.(types.Type), true, errorInfo) + ";" 390 } 391 } 392 } 393 return register + `=` + l.IndirectValue(v, errorInfo) + ";" // usually, this is a no-op as far as Haxe is concerned 394 395 } 396 397 func (l langType) TypeAssert(register string, v ssa.Value, AssertedType types.Type, CommaOk bool, errorInfo string) string { 398 if register == "" { 399 return "" 400 } 401 if CommaOk { 402 return register + `=Interface.assertOk(` + l.PogoComp().LogTypeUse(AssertedType) + `,` + l.IndirectValue(v, errorInfo) + ");" 403 } 404 return register + `=Interface.assert(` + l.PogoComp().LogTypeUse(AssertedType) + `,` + l.IndirectValue(v, errorInfo) + ");" 405 } 406 407 func getHaxeClass(fullname string) string { // NOTE capital letter de-doubling not handled here 408 if fullname[0] != '*' { // pointers can't be Haxe types 409 bits := strings.Split(fullname, "/") 410 s := bits[len(bits)-1] // right-most bit contains the package name & type name 411 // fmt.Println("DEBUG bit to consider", s) 412 if s[0] == '_' { // leading _ on the package name means a haxe type 413 //fmt.Println("DEBUG non-pointer goType", goType) 414 splits := strings.Split(s, ".") 415 if len(splits) == 2 { // we have a package and type 416 goType := splits[1][1:] // type part only, without the leading Restrictor 417 haxeType := strings.Replace(goType, "_", ".", -1) 418 haxeType = strings.Replace(haxeType, "...", ".", -1) 419 // fmt.Println("DEBUG go->haxe found", goType, "->", haxeType) 420 return haxeType 421 } 422 } 423 } 424 return "" 425 } 426 427 /* 428 func preprocessTypeName(v string) string { 429 s := "" 430 hadbackslash := false 431 content := strings.Trim(v, `"`) 432 for _, c := range content { 433 if hadbackslash { 434 hadbackslash = false 435 s += string(c) 436 } else { 437 switch c { 438 case '"': // the reason we are here - orphan "" 439 s += "\\\"" 440 case '\\': 441 hadbackslash = true 442 s += string(c) 443 default: 444 s += string(c) 445 } 446 } 447 } 448 return s 449 } 450 451 func notInterface(t types.Type) bool { 452 isNamed := false 453 tt := t 454 named1, isNamed1 := tt.(*types.Named) 455 if isNamed1 { 456 tt = named1.Underlying() 457 isNamed = true 458 } 459 ptr, isPtr := tt.(*types.Pointer) 460 if isPtr { 461 tt = ptr.Elem() 462 } 463 named2, isNamed2 := tt.(*types.Named) 464 if isNamed2 { 465 tt = named2.Underlying() 466 isNamed = true 467 } 468 _, isInterface := tt.(*types.Interface) 469 if !isInterface && isNamed { 470 return true 471 } 472 return false 473 } 474 */ 475 476 func (l langType) buildTBI() { 477 l.hc.pte = l.PogoComp().TypesEncountered 478 l.hc.pteKeys = l.PogoComp().TypesEncountered.Keys() 479 sort.Sort(pogo.TypeSorter(l.hc.pteKeys)) 480 l.hc.typesByID = make([]types.Type, l.PogoComp().NextTypeID) 481 for k := range l.hc.pteKeys { 482 v := l.hc.pte.At(l.hc.pteKeys[k]).(int) 483 l.hc.typesByID[v] = l.hc.pteKeys[k] 484 } 485 } 486 487 func (l langType) EmitTypeInfo() string { 488 489 l.BuildTypeHaxe() // generate the code to emulate compiler reflect data output 490 491 var ret string 492 ret += "\nclass TypeInfo{\n\n" 493 494 ret += fmt.Sprintf("public static var nextTypeID=%d;\n", l.PogoComp().NextTypeID) // must be last as will change during processing 495 496 // TODO review if this is required 497 ret += "public static function isHaxeClass(id:Int):Bool {\nswitch(id){" + "\n" 498 for k := range l.hc.pteKeys { 499 v := l.hc.pte.At(l.hc.pteKeys[k]) 500 goType := l.hc.pteKeys[k].String() 501 //fmt.Println("DEBUG full goType", goType) 502 haxeClass := getHaxeClass(goType) 503 if haxeClass != "" { 504 ret += "case " + fmt.Sprintf("%d", v) + `: return true; // ` + goType + "\n" 505 } 506 } 507 ret += `default: return false;}}` + "\n" 508 509 ret += "public static function getName(id:Int):String {\n" 510 ret += "\tif(id<0||id>=nextTypeID)return \"reflect.CREATED\"+Std.string(id);\n" 511 ret += "\tif(id==0)return \"(haxeTypeID=0)\";" + "\n" 512 ret += "\t#if (js || php || node) if(id==null)return \"(haxeTypeID=null)\"; #end\n" 513 ret += "\t" + `return Go_haxegoruntime_getTTypeSString.callFromRT(0,id);` + "\n}\n" 514 ret += "public static function typeString(i:Interface):String {\nreturn getName(i.typ);\n}\n" 515 /* 516 ret += "static var typIDs:Map<String,Int> = [" 517 deDup := make(map[string]bool) 518 for k := range pteKeys { 519 v := pte.At(pteKeys[k]) 520 nam := haxeStringConst("`"+preprocessTypeName(pteKeys[k].String())+"`", "CompilerInternal:haxe.EmitTypeInfo()") 521 if len(nam) != 0 { 522 if deDup[nam] { // have one already!! 523 nam = fmt.Sprintf("%s (duplicate type name! this id=%d)\"", nam[:len(nam)-1], v) 524 } else { 525 deDup[nam] = true 526 } 527 ret += ` ` + nam + ` => ` + fmt.Sprintf("%d", v) + `,` + "\n" 528 } 529 } 530 ret += "];\n" 531 */ 532 ret += "public static function getId(name:String):Int {\n" 533 ret += "\tvar t:Int;\n" 534 //ret += "\ttry { t=typIDs[name];\n" 535 //ret += "\t} catch(x:Dynamic) { Scheduler.panicFromHaxe(\"TraceInfo.getId() not found:\"+name+x); t=-1; } ;\n" 536 ret += "\t" + `t = Go_haxegoruntime_getTTypeIIDD.callFromRT(0,name);` + "\n" 537 ret += "\treturn t;\n}\n" 538 539 //function to answer the question is the type a concrete value? 540 ret += "public static function isConcrete(t:Int):Bool {\nswitch(t){" + "\n" 541 for T := range l.hc.pteKeys { 542 t := l.hc.pte.At(l.hc.pteKeys[T]) 543 switch l.hc.pteKeys[T].Underlying().(type) { 544 case *types.Interface: 545 ret += `case ` + fmt.Sprintf("%d", t) + `: return false;` + "\n" 546 } 547 } 548 ret += "default: return true;}}\n" 549 550 //emulation of: func IsIdentical(x, y Type) bool 551 ret += "public static function isIdentical(v:Int,t:Int):Bool {\nif(v==t) return true;\nswitch(v){" + "\n" 552 for V := range l.hc.pteKeys { 553 v := l.hc.pte.At(l.hc.pteKeys[V]) 554 ret0 := "" 555 for T := range l.hc.pteKeys { 556 t := l.hc.pte.At(l.hc.pteKeys[T]) 557 if v != t && types.Identical(l.hc.pteKeys[V], l.hc.pteKeys[T]) { 558 ret0 += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n" 559 } 560 } 561 if ret0 != "" { 562 ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n" 563 ret += ret0 564 ret += "default: return false;}\n" 565 } 566 } 567 ret += "default: return false;}}\n" 568 569 ret += "}\n" 570 571 l.PogoComp().WriteAsClass("TypeInfo", ret) 572 573 ret = "class TypeAssign {" 574 575 //emulation of: func IsAssignableTo(V, T Type) bool 576 ret += "public static function isAssignableTo(v:Int,t:Int):Bool {\n\tif(v==t) return true;\n" 577 ret += "\tfor(ae in isAsssignableToArray) if(ae==(v<<16|t)) return true;\n" 578 ret += "\treturn false;\n}\n" 579 580 ret += "static var isAsssignableToArray:Array<Int> = [" 581 for V := range l.hc.pteKeys { 582 v := l.hc.pte.At(l.hc.pteKeys[V]) 583 for T := range l.hc.pteKeys { 584 t := l.hc.pte.At(l.hc.pteKeys[T]) 585 if v != t && types.AssignableTo(l.hc.pteKeys[V], l.hc.pteKeys[T]) { 586 ret += fmt.Sprintf("%d,", v.(int)<<16|t.(int)) 587 } 588 } 589 ret += "\n" 590 } 591 ret += "];\n" 592 593 ret += "}\n" 594 595 l.PogoComp().WriteAsClass("TypeAssign", ret) 596 ret = "class TypeZero {" 597 598 // function to give the zero value for each type 599 ret += "public static function zeroValue(t:Int):Dynamic {\nswitch(t){" + "\n" 600 for T := range l.hc.pteKeys { 601 t := l.hc.pte.At(l.hc.pteKeys[T]) 602 z := l.LangType(l.hc.pteKeys[T], true, "EmitTypeInfo()") 603 if z == "" { 604 z = "null" 605 } 606 if z != "null" { 607 ret += `case ` + fmt.Sprintf("%d", t) + `: return ` 608 ret += z + ";\n" 609 } 610 } 611 ret += "default: return null;}}\n" 612 613 ret += "}\n" 614 615 l.PogoComp().WriteAsClass("TypeZero", ret) 616 617 return "" 618 } 619 620 func fixKeyWds(w string) string { 621 switch w { 622 case "new": 623 return w + "_" 624 default: 625 return w 626 } 627 } 628 629 func loadStoreSuffix(T types.Type, hasParameters bool) string { 630 if bt, ok := T.Underlying().(*types.Basic); ok { 631 switch bt.Kind() { 632 case types.Bool, 633 types.Int8, 634 types.Int16, 635 types.Int64, 636 types.Uint16, 637 types.Uint64, 638 types.Uintptr, 639 types.Float32, 640 types.Float64, 641 types.Complex64, 642 types.Complex128, 643 types.String: 644 return "_" + types.TypeString(T, nil /* TODO should be?: (*types.Package).Name*/) + "(" 645 case types.Uint8: // to avoid "byte" 646 return "_uint8(" 647 case types.Int, types.Int32: // for int and to avoid "rune" 648 return "_int32(" 649 case types.Uint, types.Uint32: 650 return "_uint32(" 651 } 652 } 653 if _, ok := T.Underlying().(*types.Array); ok { 654 ret := fmt.Sprintf("_object(%d", haxeStdSizes.Sizeof(T)) 655 if hasParameters { 656 ret += "," 657 } 658 return ret 659 } 660 if _, ok := T.Underlying().(*types.Struct); ok { 661 ret := fmt.Sprintf("_object(%d", haxeStdSizes.Sizeof(T)) 662 if hasParameters { 663 ret += "," 664 } 665 return ret 666 } 667 return "(" // no suffix, so some dynamic type 668 } 669 670 // Type definitions are only carried through to Haxe to allow access to objects as if they were native Haxe classes. 671 // TODO consider renaming 672 func (l langType) TypeStart(nt *types.Named, err string) string { 673 typName := "GoType" + l.LangName("", nt.String()) 674 hxTyp := l.LangType(nt.Obj().Type(), false, nt.String()) 675 ret := "" 676 switch hxTyp { 677 case "Object": 678 ret += "class " + typName 679 ret += " extends " + hxTyp + " {\n" 680 default: 681 ret += "abstract " + typName + "(" + hxTyp + ") from " + hxTyp + " to " + hxTyp + " {\n" 682 } 683 switch nt.Underlying().(type) { 684 case *types.Struct: 685 str := nt.Underlying().(*types.Struct) 686 ret += "inline public function new(){ super new(" + strconv.Itoa(int(haxeStdSizes.Sizeof(nt.Obj().Type()))) + "); }\n" 687 flds := []string{} 688 for f := 0; f < str.NumFields(); f++ { 689 fName := str.Field(f).Name() 690 if len(fName) > 0 { 691 if unicode.IsUpper(rune(fName[0])) { 692 flds = append(flds, fName) 693 } 694 } 695 } 696 sort.Strings(flds) // make sure the fields are always in the same order in the file 697 for _, fName := range flds { 698 for f := 0; f < str.NumFields(); f++ { 699 if fName == str.Field(f).Name() { 700 haxeTyp := l.LangType(str.Field(f).Type(), false, nt.String()) 701 fOff := fieldOffset(str, f) 702 sfx := loadStoreSuffix(str.Field(f).Type(), true) 703 ret += fmt.Sprintf("public var _%s(get,set):%s;\n", fName, haxeTyp) 704 ret += fmt.Sprintf("function get__%s():%s { return get%s%d); }\n", 705 fName, haxeTyp, sfx, fOff) 706 ret += fmt.Sprintf("function set__%s(v:%s):%s { return set%s%d,v); }\n", 707 fName, haxeTyp, haxeTyp, sfx, fOff) 708 break 709 } 710 } 711 } 712 case *types.Array: 713 ret += "inline public function new(){ super new(" + strconv.Itoa(int(haxeStdSizes.Sizeof(nt.Obj().Type()))) + "); }\n" 714 default: // TODO not yet sure how to handle named types that are not structs 715 ret += "inline public function new(v:" + hxTyp + ") { this = v; }\n" 716 } 717 718 meths := []string{} 719 for m := 0; m < nt.NumMethods(); m++ { 720 mName := nt.Method(m).Name() 721 if len(mName) > 0 { 722 if unicode.IsUpper(rune(mName[0])) { 723 meths = append(meths, mName) 724 } 725 } 726 } 727 sort.Strings(meths) // make sure the methods always appear in the same order in the file 728 for _, mName := range meths { 729 for m := 0; m < nt.NumMethods(); m++ { 730 meth := nt.Method(m) 731 if mName == meth.Name() { 732 sig := meth.Type().(*types.Signature) 733 ret += "// " + mName + " " + sig.String() + "\n" 734 ret += "public function _" + mName + "(" 735 for p := 0; p < sig.Params().Len(); p++ { 736 if p > 0 { 737 ret += "," 738 } 739 ret += "_" + sig.Params().At(p).Name() + ":" + l.LangType(sig.Params().At(p).Type(), false, nt.String()) 740 } 741 ret += ")" 742 switch sig.Results().Len() { 743 case 0: 744 ret += ":Void " 745 case 1: 746 ret += ":" + l.LangType(sig.Results().At(0).Type(), false, nt.String()) 747 default: 748 ret += ":{" 749 for r := 0; r < sig.Results().Len(); r++ { 750 if r > 0 { 751 ret += "," 752 } 753 ret += fmt.Sprintf("r%d:%s", r, l.LangType(sig.Results().At(r).Type(), false, nt.String())) 754 } 755 ret += "}" 756 } 757 ret += "{\n\t" 758 if sig.Results().Len() > 0 { 759 ret += "return " 760 } 761 fnToCall := l.LangName( 762 nt.Obj().Pkg().Name()+":"+sig.Recv().Type().String(), 763 meth.Name()) 764 ret += `Go_` + fnToCall + `.hx(this` 765 for p := 0; p < sig.Params().Len(); p++ { 766 ret += ", _" + sig.Params().At(p).Name() 767 } 768 ret += ");\n}\n" 769 } 770 } 771 } 772 773 l.PogoComp().WriteAsClass(typName, ret+"}\n") 774 775 return "" //ret 776 }