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  }