github.com/aykevl/tinygo@v0.5.0/compiler/reflect.go (about) 1 package compiler 2 3 import ( 4 "math/big" 5 "strings" 6 ) 7 8 var basicTypes = map[string]int64{ 9 "bool": 1, 10 "int": 2, 11 "int8": 3, 12 "int16": 4, 13 "int32": 5, 14 "int64": 6, 15 "uint": 7, 16 "uint8": 8, 17 "uint16": 9, 18 "uint32": 10, 19 "uint64": 11, 20 "uintptr": 12, 21 "float32": 13, 22 "float64": 14, 23 "complex64": 15, 24 "complex128": 16, 25 "string": 17, 26 "unsafeptr": 18, 27 } 28 29 func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) { 30 fn := c.mod.NamedFunction("reflect.ValueOf") 31 if fn.IsNil() { 32 // reflect.ValueOf is never used, so we can use the most efficient 33 // encoding possible. 34 for i, t := range typeSlice { 35 t.num = uint64(i + 1) 36 } 37 return 38 } 39 40 // Assign typecodes the way the reflect package expects. 41 fallbackIndex := 1 42 namedTypes := make(map[string]int) 43 for _, t := range typeSlice { 44 if t.name[:5] != "type:" { 45 panic("expected type name to start with 'type:'") 46 } 47 num := c.getTypeCodeNum(t.name[5:], &fallbackIndex, namedTypes) 48 if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() { 49 // TODO: support this in some way, using a side table for example. 50 // That's less efficient but better than not working at all. 51 // Particularly important on systems with 16-bit pointers (e.g. 52 // AVR). 53 panic("compiler: could not store type code number inside interface type code") 54 } 55 t.num = num.Uint64() 56 } 57 } 58 59 // getTypeCodeNum returns the typecode for a given type as expected by the 60 // reflect package. Also see getTypeCodeName, which serializes types to a string 61 // based on a types.Type value for this function. 62 func (c *Compiler) getTypeCodeNum(id string, fallbackIndex *int, namedTypes map[string]int) *big.Int { 63 // Note: see src/reflect/type.go for bit allocations. 64 // A type can be named or unnamed. Example of both: 65 // basic:~foo:uint64 66 // basic:uint64 67 // Extract the class (basic, slice, pointer, etc.), the name, and the 68 // contents of this type ID string. Allocate bits based on that, as 69 // src/runtime/types.go expects. 70 class := id[:strings.IndexByte(id, ':')] 71 value := id[len(class)+1:] 72 name := "" 73 if value[0] == '~' { 74 name = value[1:strings.IndexByte(value, ':')] 75 value = value[len(name)+2:] 76 } 77 if class == "basic" { 78 // Basic types follow the following bit pattern: 79 // ...xxxxx0 80 // where xxxxx is allocated for the 18 possible basic types and all the 81 // upper bits are used to indicate the named type. 82 num, ok := basicTypes[value] 83 if !ok { 84 panic("invalid basic type: " + id) 85 } 86 if name != "" { 87 // This type is named, set the upper bits to the name ID. 88 num |= int64(getNamedTypeNum(namedTypes, name)) << 5 89 } 90 return big.NewInt(num << 1) 91 } else { 92 // Complex types use the following bit pattern: 93 // ...nxxx1 94 // where xxx indicates the complex type (any non-basic type). The upper 95 // bits contain whatever the type contains. Types that wrap a single 96 // other type (channel, interface, pointer, slice) just contain the bits 97 // of the wrapped type. Other types (like struct) have a different 98 // method of encoding the contents of the type. 99 var num *big.Int 100 var classNumber int64 101 switch class { 102 case "chan": 103 num = c.getTypeCodeNum(value, fallbackIndex, namedTypes) 104 classNumber = 0 105 case "interface": 106 num = big.NewInt(int64(*fallbackIndex)) 107 *fallbackIndex++ 108 classNumber = 1 109 case "pointer": 110 num = c.getTypeCodeNum(value, fallbackIndex, namedTypes) 111 classNumber = 2 112 case "slice": 113 num = c.getTypeCodeNum(value, fallbackIndex, namedTypes) 114 classNumber = 3 115 case "array": 116 num = big.NewInt(int64(*fallbackIndex)) 117 *fallbackIndex++ 118 classNumber = 4 119 case "func": 120 num = big.NewInt(int64(*fallbackIndex)) 121 *fallbackIndex++ 122 classNumber = 5 123 case "map": 124 num = big.NewInt(int64(*fallbackIndex)) 125 *fallbackIndex++ 126 classNumber = 6 127 case "struct": 128 num = big.NewInt(int64(*fallbackIndex)) 129 *fallbackIndex++ 130 classNumber = 7 131 default: 132 panic("unknown type kind: " + id) 133 } 134 if name == "" { 135 num.Lsh(num, 5).Or(num, big.NewInt((classNumber<<1)+1)) 136 } else { 137 // TODO: store num in a sidetable 138 num = big.NewInt(int64(getNamedTypeNum(namedTypes, name))<<1 | 1) 139 num.Lsh(num, 4).Or(num, big.NewInt((classNumber<<1)+1)) 140 } 141 return num 142 } 143 } 144 145 // getNamedTypeNum returns an appropriate (unique) number for the given named 146 // type. If the name already has a number that number is returned, else a new 147 // number is returned. The number is always non-zero. 148 func getNamedTypeNum(namedTypes map[string]int, name string) int { 149 if num, ok := namedTypes[name]; ok { 150 return num 151 } else { 152 num = len(namedTypes) + 1 153 namedTypes[name] = num 154 return num 155 } 156 }