
This commit makes sure all Go types can be encoded in the interface type code, so that Type.Kind() always returns a proper type kind for any non-nil interface.
156 строки
4,6 КиБ
Go
156 строки
4,6 КиБ
Go
package compiler
|
|
|
|
import (
|
|
"math/big"
|
|
"strings"
|
|
)
|
|
|
|
var basicTypes = map[string]int64{
|
|
"bool": 1,
|
|
"int": 2,
|
|
"int8": 3,
|
|
"int16": 4,
|
|
"int32": 5,
|
|
"int64": 6,
|
|
"uint": 7,
|
|
"uint8": 8,
|
|
"uint16": 9,
|
|
"uint32": 10,
|
|
"uint64": 11,
|
|
"uintptr": 12,
|
|
"float32": 13,
|
|
"float64": 14,
|
|
"complex64": 15,
|
|
"complex128": 16,
|
|
"string": 17,
|
|
"unsafeptr": 18,
|
|
}
|
|
|
|
func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
|
|
fn := c.mod.NamedFunction("reflect.ValueOf")
|
|
if fn.IsNil() {
|
|
// reflect.ValueOf is never used, so we can use the most efficient
|
|
// encoding possible.
|
|
for i, t := range typeSlice {
|
|
t.num = uint64(i + 1)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Assign typecodes the way the reflect package expects.
|
|
fallbackIndex := 1
|
|
namedTypes := make(map[string]int)
|
|
for _, t := range typeSlice {
|
|
if t.name[:5] != "type:" {
|
|
panic("expected type name to start with 'type:'")
|
|
}
|
|
num := c.getTypeCodeNum(t.name[5:], &fallbackIndex, namedTypes)
|
|
if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
|
|
// TODO: support this in some way, using a side table for example.
|
|
// That's less efficient but better than not working at all.
|
|
// Particularly important on systems with 16-bit pointers (e.g.
|
|
// AVR).
|
|
panic("compiler: could not store type code number inside interface type code")
|
|
}
|
|
t.num = num.Uint64()
|
|
}
|
|
}
|
|
|
|
// getTypeCodeNum returns the typecode for a given type as expected by the
|
|
// reflect package. Also see getTypeCodeName, which serializes types to a string
|
|
// based on a types.Type value for this function.
|
|
func (c *Compiler) getTypeCodeNum(id string, fallbackIndex *int, namedTypes map[string]int) *big.Int {
|
|
// Note: see src/reflect/type.go for bit allocations.
|
|
// A type can be named or unnamed. Example of both:
|
|
// basic:~foo:uint64
|
|
// basic:uint64
|
|
// Extract the class (basic, slice, pointer, etc.), the name, and the
|
|
// contents of this type ID string. Allocate bits based on that, as
|
|
// src/runtime/types.go expects.
|
|
class := id[:strings.IndexByte(id, ':')]
|
|
value := id[len(class)+1:]
|
|
name := ""
|
|
if value[0] == '~' {
|
|
name = value[1:strings.IndexByte(value, ':')]
|
|
value = value[len(name)+2:]
|
|
}
|
|
if class == "basic" {
|
|
// Basic types follow the following bit pattern:
|
|
// ...xxxxx0
|
|
// where xxxxx is allocated for the 18 possible basic types and all the
|
|
// upper bits are used to indicate the named type.
|
|
num, ok := basicTypes[value]
|
|
if !ok {
|
|
panic("invalid basic type: " + id)
|
|
}
|
|
if name != "" {
|
|
// This type is named, set the upper bits to the name ID.
|
|
num |= int64(getNamedTypeNum(namedTypes, name)) << 5
|
|
}
|
|
return big.NewInt(num << 1)
|
|
} else {
|
|
// Complex types use the following bit pattern:
|
|
// ...nxxx1
|
|
// where xxx indicates the complex type (any non-basic type). The upper
|
|
// bits contain whatever the type contains. Types that wrap a single
|
|
// other type (channel, interface, pointer, slice) just contain the bits
|
|
// of the wrapped type. Other types (like struct) have a different
|
|
// method of encoding the contents of the type.
|
|
var num *big.Int
|
|
var classNumber int64
|
|
switch class {
|
|
case "chan":
|
|
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
|
|
classNumber = 0
|
|
case "interface":
|
|
num = big.NewInt(int64(*fallbackIndex))
|
|
*fallbackIndex++
|
|
classNumber = 1
|
|
case "pointer":
|
|
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
|
|
classNumber = 2
|
|
case "slice":
|
|
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
|
|
classNumber = 3
|
|
case "array":
|
|
num = big.NewInt(int64(*fallbackIndex))
|
|
*fallbackIndex++
|
|
classNumber = 4
|
|
case "func":
|
|
num = big.NewInt(int64(*fallbackIndex))
|
|
*fallbackIndex++
|
|
classNumber = 5
|
|
case "map":
|
|
num = big.NewInt(int64(*fallbackIndex))
|
|
*fallbackIndex++
|
|
classNumber = 6
|
|
case "struct":
|
|
num = big.NewInt(int64(*fallbackIndex))
|
|
*fallbackIndex++
|
|
classNumber = 7
|
|
default:
|
|
panic("unknown type kind: " + id)
|
|
}
|
|
if name == "" {
|
|
num.Lsh(num, 5).Or(num, big.NewInt((classNumber<<1)+1))
|
|
} else {
|
|
// TODO: store num in a sidetable
|
|
num = big.NewInt(int64(getNamedTypeNum(namedTypes, name))<<1 | 1)
|
|
num.Lsh(num, 4).Or(num, big.NewInt((classNumber<<1)+1))
|
|
}
|
|
return num
|
|
}
|
|
}
|
|
|
|
// getNamedTypeNum returns an appropriate (unique) number for the given named
|
|
// type. If the name already has a number that number is returned, else a new
|
|
// number is returned. The number is always non-zero.
|
|
func getNamedTypeNum(namedTypes map[string]int, name string) int {
|
|
if num, ok := namedTypes[name]; ok {
|
|
return num
|
|
} else {
|
|
num = len(namedTypes) + 1
|
|
namedTypes[name] = num
|
|
return num
|
|
}
|
|
}
|