reflect: add limited support for all type kinds

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.
Этот коммит содержится в:
Ayke van Laethem 2019-01-20 17:30:27 +01:00
родитель 101f2e519b
коммит dfef168139
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
6 изменённых файлов: 377 добавлений и 66 удалений

Просмотреть файл

@ -8,6 +8,8 @@ package compiler
import (
"go/token"
"go/types"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/ir"
"golang.org/x/tools/go/ssa"
@ -92,55 +94,91 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
// interface lowering pass to assign type codes as expected by the reflect
// package. See getTypeCodeNum.
func getTypeCodeName(t types.Type) string {
name := ""
if named, ok := t.(*types.Named); ok {
name = "~" + named.String() + ":"
t = t.Underlying()
}
switch t := t.(type) {
case *types.Array:
return "array:" + name + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic:
var name string
var kind string
switch t.Kind() {
case types.Bool:
name = "bool"
kind = "bool"
case types.Int:
name = "int"
kind = "int"
case types.Int8:
name = "int8"
kind = "int8"
case types.Int16:
name = "int16"
kind = "int16"
case types.Int32:
name = "int32"
kind = "int32"
case types.Int64:
name = "int64"
kind = "int64"
case types.Uint:
name = "uint"
kind = "uint"
case types.Uint8:
name = "uint8"
kind = "uint8"
case types.Uint16:
name = "uint16"
kind = "uint16"
case types.Uint32:
name = "uint32"
kind = "uint32"
case types.Uint64:
name = "uint64"
kind = "uint64"
case types.Uintptr:
name = "uintptr"
kind = "uintptr"
case types.Float32:
name = "float32"
kind = "float32"
case types.Float64:
name = "float64"
kind = "float64"
case types.Complex64:
name = "complex64"
kind = "complex64"
case types.Complex128:
name = "complex128"
kind = "complex128"
case types.String:
name = "string"
kind = "string"
case types.UnsafePointer:
name = "unsafeptr"
kind = "unsafeptr"
default:
panic("unknown basic type: " + t.Name())
}
return "basic:" + name
return "basic:" + name + kind
case *types.Chan:
return "chan:" + name + getTypeCodeName(t.Elem())
case *types.Interface:
methods := make([]string, t.NumMethods())
for i := 0; i < t.NumMethods(); i++ {
methods[i] = getTypeCodeName(t.Method(i).Type())
}
return "interface:" + name + "{" + strings.Join(methods, ",") + "}"
case *types.Map:
keyType := getTypeCodeName(t.Key())
elemType := getTypeCodeName(t.Elem())
return "map:" + name + "{" + keyType + "," + elemType + "}"
case *types.Pointer:
return "pointer:" + name + getTypeCodeName(t.Elem())
case *types.Signature:
params := make([]string, t.Params().Len())
for i := 0; i < t.Params().Len(); i++ {
params[i] = getTypeCodeName(t.Params().At(i).Type())
}
results := make([]string, t.Results().Len())
for i := 0; i < t.Results().Len(); i++ {
results[i] = getTypeCodeName(t.Results().At(i).Type())
}
return "func:" + name + "{" + strings.Join(params, ",") + "}{" + strings.Join(results, ",") + "}"
case *types.Slice:
return "slice:" + getTypeCodeName(t.Elem())
return "slice:" + name + getTypeCodeName(t.Elem())
case *types.Struct:
elems := make([]string, t.NumFields())
for i := 0; i < t.NumFields(); i++ {
elems[i] = getTypeCodeName(t.Field(i).Type())
}
return "struct:" + name + "{" + strings.Join(elems, ",") + "}"
default:
// Unknown type, fall back to the .String() method for identification.
return "other:" + t.String()
panic("unknown type: " + t.String())
}
}

Просмотреть файл

@ -39,18 +39,12 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
// 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:])
if num == nil {
// Fallback/unsupported types have a typecode with the lowest bits
// set to 11.
t.num = uint64(fallbackIndex<<2 | 3)
fallbackIndex++
continue
}
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.
@ -65,20 +59,98 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
// 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(name string) *big.Int {
if strings.HasPrefix(name, "basic:") {
// Basic types have a typecode with the lowest bits set to 00.
num, ok := basicTypes[name[len("basic:"):]]
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: " + name)
panic("invalid basic type: " + id)
}
return big.NewInt(num<<2 | 0)
} else if strings.HasPrefix(name, "slice:") {
// Slices have a typecode with the lowest bits set to 01.
num := c.getTypeCodeNum(name[len("slice:"):])
num.Lsh(num, 2).Or(num, big.NewInt(1))
return num
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 {
return nil
// 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
}
}

Просмотреть файл

@ -4,10 +4,26 @@ import (
"unsafe"
)
// A Kind is the number that the compiler uses for this type.
// The compiler uses a compact encoding to store type information. Unlike the
// main Go compiler, most of the types are stored directly in the type code.
//
// Not used directly. These types are all replaced with the number the compiler
// uses internally for the type.
// Type code bit allocation:
// xxxxx0: basic types, where xxxxx is the basic type number (never 0).
// The higher bits indicate the named type, if any.
// nxxx1: complex types, where n indicates whether this is a named type (named
// if set) and xxx contains the type kind number:
// 0 (0001): Chan
// 1 (0011): Interface
// 2 (0101): Ptr
// 3 (0111): Slice
// 4 (1001): Array
// 5 (1011): Func
// 6 (1101): Map
// 7 (1111): Struct
// The higher bits are either the contents of the type depending on the
// type (if n is clear) or indicate the number of the named type (if n
// is set).
type Kind uintptr
// Copied from reflect/type.go
@ -32,13 +48,13 @@ const (
Complex128
String
UnsafePointer
Array
Chan
Func
Interface
Map
Ptr
Slice
Array
Func
Map
Struct
)
@ -80,8 +96,22 @@ func (k Kind) String() string {
return "string"
case UnsafePointer:
return "unsafe.Pointer"
case Chan:
return "chan"
case Interface:
return "interface"
case Ptr:
return "ptr"
case Slice:
return "slice"
case Array:
return "array"
case Func:
return "func"
case Map:
return "map"
case Struct:
return "struct"
default:
return "invalid"
}
@ -89,7 +119,7 @@ func (k Kind) String() string {
// basicType returns a new Type for this kind if Kind is a basic type.
func (k Kind) basicType() Type {
return Type(k << 2 | 0)
return Type(k << 1)
}
// The typecode as used in an interface{}.
@ -104,22 +134,22 @@ func (t Type) String() string {
}
func (t Type) Kind() Kind {
if t % 4 == 0 {
// Basic type
return Kind(t >> 2)
} else if t % 4 == 1 {
// Slice
return Slice
if t % 2 == 0 {
// basic type
return Kind((t >> 1) % 32)
} else {
return Invalid // TODO
return Kind(t >> 1) % 8 + 19
}
}
func (t Type) Elem() Type {
switch t.Kind() {
case Slice:
return t >> 2
default: // not implemented: Array, Chan, Map, Ptr
case Chan, Ptr, Slice:
if (t >> 4) % 2 != 0 {
panic("unimplemented: (reflect.Type).Elem() for named types")
}
return t >> 5
default: // not implemented: Array, Map
panic("unimplemented: (reflect.Type).Elem()")
}
}
@ -164,7 +194,7 @@ func (t Type) Size() uintptr {
return 16
case String:
return unsafe.Sizeof(StringHeader{})
case UnsafePointer:
case UnsafePointer, Chan, Map, Ptr:
return unsafe.Sizeof(uintptr(0))
case Slice:
return unsafe.Sizeof(SliceHeader{})

Просмотреть файл

@ -31,16 +31,38 @@ func (v Value) Kind() Kind {
}
func (v Value) IsNil() bool {
panic("unimplemented: (reflect.Value).IsNil()")
switch v.Kind() {
case Chan, Map, Ptr:
return v.value == nil
case Func:
if v.value == nil {
return true
}
fn := (*funcHeader)(v.value)
return fn.Code == nil
case Slice:
if v.value == nil {
return true
}
slice := (*SliceHeader)(v.value)
return slice.Data == 0
case Interface:
panic("unimplemented: (reflect.Value).IsNil()")
default:
panic(&ValueError{"IsNil"})
}
}
func (v Value) Pointer() uintptr {
switch v.Kind() {
case UnsafePointer:
case Chan, Map, Ptr, UnsafePointer:
return uintptr(v.value)
case Chan, Func, Map, Ptr, Slice:
case Slice:
slice := (*SliceHeader)(v.value)
return slice.Data
case Func:
panic("unimplemented: (reflect.Value).Pointer()")
default:
default: // not implemented: Func
panic(&ValueError{"Pointer"})
}
}
@ -322,6 +344,11 @@ func MakeSlice(typ Type, len, cap int) Value {
panic("unimplemented: reflect.MakeSlice()")
}
type funcHeader struct {
Context unsafe.Pointer
Code unsafe.Pointer
}
type SliceHeader struct {
Data uintptr
Len uintptr

81
testdata/reflect.go предоставленный
Просмотреть файл

@ -5,16 +5,29 @@ import (
"unsafe"
)
type myint int
type (
myint int
myslice []byte
myslice2 []myint
)
func main() {
println("matching types")
println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5)))
println(reflect.TypeOf(int(3)) == reflect.TypeOf(uint(5)))
println(reflect.TypeOf(myint(3)) == reflect.TypeOf(int(5)))
println(reflect.TypeOf(myslice{}) == reflect.TypeOf([]byte{}))
println(reflect.TypeOf(myslice2{}) == reflect.TypeOf([]myint{}))
println(reflect.TypeOf(myslice2{}) == reflect.TypeOf([]int{}))
println("\nvalues of interfaces")
var zeroSlice []byte
var zeroFunc func()
var zeroMap map[string]int
var zeroChan chan int
n := 42
for _, v := range []interface{}{
// basic types
true,
false,
int(2000),
@ -37,15 +50,57 @@ func main() {
float64(3.14),
complex64(1.2 + 0.3i),
complex128(1.3 + 0.4i),
myint(32),
"foo",
unsafe.Pointer(new(int)),
// channels
zeroChan,
// pointers
new(int),
new(error),
&n,
// slices
[]byte{1, 2, 3},
make([]uint8, 2, 5),
[]rune{3, 5},
[]string{"xyz", "Z"},
zeroSlice,
[]byte{},
// array
[4]int{1, 2, 3, 4},
// functions
zeroFunc,
emptyFunc,
// maps
zeroMap,
map[string]int{},
// structs
struct{}{},
struct{ error }{},
} {
showValue(v, "")
}
// test sizes
println("\nsizes:")
println("int8", int(reflect.TypeOf(int8(0)).Size()))
println("int16", int(reflect.TypeOf(int16(0)).Size()))
println("int32", int(reflect.TypeOf(int32(0)).Size()))
println("int64", int(reflect.TypeOf(int64(0)).Size()))
println("uint8", int(reflect.TypeOf(uint8(0)).Size()))
println("uint16", int(reflect.TypeOf(uint16(0)).Size()))
println("uint32", int(reflect.TypeOf(uint32(0)).Size()))
println("uint64", int(reflect.TypeOf(uint64(0)).Size()))
println("float32", int(reflect.TypeOf(float32(0)).Size()))
println("float64", int(reflect.TypeOf(float64(0)).Size()))
println("complex64", int(reflect.TypeOf(complex64(0)).Size()))
println("complex128", int(reflect.TypeOf(complex128(0)).Size()))
assertSize(reflect.TypeOf(uintptr(0)).Size() == unsafe.Sizeof(uintptr(0)), "uintptr")
assertSize(reflect.TypeOf("").Size() == unsafe.Sizeof(""), "string")
assertSize(reflect.TypeOf(new(int)).Size() == unsafe.Sizeof(new(int)), "*int")
}
func emptyFunc() {
}
func showValue(v interface{}, indent string) {
@ -79,13 +134,37 @@ func showValue(v interface{}, indent string) {
}
case reflect.UnsafePointer:
println(indent+" pointer:", rv.Pointer() != 0)
case reflect.Array:
println(indent + " array")
case reflect.Chan:
println(indent+" chan:", rt.Elem().Kind().String())
println(indent+" nil:", rv.IsNil())
case reflect.Func:
println(indent + " func")
println(indent+" nil:", rv.IsNil())
case reflect.Map:
println(indent + " map")
println(indent+" nil:", rv.IsNil())
case reflect.Ptr:
println(indent+" pointer:", rv.Pointer() != 0, rt.Elem().Kind().String())
println(indent+" nil:", rv.IsNil())
case reflect.Slice:
println(indent+" slice:", rt.Elem().Kind().String(), rv.Len(), rv.Cap())
println(indent+" pointer:", rv.Pointer() != 0)
println(indent+" nil:", rv.IsNil())
for i := 0; i < rv.Len(); i++ {
println(indent+" indexing:", i)
showValue(rv.Index(i).Interface(), indent+" ")
}
case reflect.Struct:
println(indent + " struct")
default:
println(indent + " unknown type kind!")
}
}
func assertSize(ok bool, typ string) {
if !ok {
panic("size mismatch for type " + typ)
}
}

65
testdata/reflect.txt предоставленный
Просмотреть файл

@ -2,6 +2,9 @@ matching types
true
false
false
false
false
false
values of interfaces
reflect type: bool
@ -48,6 +51,8 @@ reflect type: complex64
complex: (+1.200000e+000+3.000000e-001i)
reflect type: complex128
complex: (+1.300000e+000+4.000000e-001i)
reflect type: int
int: 32
reflect type: string
string: foo 3
reflect type: uint8
@ -58,8 +63,22 @@ reflect type: string
uint: 111
reflect type: unsafe.Pointer
pointer: true
reflect type: chan
chan: int
nil: true
reflect type: ptr
pointer: true int
nil: false
reflect type: ptr
pointer: true interface
nil: false
reflect type: ptr
pointer: true int
nil: false
reflect type: slice
slice: uint8 3 3
pointer: true
nil: false
indexing: 0
reflect type: uint8
uint: 1
@ -71,6 +90,8 @@ reflect type: slice
uint: 3
reflect type: slice
slice: uint8 2 5
pointer: true
nil: false
indexing: 0
reflect type: uint8
uint: 0
@ -79,6 +100,8 @@ reflect type: slice
uint: 0
reflect type: slice
slice: int32 2 2
pointer: true
nil: false
indexing: 0
reflect type: int32
int: 3
@ -87,6 +110,8 @@ reflect type: slice
int: 5
reflect type: slice
slice: string 2 2
pointer: true
nil: false
indexing: 0
reflect type: string
string: xyz 3
@ -101,3 +126,43 @@ reflect type: slice
string: Z 1
reflect type: uint8
uint: 90
reflect type: slice
slice: uint8 0 0
pointer: false
nil: true
reflect type: slice
slice: uint8 0 0
pointer: true
nil: false
reflect type: array
array
reflect type: func
func
nil: true
reflect type: func
func
nil: false
reflect type: map
map
nil: true
reflect type: map
map
nil: false
reflect type: struct
struct
reflect type: struct
struct
sizes:
int8 1
int16 2
int32 4
int64 8
uint8 1
uint16 2
uint32 4
uint64 8
float32 4
float64 8
complex64 8
complex128 16