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.
Этот коммит содержится в:
родитель
101f2e519b
коммит
dfef168139
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
предоставленный
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
предоставленный
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
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче