1269 строки
33 КиБ
Go
1269 строки
33 КиБ
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Type information of an interface is stored as a pointer to a global in the
|
|
// interface type (runtime._interface). This is called a type struct.
|
|
// It always starts with a byte that contains both the type kind and a few
|
|
// flags. In most cases it also contains a pointer to another type struct
|
|
// (ptrTo), that is the pointer type of the current type (for example, type int
|
|
// also has a pointer to the type *int). The exception is pointer types, to
|
|
// avoid infinite recursion.
|
|
//
|
|
// The layouts specifically look like this:
|
|
// - basic types (Bool..UnsafePointer):
|
|
// meta uint8 // actually: kind + flags
|
|
// ptrTo *typeStruct
|
|
// - channels and slices (see elemType):
|
|
// meta uint8
|
|
// nmethods uint16 (0)
|
|
// ptrTo *typeStruct
|
|
// elementType *typeStruct // the type that you get with .Elem()
|
|
// - pointer types (see ptrType, this doesn't include chan, map, etc):
|
|
// meta uint8
|
|
// nmethods uint16
|
|
// elementType *typeStruct
|
|
// - array types (see arrayType)
|
|
// meta uint8
|
|
// nmethods uint16 (0)
|
|
// ptrTo *typeStruct
|
|
// elem *typeStruct // element type of the array
|
|
// arrayLen uintptr // length of the array (this is part of the type)
|
|
// - map types (this is still missing the key and element types)
|
|
// meta uint8
|
|
// nmethods uint16 (0)
|
|
// ptrTo *typeStruct
|
|
// elem *typeStruct
|
|
// key *typeStruct
|
|
// - struct types (see structType):
|
|
// meta uint8
|
|
// nmethods uint16
|
|
// ptrTo *typeStruct
|
|
// pkgpath *byte // package path; null terminated
|
|
// numField uint16
|
|
// fields [...]structField // the remaining fields are all of type structField
|
|
// - interface types (this is missing the interface methods):
|
|
// meta uint8
|
|
// ptrTo *typeStruct
|
|
// - signature types (this is missing input and output parameters):
|
|
// meta uint8
|
|
// ptrTo *typeStruct
|
|
// - named types
|
|
// meta uint8
|
|
// nmethods uint16 // number of methods
|
|
// ptrTo *typeStruct
|
|
// elem *typeStruct // underlying type
|
|
// pkgpath *byte // pkgpath; null terminated
|
|
// name [1]byte // actual name; null terminated
|
|
//
|
|
// The type struct is essentially a union of all the above types. Which it is,
|
|
// can be determined by looking at the meta byte.
|
|
|
|
package reflect
|
|
|
|
import (
|
|
"internal/itoa"
|
|
"unsafe"
|
|
)
|
|
|
|
// Flags stored in the first byte of the struct field byte array. Must be kept
|
|
// up to date with compiler/interface.go.
|
|
const (
|
|
structFieldFlagAnonymous = 1 << iota
|
|
structFieldFlagHasTag
|
|
structFieldFlagIsExported
|
|
structFieldFlagIsEmbedded
|
|
)
|
|
|
|
type Kind uint8
|
|
|
|
// Copied from reflect/type.go
|
|
// https://golang.org/src/reflect/type.go?s=8302:8316#L217
|
|
// These constants must match basicTypes and the typeKind* constants in
|
|
// compiler/interface.go
|
|
const (
|
|
Invalid Kind = iota
|
|
Bool
|
|
Int
|
|
Int8
|
|
Int16
|
|
Int32
|
|
Int64
|
|
Uint
|
|
Uint8
|
|
Uint16
|
|
Uint32
|
|
Uint64
|
|
Uintptr
|
|
Float32
|
|
Float64
|
|
Complex64
|
|
Complex128
|
|
String
|
|
UnsafePointer
|
|
Chan
|
|
Interface
|
|
Pointer
|
|
Slice
|
|
Array
|
|
Func
|
|
Map
|
|
Struct
|
|
)
|
|
|
|
// Ptr is the old name for the Pointer kind.
|
|
const Ptr = Pointer
|
|
|
|
func (k Kind) String() string {
|
|
switch k {
|
|
case Bool:
|
|
return "bool"
|
|
case Int:
|
|
return "int"
|
|
case Int8:
|
|
return "int8"
|
|
case Int16:
|
|
return "int16"
|
|
case Int32:
|
|
return "int32"
|
|
case Int64:
|
|
return "int64"
|
|
case Uint:
|
|
return "uint"
|
|
case Uint8:
|
|
return "uint8"
|
|
case Uint16:
|
|
return "uint16"
|
|
case Uint32:
|
|
return "uint32"
|
|
case Uint64:
|
|
return "uint64"
|
|
case Uintptr:
|
|
return "uintptr"
|
|
case Float32:
|
|
return "float32"
|
|
case Float64:
|
|
return "float64"
|
|
case Complex64:
|
|
return "complex64"
|
|
case Complex128:
|
|
return "complex128"
|
|
case String:
|
|
return "string"
|
|
case UnsafePointer:
|
|
return "unsafe.Pointer"
|
|
case Chan:
|
|
return "chan"
|
|
case Interface:
|
|
return "interface"
|
|
case Pointer:
|
|
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"
|
|
}
|
|
}
|
|
|
|
// Copied from reflect/type.go
|
|
// https://go.dev/src/reflect/type.go?#L348
|
|
|
|
// ChanDir represents a channel type's direction.
|
|
type ChanDir int
|
|
|
|
const (
|
|
RecvDir ChanDir = 1 << iota // <-chan
|
|
SendDir // chan<-
|
|
BothDir = RecvDir | SendDir // chan
|
|
)
|
|
|
|
// Method represents a single method.
|
|
type Method struct {
|
|
// Name is the method name.
|
|
Name string
|
|
|
|
// PkgPath is the package path that qualifies a lower case (unexported)
|
|
// method name. It is empty for upper case (exported) method names.
|
|
// The combination of PkgPath and Name uniquely identifies a method
|
|
// in a method set.
|
|
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
|
|
PkgPath string
|
|
|
|
Type Type // method type
|
|
Func Value // func with receiver as first argument
|
|
Index int // index for Type.Method
|
|
}
|
|
|
|
// The following Type type has been copied almost entirely from
|
|
// https://github.com/golang/go/blob/go1.15/src/reflect/type.go#L27-L212.
|
|
// Some methods have been commented out as they haven't yet been implemented.
|
|
|
|
// Type is the representation of a Go type.
|
|
//
|
|
// Not all methods apply to all kinds of types. Restrictions,
|
|
// if any, are noted in the documentation for each method.
|
|
// Use the Kind method to find out the kind of type before
|
|
// calling kind-specific methods. Calling a method
|
|
// inappropriate to the kind of type causes a run-time panic.
|
|
//
|
|
// Type values are comparable, such as with the == operator,
|
|
// so they can be used as map keys.
|
|
// Two Type values are equal if they represent identical types.
|
|
type Type interface {
|
|
// Methods applicable to all types.
|
|
|
|
// Align returns the alignment in bytes of a value of
|
|
// this type when allocated in memory.
|
|
Align() int
|
|
|
|
// FieldAlign returns the alignment in bytes of a value of
|
|
// this type when used as a field in a struct.
|
|
FieldAlign() int
|
|
|
|
// Method returns the i'th method in the type's method set.
|
|
// It panics if i is not in the range [0, NumMethod()).
|
|
//
|
|
// For a non-interface type T or *T, the returned Method's Type and Func
|
|
// fields describe a function whose first argument is the receiver.
|
|
//
|
|
// For an interface type, the returned Method's Type field gives the
|
|
// method signature, without a receiver, and the Func field is nil.
|
|
//
|
|
// Only exported methods are accessible and they are sorted in
|
|
// lexicographic order.
|
|
Method(int) Method
|
|
|
|
// MethodByName returns the method with that name in the type's
|
|
// method set and a boolean indicating if the method was found.
|
|
//
|
|
// For a non-interface type T or *T, the returned Method's Type and Func
|
|
// fields describe a function whose first argument is the receiver.
|
|
//
|
|
// For an interface type, the returned Method's Type field gives the
|
|
// method signature, without a receiver, and the Func field is nil.
|
|
MethodByName(string) (Method, bool)
|
|
|
|
// NumMethod returns the number of exported methods in the type's method set.
|
|
NumMethod() int
|
|
|
|
// Name returns the type's name within its package for a defined type.
|
|
// For other (non-defined) types it returns the empty string.
|
|
Name() string
|
|
|
|
// PkgPath returns a defined type's package path, that is, the import path
|
|
// that uniquely identifies the package, such as "encoding/base64".
|
|
// If the type was predeclared (string, error) or not defined (*T, struct{},
|
|
// []int, or A where A is an alias for a non-defined type), the package path
|
|
// will be the empty string.
|
|
PkgPath() string
|
|
|
|
// Size returns the number of bytes needed to store
|
|
// a value of the given type; it is analogous to unsafe.Sizeof.
|
|
Size() uintptr
|
|
|
|
// String returns a string representation of the type.
|
|
// The string representation may use shortened package names
|
|
// (e.g., base64 instead of "encoding/base64") and is not
|
|
// guaranteed to be unique among types. To test for type identity,
|
|
// compare the Types directly.
|
|
String() string
|
|
|
|
// Kind returns the specific kind of this type.
|
|
Kind() Kind
|
|
|
|
// Implements reports whether the type implements the interface type u.
|
|
Implements(u Type) bool
|
|
|
|
// AssignableTo reports whether a value of the type is assignable to type u.
|
|
AssignableTo(u Type) bool
|
|
|
|
// ConvertibleTo reports whether a value of the type is convertible to type u.
|
|
ConvertibleTo(u Type) bool
|
|
|
|
// Comparable reports whether values of this type are comparable.
|
|
Comparable() bool
|
|
|
|
// Methods applicable only to some types, depending on Kind.
|
|
// The methods allowed for each kind are:
|
|
//
|
|
// Int*, Uint*, Float*, Complex*: Bits
|
|
// Array: Elem, Len
|
|
// Chan: ChanDir, Elem
|
|
// Func: In, NumIn, Out, NumOut, IsVariadic.
|
|
// Map: Key, Elem
|
|
// Pointer: Elem
|
|
// Slice: Elem
|
|
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
|
|
|
|
// Bits returns the size of the type in bits.
|
|
// It panics if the type's Kind is not one of the
|
|
// sized or unsized Int, Uint, Float, or Complex kinds.
|
|
Bits() int
|
|
|
|
// ChanDir returns a channel type's direction.
|
|
// It panics if the type's Kind is not Chan.
|
|
ChanDir() ChanDir
|
|
|
|
// IsVariadic reports whether a function type's final input parameter
|
|
// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
|
|
// implicit actual type []T.
|
|
//
|
|
// For concreteness, if t represents func(x int, y ... float64), then
|
|
//
|
|
// t.NumIn() == 2
|
|
// t.In(0) is the reflect.Type for "int"
|
|
// t.In(1) is the reflect.Type for "[]float64"
|
|
// t.IsVariadic() == true
|
|
//
|
|
// IsVariadic panics if the type's Kind is not Func.
|
|
IsVariadic() bool
|
|
|
|
// Elem returns a type's element type.
|
|
// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
|
|
Elem() Type
|
|
|
|
// Field returns a struct type's i'th field.
|
|
// It panics if the type's Kind is not Struct.
|
|
// It panics if i is not in the range [0, NumField()).
|
|
Field(i int) StructField
|
|
|
|
// FieldByIndex returns the nested field corresponding
|
|
// to the index sequence. It is equivalent to calling Field
|
|
// successively for each index i.
|
|
// It panics if the type's Kind is not Struct.
|
|
FieldByIndex(index []int) StructField
|
|
|
|
// FieldByName returns the struct field with the given name
|
|
// and a boolean indicating if the field was found.
|
|
FieldByName(name string) (StructField, bool)
|
|
|
|
// FieldByNameFunc returns the struct field with a name
|
|
// that satisfies the match function and a boolean indicating if
|
|
// the field was found.
|
|
//
|
|
// FieldByNameFunc considers the fields in the struct itself
|
|
// and then the fields in any embedded structs, in breadth first order,
|
|
// stopping at the shallowest nesting depth containing one or more
|
|
// fields satisfying the match function. If multiple fields at that depth
|
|
// satisfy the match function, they cancel each other
|
|
// and FieldByNameFunc returns no match.
|
|
// This behavior mirrors Go's handling of name lookup in
|
|
// structs containing embedded fields.
|
|
//FieldByNameFunc(match func(string) bool) (StructField, bool)
|
|
|
|
// In returns the type of a function type's i'th input parameter.
|
|
// It panics if the type's Kind is not Func.
|
|
// It panics if i is not in the range [0, NumIn()).
|
|
In(i int) Type
|
|
|
|
// Key returns a map type's key type.
|
|
// It panics if the type's Kind is not Map.
|
|
Key() Type
|
|
|
|
// Len returns an array type's length.
|
|
// It panics if the type's Kind is not Array.
|
|
Len() int
|
|
|
|
// NumField returns a struct type's field count.
|
|
// It panics if the type's Kind is not Struct.
|
|
NumField() int
|
|
|
|
// NumIn returns a function type's input parameter count.
|
|
// It panics if the type's Kind is not Func.
|
|
NumIn() int
|
|
|
|
// NumOut returns a function type's output parameter count.
|
|
// It panics if the type's Kind is not Func.
|
|
NumOut() int
|
|
|
|
// Out returns the type of a function type's i'th output parameter.
|
|
// It panics if the type's Kind is not Func.
|
|
// It panics if i is not in the range [0, NumOut()).
|
|
Out(i int) Type
|
|
}
|
|
|
|
// Constants for the 'meta' byte.
|
|
const (
|
|
kindMask = 31 // mask to apply to the meta byte to get the Kind value
|
|
flagNamed = 32 // flag that is set if this is a named type
|
|
)
|
|
|
|
// The base type struct. All type structs start with this.
|
|
type rawType struct {
|
|
meta uint8 // metadata byte, contains kind and flags (see contants above)
|
|
}
|
|
|
|
// All types that have an element type: named, chan, slice, array, map (but not
|
|
// pointer because it doesn't have ptrTo).
|
|
type elemType struct {
|
|
rawType
|
|
numMethod uint16
|
|
ptrTo *rawType
|
|
elem *rawType
|
|
}
|
|
|
|
type ptrType struct {
|
|
rawType
|
|
numMethod uint16
|
|
elem *rawType
|
|
}
|
|
|
|
type arrayType struct {
|
|
rawType
|
|
numMethod uint16
|
|
ptrTo *rawType
|
|
elem *rawType
|
|
arrayLen uintptr
|
|
}
|
|
|
|
type mapType struct {
|
|
rawType
|
|
numMethod uint16
|
|
ptrTo *rawType
|
|
elem *rawType
|
|
key *rawType
|
|
}
|
|
|
|
type namedType struct {
|
|
rawType
|
|
numMethod uint16
|
|
ptrTo *rawType
|
|
elem *rawType
|
|
pkg *byte
|
|
name [1]byte
|
|
}
|
|
|
|
// Type for struct types. The numField value is intentionally put before ptrTo
|
|
// for better struct packing on 32-bit and 64-bit architectures. On these
|
|
// architectures, the ptrTo field still has the same offset as in all the other
|
|
// type structs.
|
|
// The fields array isn't necessarily 1 structField long, instead it is as long
|
|
// as numFields. The array is given a length of 1 to satisfy the Go type
|
|
// checker.
|
|
type structType struct {
|
|
rawType
|
|
numMethod uint16
|
|
ptrTo *rawType
|
|
pkgpath *byte
|
|
numField uint16
|
|
fields [1]structField // the remaining fields are all of type structField
|
|
}
|
|
|
|
type structField struct {
|
|
fieldType *rawType
|
|
data unsafe.Pointer // various bits of information, packed in a byte array
|
|
}
|
|
|
|
// Equivalent to (go/types.Type).Underlying(): if this is a named type return
|
|
// the underlying type, else just return the type itself.
|
|
func (t *rawType) underlying() *rawType {
|
|
if t.isNamed() {
|
|
return (*elemType)(unsafe.Pointer(t)).elem
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t *rawType) isNamed() bool {
|
|
return t.meta&flagNamed != 0
|
|
}
|
|
|
|
func TypeOf(i interface{}) Type {
|
|
if i == nil {
|
|
return nil
|
|
}
|
|
typecode, _ := decomposeInterface(i)
|
|
return (*rawType)(typecode)
|
|
}
|
|
|
|
func PtrTo(t Type) Type { return PointerTo(t) }
|
|
|
|
func PointerTo(t Type) Type {
|
|
return pointerTo(t.(*rawType))
|
|
}
|
|
|
|
func pointerTo(t *rawType) *rawType {
|
|
if t.isNamed() {
|
|
return (*elemType)(unsafe.Pointer(t)).ptrTo
|
|
}
|
|
|
|
switch t.Kind() {
|
|
case Pointer:
|
|
// TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131
|
|
// We need to be able to create types that match existing types to prevent typecode equality.
|
|
panic("reflect: cannot make **T type")
|
|
case Struct:
|
|
return (*structType)(unsafe.Pointer(t)).ptrTo
|
|
default:
|
|
return (*elemType)(unsafe.Pointer(t)).ptrTo
|
|
}
|
|
}
|
|
|
|
func (t *rawType) String() string {
|
|
if t.isNamed() {
|
|
s := t.name()
|
|
if s[0] == '.' {
|
|
return s[1:]
|
|
}
|
|
return s
|
|
}
|
|
|
|
switch t.Kind() {
|
|
case Chan:
|
|
return "chan " + t.elem().String()
|
|
case Pointer:
|
|
return "*" + t.elem().String()
|
|
case Slice:
|
|
return "[]" + t.elem().String()
|
|
case Array:
|
|
return "[" + itoa.Itoa(t.Len()) + "]" + t.elem().String()
|
|
case Map:
|
|
return "map[" + t.key().String() + "]" + t.elem().String()
|
|
case Struct:
|
|
s := "struct {"
|
|
numField := t.NumField()
|
|
for i := 0; i < numField; i++ {
|
|
f := t.rawField(i)
|
|
s += " " + f.Name + " " + f.Type.String()
|
|
// every field except the last needs a semicolon
|
|
if i < numField-1 {
|
|
s += ";"
|
|
}
|
|
}
|
|
s += " }"
|
|
return s
|
|
default:
|
|
return t.Kind().String()
|
|
}
|
|
|
|
return t.Kind().String()
|
|
}
|
|
|
|
func (t *rawType) Kind() Kind {
|
|
if t == nil {
|
|
return Invalid
|
|
}
|
|
return Kind(t.meta & kindMask)
|
|
}
|
|
|
|
// Elem returns the element type for channel, slice and array types, the
|
|
// pointed-to value for pointer types, and the key type for map types.
|
|
func (t *rawType) Elem() Type {
|
|
return t.elem()
|
|
}
|
|
|
|
func (t *rawType) elem() *rawType {
|
|
underlying := t.underlying()
|
|
switch underlying.Kind() {
|
|
case Pointer:
|
|
return (*ptrType)(unsafe.Pointer(underlying)).elem
|
|
case Chan, Slice, Array, Map:
|
|
return (*elemType)(unsafe.Pointer(underlying)).elem
|
|
default:
|
|
panic(&TypeError{"Elem"})
|
|
}
|
|
}
|
|
|
|
func (t *rawType) key() *rawType {
|
|
underlying := t.underlying()
|
|
if underlying.Kind() != Map {
|
|
panic(&TypeError{"Key"})
|
|
}
|
|
return (*mapType)(unsafe.Pointer(underlying)).key
|
|
}
|
|
|
|
// Field returns the type of the i'th field of this struct type. It panics if t
|
|
// is not a struct type.
|
|
func (t *rawType) Field(i int) StructField {
|
|
field := t.rawField(i)
|
|
return StructField{
|
|
Name: field.Name,
|
|
PkgPath: field.PkgPath,
|
|
Type: field.Type, // note: converts rawType to Type
|
|
Tag: field.Tag,
|
|
Anonymous: field.Anonymous,
|
|
Offset: field.Offset,
|
|
Index: []int{i},
|
|
}
|
|
}
|
|
|
|
func rawStructFieldFromPointer(descriptor *structType, fieldType *rawType, data unsafe.Pointer, flagsByte uint8, name string, offset uintptr) rawStructField {
|
|
// Read the field tag, if there is one.
|
|
var tag string
|
|
if flagsByte&structFieldFlagHasTag != 0 {
|
|
data = unsafe.Add(data, 1) // C: data+1
|
|
tagLen := uintptr(*(*byte)(data))
|
|
data = unsafe.Add(data, 1) // C: data+1
|
|
tag = *(*string)(unsafe.Pointer(&stringHeader{
|
|
data: data,
|
|
len: tagLen,
|
|
}))
|
|
}
|
|
|
|
// Set the PkgPath to some (arbitrary) value if the package path is not
|
|
// exported.
|
|
pkgPath := ""
|
|
if flagsByte&structFieldFlagIsExported == 0 {
|
|
// This field is unexported.
|
|
pkgPath = readStringZ(unsafe.Pointer(descriptor.pkgpath))
|
|
}
|
|
|
|
return rawStructField{
|
|
Name: name,
|
|
PkgPath: pkgPath,
|
|
Type: fieldType,
|
|
Tag: StructTag(tag),
|
|
Anonymous: flagsByte&structFieldFlagAnonymous != 0,
|
|
Offset: offset,
|
|
}
|
|
}
|
|
|
|
// rawField returns nearly the same value as Field but without converting the
|
|
// Type member to an interface.
|
|
//
|
|
// For internal use only.
|
|
func (t *rawType) rawField(n int) rawStructField {
|
|
if t.Kind() != Struct {
|
|
panic(&TypeError{"Field"})
|
|
}
|
|
descriptor := (*structType)(unsafe.Pointer(t.underlying()))
|
|
if uint(n) >= uint(descriptor.numField) {
|
|
panic("reflect: field index out of range")
|
|
}
|
|
|
|
// Iterate over all the fields to calculate the offset.
|
|
// This offset could have been stored directly in the array (to make the
|
|
// lookup faster), but by calculating it on-the-fly a bit of storage can be
|
|
// saved.
|
|
field := &descriptor.fields[0]
|
|
var offset uintptr = 0
|
|
for i := 0; i < n; i++ {
|
|
offset += field.fieldType.Size()
|
|
|
|
// Increment pointer to the next field.
|
|
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
|
|
|
|
// Align the offset for the next field.
|
|
offset = align(offset, uintptr(field.fieldType.Align()))
|
|
}
|
|
|
|
data := field.data
|
|
|
|
// Read some flags of this field, like whether the field is an embedded
|
|
// field. See structFieldFlagAnonymous and similar flags.
|
|
flagsByte := *(*byte)(data)
|
|
data = unsafe.Add(data, 1)
|
|
|
|
name := readStringZ(data)
|
|
data = unsafe.Add(data, len(name))
|
|
|
|
return rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset)
|
|
}
|
|
|
|
// rawFieldByName returns nearly the same value as FieldByName but without converting the
|
|
// Type member to an interface.
|
|
//
|
|
// For internal use only.
|
|
func (t *rawType) rawFieldByName(n string) (rawStructField, []int, bool) {
|
|
if t.Kind() != Struct {
|
|
panic(&TypeError{"Field"})
|
|
}
|
|
|
|
type fieldWalker struct {
|
|
t *rawType
|
|
index []int
|
|
}
|
|
|
|
queue := make([]fieldWalker, 0, 4)
|
|
queue = append(queue, fieldWalker{t, nil})
|
|
|
|
for len(queue) > 0 {
|
|
type result struct {
|
|
r rawStructField
|
|
index []int
|
|
}
|
|
|
|
var found []result
|
|
var nextlevel []fieldWalker
|
|
|
|
// For all the structs at this level..
|
|
for _, ll := range queue {
|
|
// Iterate over all the fields looking for the matching name
|
|
// Also calculate field offset.
|
|
|
|
descriptor := (*structType)(unsafe.Pointer(ll.t.underlying()))
|
|
var offset uintptr
|
|
field := &descriptor.fields[0]
|
|
|
|
for i := uint16(0); i < descriptor.numField; i++ {
|
|
data := field.data
|
|
|
|
// Read some flags of this field, like whether the field is an embedded
|
|
// field. See structFieldFlagAnonymous and similar flags.
|
|
flagsByte := *(*byte)(data)
|
|
data = unsafe.Add(data, 1)
|
|
|
|
name := readStringZ(data)
|
|
data = unsafe.Add(data, len(name))
|
|
if name == n {
|
|
found = append(found, result{
|
|
rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset),
|
|
append(ll.index, int(i)),
|
|
})
|
|
}
|
|
|
|
structOrPtrToStruct := field.fieldType.Kind() == Struct || (field.fieldType.Kind() == Pointer && field.fieldType.elem().Kind() == Struct)
|
|
if flagsByte&structFieldFlagIsEmbedded == structFieldFlagIsEmbedded && structOrPtrToStruct {
|
|
embedded := field.fieldType
|
|
if embedded.Kind() == Pointer {
|
|
embedded = embedded.elem()
|
|
}
|
|
|
|
nextlevel = append(nextlevel, fieldWalker{
|
|
t: embedded,
|
|
index: append(ll.index, int(i)),
|
|
})
|
|
}
|
|
|
|
offset += field.fieldType.Size()
|
|
|
|
// update offset/field pointer if there *is* a next field
|
|
if i < descriptor.numField-1 {
|
|
|
|
// Increment pointer to the next field.
|
|
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
|
|
|
|
// Align the offset for the next field.
|
|
offset = align(offset, uintptr(field.fieldType.Align()))
|
|
}
|
|
}
|
|
}
|
|
|
|
// found multiple hits at this level
|
|
if len(found) > 1 {
|
|
return rawStructField{}, nil, false
|
|
}
|
|
|
|
// found the field we were looking for
|
|
if len(found) == 1 {
|
|
r := found[0]
|
|
return r.r, r.index, true
|
|
}
|
|
|
|
// else len(found) == 0, move on to the next level
|
|
queue = append(queue[:0], nextlevel...)
|
|
}
|
|
|
|
// didn't find it
|
|
return rawStructField{}, nil, false
|
|
}
|
|
|
|
// Bits returns the number of bits that this type uses. It is only valid for
|
|
// arithmetic types (integers, floats, and complex numbers). For other types, it
|
|
// will panic.
|
|
func (t *rawType) Bits() int {
|
|
kind := t.Kind()
|
|
if kind >= Int && kind <= Complex128 {
|
|
return int(t.Size()) * 8
|
|
}
|
|
panic(TypeError{"Bits"})
|
|
}
|
|
|
|
// Len returns the number of elements in this array. It panics of the type kind
|
|
// is not Array.
|
|
func (t *rawType) Len() int {
|
|
if t.Kind() != Array {
|
|
panic(TypeError{"Len"})
|
|
}
|
|
|
|
return int((*arrayType)(unsafe.Pointer(t.underlying())).arrayLen)
|
|
}
|
|
|
|
// NumField returns the number of fields of a struct type. It panics for other
|
|
// type kinds.
|
|
func (t *rawType) NumField() int {
|
|
if t.Kind() != Struct {
|
|
panic(&TypeError{"NumField"})
|
|
}
|
|
return int((*structType)(unsafe.Pointer(t.underlying())).numField)
|
|
}
|
|
|
|
// Size returns the size in bytes of a given type. It is similar to
|
|
// unsafe.Sizeof.
|
|
func (t *rawType) Size() uintptr {
|
|
switch t.Kind() {
|
|
case Bool, Int8, Uint8:
|
|
return 1
|
|
case Int16, Uint16:
|
|
return 2
|
|
case Int32, Uint32:
|
|
return 4
|
|
case Int64, Uint64:
|
|
return 8
|
|
case Int, Uint:
|
|
return unsafe.Sizeof(int(0))
|
|
case Uintptr:
|
|
return unsafe.Sizeof(uintptr(0))
|
|
case Float32:
|
|
return 4
|
|
case Float64:
|
|
return 8
|
|
case Complex64:
|
|
return 8
|
|
case Complex128:
|
|
return 16
|
|
case String:
|
|
return unsafe.Sizeof("")
|
|
case UnsafePointer, Chan, Map, Pointer:
|
|
return unsafe.Sizeof(uintptr(0))
|
|
case Slice:
|
|
return unsafe.Sizeof([]int{})
|
|
case Interface:
|
|
return unsafe.Sizeof(interface{}(nil))
|
|
case Func:
|
|
var f func()
|
|
return unsafe.Sizeof(f)
|
|
case Array:
|
|
return t.elem().Size() * uintptr(t.Len())
|
|
case Struct:
|
|
numField := t.NumField()
|
|
if numField == 0 {
|
|
return 0
|
|
}
|
|
lastField := t.rawField(numField - 1)
|
|
return align(lastField.Offset+lastField.Type.Size(), uintptr(t.Align()))
|
|
default:
|
|
panic("unimplemented: size of type")
|
|
}
|
|
}
|
|
|
|
// Align returns the alignment of this type. It is similar to calling
|
|
// unsafe.Alignof.
|
|
func (t *rawType) Align() int {
|
|
switch t.Kind() {
|
|
case Bool, Int8, Uint8:
|
|
return int(unsafe.Alignof(int8(0)))
|
|
case Int16, Uint16:
|
|
return int(unsafe.Alignof(int16(0)))
|
|
case Int32, Uint32:
|
|
return int(unsafe.Alignof(int32(0)))
|
|
case Int64, Uint64:
|
|
return int(unsafe.Alignof(int64(0)))
|
|
case Int, Uint:
|
|
return int(unsafe.Alignof(int(0)))
|
|
case Uintptr:
|
|
return int(unsafe.Alignof(uintptr(0)))
|
|
case Float32:
|
|
return int(unsafe.Alignof(float32(0)))
|
|
case Float64:
|
|
return int(unsafe.Alignof(float64(0)))
|
|
case Complex64:
|
|
return int(unsafe.Alignof(complex64(0)))
|
|
case Complex128:
|
|
return int(unsafe.Alignof(complex128(0)))
|
|
case String:
|
|
return int(unsafe.Alignof(""))
|
|
case UnsafePointer, Chan, Map, Pointer:
|
|
return int(unsafe.Alignof(uintptr(0)))
|
|
case Slice:
|
|
return int(unsafe.Alignof([]int(nil)))
|
|
case Interface:
|
|
return int(unsafe.Alignof(interface{}(nil)))
|
|
case Func:
|
|
var f func()
|
|
return int(unsafe.Alignof(f))
|
|
case Struct:
|
|
numField := t.NumField()
|
|
alignment := 1
|
|
for i := 0; i < numField; i++ {
|
|
fieldAlignment := t.rawField(i).Type.Align()
|
|
if fieldAlignment > alignment {
|
|
alignment = fieldAlignment
|
|
}
|
|
}
|
|
return alignment
|
|
case Array:
|
|
return t.elem().Align()
|
|
default:
|
|
panic("unimplemented: alignment of type")
|
|
}
|
|
}
|
|
|
|
// FieldAlign returns the alignment if this type is used in a struct field. It
|
|
// is currently an alias for Align() but this might change in the future.
|
|
func (t *rawType) FieldAlign() int {
|
|
return t.Align()
|
|
}
|
|
|
|
// AssignableTo returns whether a value of type t can be assigned to a variable
|
|
// of type u.
|
|
func (t *rawType) AssignableTo(u Type) bool {
|
|
if t == u.(*rawType) {
|
|
return true
|
|
}
|
|
|
|
if u.Kind() == Interface && u.NumMethod() == 0 {
|
|
return true
|
|
}
|
|
|
|
if u.Kind() == Interface {
|
|
panic("reflect: unimplemented: AssignableTo with interface")
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *rawType) Implements(u Type) bool {
|
|
if u.Kind() != Interface {
|
|
panic("reflect: non-interface type passed to Type.Implements")
|
|
}
|
|
return t.AssignableTo(u)
|
|
}
|
|
|
|
// Comparable returns whether values of this type can be compared to each other.
|
|
func (t *rawType) Comparable() bool {
|
|
switch t.Kind() {
|
|
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
|
return true
|
|
case Float32, Float64, Complex64, Complex128:
|
|
return true
|
|
case String:
|
|
return true
|
|
case UnsafePointer:
|
|
return true
|
|
case Chan:
|
|
return true
|
|
case Interface:
|
|
return true
|
|
case Pointer:
|
|
return true
|
|
case Slice:
|
|
return false
|
|
case Array:
|
|
return t.elem().Comparable()
|
|
case Func:
|
|
return false
|
|
case Map:
|
|
return false
|
|
case Struct:
|
|
numField := t.NumField()
|
|
for i := 0; i < numField; i++ {
|
|
if !t.rawField(i).Type.Comparable() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
default:
|
|
panic(TypeError{"Comparable"})
|
|
}
|
|
}
|
|
|
|
// isbinary() returns if the hashmapAlgorithmBinary functions can be used on this type
|
|
func (t *rawType) isBinary() bool {
|
|
switch t.Kind() {
|
|
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
|
return true
|
|
case Float32, Float64, Complex64, Complex128:
|
|
return true
|
|
case Pointer:
|
|
return true
|
|
case Array:
|
|
return t.elem().isBinary()
|
|
case Struct:
|
|
numField := t.NumField()
|
|
for i := 0; i < numField; i++ {
|
|
if !t.rawField(i).Type.isBinary() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t rawType) ChanDir() ChanDir {
|
|
panic("unimplemented: (reflect.Type).ChanDir()")
|
|
}
|
|
|
|
func (t *rawType) ConvertibleTo(u Type) bool {
|
|
panic("unimplemented: (reflect.Type).ConvertibleTo()")
|
|
}
|
|
|
|
func (t *rawType) IsVariadic() bool {
|
|
panic("unimplemented: (reflect.Type).IsVariadic()")
|
|
}
|
|
|
|
func (t *rawType) NumIn() int {
|
|
panic("unimplemented: (reflect.Type).NumIn()")
|
|
}
|
|
|
|
func (t *rawType) NumOut() int {
|
|
panic("unimplemented: (reflect.Type).NumOut()")
|
|
}
|
|
|
|
func (t *rawType) NumMethod() int {
|
|
|
|
if t.isNamed() {
|
|
return int((*namedType)(unsafe.Pointer(t)).numMethod)
|
|
}
|
|
|
|
switch t.Kind() {
|
|
case Pointer:
|
|
return int((*ptrType)(unsafe.Pointer(t)).numMethod)
|
|
case Struct:
|
|
return int((*structType)(unsafe.Pointer(t)).numMethod)
|
|
}
|
|
|
|
// Other types have no methods attached. Note we don't panic here.
|
|
return 0
|
|
}
|
|
|
|
// Read and return a null terminated string starting from data.
|
|
func readStringZ(data unsafe.Pointer) string {
|
|
start := data
|
|
var len uintptr
|
|
for *(*byte)(data) != 0 {
|
|
len++
|
|
data = unsafe.Add(data, 1) // C: data++
|
|
}
|
|
|
|
return *(*string)(unsafe.Pointer(&stringHeader{
|
|
data: start,
|
|
len: len,
|
|
}))
|
|
}
|
|
|
|
func (t *rawType) name() string {
|
|
ntype := (*namedType)(unsafe.Pointer(t))
|
|
return readStringZ(unsafe.Pointer(&ntype.name[0]))
|
|
}
|
|
|
|
func (t *rawType) Name() string {
|
|
if t.isNamed() {
|
|
name := t.name()
|
|
for i := 0; i < len(name); i++ {
|
|
if name[i] == '.' {
|
|
return name[i+1:]
|
|
}
|
|
}
|
|
panic("corrupt name data")
|
|
}
|
|
|
|
if t.Kind() <= UnsafePointer {
|
|
return t.Kind().String()
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (t *rawType) Key() Type {
|
|
return t.key()
|
|
}
|
|
|
|
func (t rawType) In(i int) Type {
|
|
panic("unimplemented: (reflect.Type).In()")
|
|
}
|
|
|
|
func (t rawType) Out(i int) Type {
|
|
panic("unimplemented: (reflect.Type).Out()")
|
|
}
|
|
|
|
func (t rawType) Method(i int) Method {
|
|
panic("unimplemented: (reflect.Type).Method()")
|
|
}
|
|
|
|
func (t rawType) MethodByName(name string) (Method, bool) {
|
|
panic("unimplemented: (reflect.Type).MethodByName()")
|
|
}
|
|
|
|
func (t *rawType) PkgPath() string {
|
|
if t.isNamed() {
|
|
ntype := (*namedType)(unsafe.Pointer(t))
|
|
return readStringZ(unsafe.Pointer(ntype.pkg))
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (t *rawType) FieldByName(name string) (StructField, bool) {
|
|
if t.Kind() != Struct {
|
|
panic(TypeError{"FieldByName"})
|
|
}
|
|
|
|
field, index, ok := t.rawFieldByName(name)
|
|
if !ok {
|
|
return StructField{}, false
|
|
}
|
|
|
|
return StructField{
|
|
Name: field.Name,
|
|
PkgPath: field.PkgPath,
|
|
Type: field.Type, // note: converts rawType to Type
|
|
Tag: field.Tag,
|
|
Anonymous: field.Anonymous,
|
|
Offset: field.Offset,
|
|
Index: index,
|
|
}, true
|
|
}
|
|
|
|
func (t *rawType) FieldByIndex(index []int) StructField {
|
|
ftype := t
|
|
var field rawStructField
|
|
|
|
for _, n := range index {
|
|
structOrPtrToStruct := ftype.Kind() == Struct || (ftype.Kind() == Pointer && ftype.elem().Kind() == Struct)
|
|
if !structOrPtrToStruct {
|
|
panic(&TypeError{"FieldByIndex:" + ftype.Kind().String()})
|
|
}
|
|
|
|
if ftype.Kind() == Pointer {
|
|
ftype = ftype.elem()
|
|
}
|
|
|
|
field = ftype.rawField(n)
|
|
ftype = field.Type
|
|
}
|
|
|
|
return StructField{
|
|
Name: field.Name,
|
|
PkgPath: field.PkgPath,
|
|
Type: field.Type, // note: converts rawType to Type
|
|
Tag: field.Tag,
|
|
Anonymous: field.Anonymous,
|
|
Offset: field.Offset,
|
|
Index: index,
|
|
}
|
|
}
|
|
|
|
// A StructField describes a single field in a struct.
|
|
type StructField struct {
|
|
// Name indicates the field name.
|
|
Name string
|
|
|
|
// PkgPath is the package path where the struct containing this field is
|
|
// declared for unexported fields, or the empty string for exported fields.
|
|
PkgPath string
|
|
|
|
Type Type
|
|
Tag StructTag // field tag string
|
|
Offset uintptr
|
|
Index []int // index sequence for Type.FieldByIndex
|
|
Anonymous bool
|
|
}
|
|
|
|
// IsExported reports whether the field is exported.
|
|
func (f StructField) IsExported() bool {
|
|
return f.PkgPath == ""
|
|
}
|
|
|
|
// rawStructField is the same as StructField but with the Type member replaced
|
|
// with rawType. For internal use only. Avoiding this conversion to the Type
|
|
// interface improves code size in many cases.
|
|
type rawStructField struct {
|
|
Name string
|
|
PkgPath string
|
|
Type *rawType
|
|
Tag StructTag
|
|
Offset uintptr
|
|
Anonymous bool
|
|
}
|
|
|
|
// A StructTag is the tag string in a struct field.
|
|
type StructTag string
|
|
|
|
// TODO: it would be feasible to do the key/value splitting at compile time,
|
|
// avoiding the code size cost of doing it at runtime
|
|
|
|
// Get returns the value associated with key in the tag string.
|
|
func (tag StructTag) Get(key string) string {
|
|
v, _ := tag.Lookup(key)
|
|
return v
|
|
}
|
|
|
|
// Lookup returns the value associated with key in the tag string.
|
|
func (tag StructTag) Lookup(key string) (value string, ok bool) {
|
|
for tag != "" {
|
|
// Skip leading space.
|
|
i := 0
|
|
for i < len(tag) && tag[i] == ' ' {
|
|
i++
|
|
}
|
|
tag = tag[i:]
|
|
if tag == "" {
|
|
break
|
|
}
|
|
|
|
// Scan to colon. A space, a quote or a control character is a syntax error.
|
|
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
|
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
|
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
|
i = 0
|
|
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
|
i++
|
|
}
|
|
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
|
break
|
|
}
|
|
name := string(tag[:i])
|
|
tag = tag[i+1:]
|
|
|
|
// Scan quoted string to find value.
|
|
i = 1
|
|
for i < len(tag) && tag[i] != '"' {
|
|
if tag[i] == '\\' {
|
|
i++
|
|
}
|
|
i++
|
|
}
|
|
if i >= len(tag) {
|
|
break
|
|
}
|
|
qvalue := string(tag[:i+1])
|
|
tag = tag[i+1:]
|
|
|
|
if key == name {
|
|
value, err := unquote(qvalue)
|
|
if err != nil {
|
|
break
|
|
}
|
|
return value, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// TypeError is the error that is used in a panic when invoking a method on a
|
|
// type that is not applicable to that type.
|
|
type TypeError struct {
|
|
Method string
|
|
}
|
|
|
|
func (e *TypeError) Error() string {
|
|
return "reflect: call of reflect.Type." + e.Method + " on invalid type"
|
|
}
|
|
|
|
func align(offset uintptr, alignment uintptr) uintptr {
|
|
return (offset + alignment - 1) &^ (alignment - 1)
|
|
}
|
|
|
|
func SliceOf(t Type) Type {
|
|
panic("unimplemented: reflect.SliceOf()")
|
|
}
|
|
|
|
func ArrayOf(n int, t Type) Type {
|
|
panic("unimplemented: reflect.ArrayOf()")
|
|
}
|
|
|
|
func StructOf([]StructField) Type {
|
|
panic("unimplemented: reflect.StructOf()")
|
|
}
|
|
|
|
func MapOf(key, value Type) Type {
|
|
panic("unimplemented: reflect.MapOf()")
|
|
}
|