blob: af581d3a71c61a4362b40cabe88073a07c07448c [file] [log] [blame]
// 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.
// Package reflectlite implements lightweight version of reflect, not using
// any package except for "runtime", "unsafe", and "internal/abi"
package reflectlite
import (
"internal/abi"
"unsafe"
)
// 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.
// 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
// 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
// Comparable reports whether values of this type are comparable.
Comparable() bool
// 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
// Elem returns a type's element type.
// It panics if the type's Kind is not Ptr.
Elem() Type
common() *abi.Type
uncommon() *uncommonType
}
/*
* These data structures are known to the compiler (../../cmd/internal/reflectdata/reflect.go).
* A few are known to ../runtime/type.go to convey to debuggers.
* They are also known to ../runtime/type.go.
*/
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind = abi.Kind
const Ptr = abi.Pointer
const (
// Import-and-export these constants as necessary
Interface = abi.Interface
Slice = abi.Slice
String = abi.String
Struct = abi.Struct
)
type nameOff = abi.NameOff
type typeOff = abi.TypeOff
type textOff = abi.TextOff
type rtype struct {
*abi.Type
}
// uncommonType is present only for defined types or types with methods
// (if T is a defined type, the uncommonTypes for T and *T have methods).
// Using a pointer to this struct reduces the overall size required
// to describe a non-defined type with no methods.
type uncommonType = abi.UncommonType
// arrayType represents a fixed array type.
type arrayType = abi.ArrayType
// chanType represents a channel type.
type chanType = abi.ChanType
type funcType = abi.FuncType
type interfaceType = abi.InterfaceType
// ptrType represents a pointer type.
type ptrType = abi.PtrType
// sliceType represents a slice type.
type sliceType = abi.SliceType
// structType represents a struct type.
type structType = abi.StructType
// name is an encoded type name with optional extra data.
//
// The first byte is a bit field containing:
//
// 1<<0 the name is exported
// 1<<1 tag data follows the name
// 1<<2 pkgPath nameOff follows the name and tag
//
// The next two bytes are the data length:
//
// l := uint16(data[1])<<8 | uint16(data[2])
//
// Bytes [3:3+l] are the string data.
//
// If tag data follows then bytes 3+l and 3+l+1 are the tag length,
// with the data following.
//
// If the import path follows, then 4 bytes at the end of
// the data form a nameOff. The import path is only set for concrete
// methods that are defined in a different package than their type.
//
// If a name starts with "*", then the exported bit represents
// whether the pointed to type is exported.
type name struct {
bytes *byte
}
func (n name) data(off int, whySafe string) *byte {
return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe))
}
func (n name) isExported() bool {
return (*n.bytes)&(1<<0) != 0
}
func (n name) hasTag() bool {
return (*n.bytes)&(1<<1) != 0
}
func (n name) embedded() bool {
return (*n.bytes)&(1<<3) != 0
}
// readVarint parses a varint as encoded by encoding/binary.
// It returns the number of encoded bytes and the encoded value.
func (n name) readVarint(off int) (int, int) {
v := 0
for i := 0; ; i++ {
x := *n.data(off+i, "read varint")
v += int(x&0x7f) << (7 * i)
if x&0x80 == 0 {
return i + 1, v
}
}
}
func (n name) name() string {
if n.bytes == nil {
return ""
}
i, l := n.readVarint(1)
return unsafe.String(n.data(1+i, "non-empty string"), l)
}
func (n name) tag() string {
if !n.hasTag() {
return ""
}
i, l := n.readVarint(1)
i2, l2 := n.readVarint(1 + i + l)
return unsafe.String(n.data(1+i+l+i2, "non-empty string"), l2)
}
func pkgPath(n abi.Name) string {
if n.Bytes == nil || *n.DataChecked(0, "name flag field")&(1<<2) == 0 {
return ""
}
i, l := n.ReadVarint(1)
off := 1 + i + l
if n.HasTag() {
i2, l2 := n.ReadVarint(off)
off += i2 + l2
}
var nameOff int32
// Note that this field may not be aligned in memory,
// so we cannot use a direct int32 assignment here.
copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.DataChecked(off, "name offset field")))[:])
pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.Bytes), nameOff))}
return pkgPathName.name()
}
/*
* The compiler knows the exact layout of all the data structures above.
* The compiler does not know about the data structures and methods below.
*/
// resolveNameOff resolves a name offset from a base pointer.
// The (*rtype).nameOff method is a convenience wrapper for this function.
// Implemented in the runtime package.
//
//go:noescape
func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer
// resolveTypeOff resolves an *rtype offset from a base type.
// The (*rtype).typeOff method is a convenience wrapper for this function.
// Implemented in the runtime package.
//
//go:noescape
func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
func (t rtype) nameOff(off nameOff) abi.Name {
return abi.Name{Bytes: (*byte)(resolveNameOff(unsafe.Pointer(t.Type), int32(off)))}
}
func (t rtype) typeOff(off typeOff) *abi.Type {
return (*abi.Type)(resolveTypeOff(unsafe.Pointer(t.Type), int32(off)))
}
func (t rtype) uncommon() *uncommonType {
return t.Uncommon()
}
func (t rtype) String() string {
s := t.nameOff(t.Str).Name()
if t.TFlag&abi.TFlagExtraStar != 0 {
return s[1:]
}
return s
}
func (t rtype) common() *abi.Type { return t.Type }
func (t rtype) exportedMethods() []abi.Method {
ut := t.uncommon()
if ut == nil {
return nil
}
return ut.ExportedMethods()
}
func (t rtype) NumMethod() int {
tt := t.Type.InterfaceType()
if tt != nil {
return tt.NumMethod()
}
return len(t.exportedMethods())
}
func (t rtype) PkgPath() string {
if t.TFlag&abi.TFlagNamed == 0 {
return ""
}
ut := t.uncommon()
if ut == nil {
return ""
}
return t.nameOff(ut.PkgPath).Name()
}
func (t rtype) Name() string {
if !t.HasName() {
return ""
}
s := t.String()
i := len(s) - 1
sqBrackets := 0
for i >= 0 && (s[i] != '.' || sqBrackets != 0) {
switch s[i] {
case ']':
sqBrackets++
case '[':
sqBrackets--
}
i--
}
return s[i+1:]
}
func toRType(t *abi.Type) rtype {
return rtype{t}
}
func elem(t *abi.Type) *abi.Type {
et := t.Elem()
if et != nil {
return et
}
panic("reflect: Elem of invalid type " + toRType(t).String())
}
func (t rtype) Elem() Type {
return toType(elem(t.common()))
}
func (t rtype) In(i int) Type {
tt := t.Type.FuncType()
if tt == nil {
panic("reflect: In of non-func type")
}
return toType(tt.InSlice()[i])
}
func (t rtype) Key() Type {
tt := t.Type.MapType()
if tt == nil {
panic("reflect: Key of non-map type")
}
return toType(tt.Key)
}
func (t rtype) Len() int {
tt := t.Type.ArrayType()
if tt == nil {
panic("reflect: Len of non-array type")
}
return int(tt.Len)
}
func (t rtype) NumField() int {
tt := t.Type.StructType()
if tt == nil {
panic("reflect: NumField of non-struct type")
}
return len(tt.Fields)
}
func (t rtype) NumIn() int {
tt := t.Type.FuncType()
if tt == nil {
panic("reflect: NumIn of non-func type")
}
return int(tt.InCount)
}
func (t rtype) NumOut() int {
tt := t.Type.FuncType()
if tt == nil {
panic("reflect: NumOut of non-func type")
}
return tt.NumOut()
}
func (t rtype) Out(i int) Type {
tt := t.Type.FuncType()
if tt == nil {
panic("reflect: Out of non-func type")
}
return toType(tt.OutSlice()[i])
}
// add returns p+x.
//
// The whySafe string is ignored, so that the function still inlines
// as efficiently as p+x, but all call sites should use the string to
// record why the addition is safe, which is to say why the addition
// does not cause x to advance to the very end of p's allocation
// and therefore point incorrectly at the next block in memory.
func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
return unsafe.Pointer(uintptr(p) + x)
}
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i any) Type {
return toType(abi.TypeOf(i))
}
func (t rtype) Implements(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.Implements")
}
if u.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements")
}
return implements(u.common(), t.common())
}
func (t rtype) AssignableTo(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.AssignableTo")
}
uu := u.common()
tt := t.common()
return directlyAssignable(uu, tt) || implements(uu, tt)
}
func (t rtype) Comparable() bool {
return t.Equal != nil
}
// implements reports whether the type V implements the interface type T.
func implements(T, V *abi.Type) bool {
t := T.InterfaceType()
if t == nil {
return false
}
if len(t.Methods) == 0 {
return true
}
rT := toRType(T)
rV := toRType(V)
// The same algorithm applies in both cases, but the
// method tables for an interface type and a concrete type
// are different, so the code is duplicated.
// In both cases the algorithm is a linear scan over the two
// lists - T's methods and V's methods - simultaneously.
// Since method tables are stored in a unique sorted order
// (alphabetical, with no duplicate method names), the scan
// through V's methods must hit a match for each of T's
// methods along the way, or else V does not implement T.
// This lets us run the scan in overall linear time instead of
// the quadratic time a naive search would require.
// See also ../runtime/iface.go.
if V.Kind() == Interface {
v := (*interfaceType)(unsafe.Pointer(V))
i := 0
for j := 0; j < len(v.Methods); j++ {
tm := &t.Methods[i]
tmName := rT.nameOff(tm.Name)
vm := &v.Methods[j]
vmName := rV.nameOff(vm.Name)
if vmName.Name() == tmName.Name() && rV.typeOff(vm.Typ) == rT.typeOff(tm.Typ) {
if !tmName.IsExported() {
tmPkgPath := pkgPath(tmName)
if tmPkgPath == "" {
tmPkgPath = t.PkgPath.Name()
}
vmPkgPath := pkgPath(vmName)
if vmPkgPath == "" {
vmPkgPath = v.PkgPath.Name()
}
if tmPkgPath != vmPkgPath {
continue
}
}
if i++; i >= len(t.Methods) {
return true
}
}
}
return false
}
v := V.Uncommon()
if v == nil {
return false
}
i := 0
vmethods := v.Methods()
for j := 0; j < int(v.Mcount); j++ {
tm := &t.Methods[i]
tmName := rT.nameOff(tm.Name)
vm := vmethods[j]
vmName := rV.nameOff(vm.Name)
if vmName.Name() == tmName.Name() && rV.typeOff(vm.Mtyp) == rT.typeOff(tm.Typ) {
if !tmName.IsExported() {
tmPkgPath := pkgPath(tmName)
if tmPkgPath == "" {
tmPkgPath = t.PkgPath.Name()
}
vmPkgPath := pkgPath(vmName)
if vmPkgPath == "" {
vmPkgPath = rV.nameOff(v.PkgPath).Name()
}
if tmPkgPath != vmPkgPath {
continue
}
}
if i++; i >= len(t.Methods) {
return true
}
}
}
return false
}
// directlyAssignable reports whether a value x of type V can be directly
// assigned (using memmove) to a value of type T.
// https://golang.org/doc/go_spec.html#Assignability
// Ignoring the interface rules (implemented elsewhere)
// and the ideal constant rules (no ideal constants at run time).
func directlyAssignable(T, V *abi.Type) bool {
// x's type V is identical to T?
if T == V {
return true
}
// Otherwise at least one of T and V must not be defined
// and they must have the same kind.
if T.HasName() && V.HasName() || T.Kind() != V.Kind() {
return false
}
// x's type T and V must have identical underlying types.
return haveIdenticalUnderlyingType(T, V, true)
}
func haveIdenticalType(T, V *abi.Type, cmpTags bool) bool {
if cmpTags {
return T == V
}
if toRType(T).Name() != toRType(V).Name() || T.Kind() != V.Kind() {
return false
}
return haveIdenticalUnderlyingType(T, V, false)
}
func haveIdenticalUnderlyingType(T, V *abi.Type, cmpTags bool) bool {
if T == V {
return true
}
kind := T.Kind()
if kind != V.Kind() {
return false
}
// Non-composite types of equal kind have same underlying type
// (the predefined instance of the type).
if abi.Bool <= kind && kind <= abi.Complex128 || kind == abi.String || kind == abi.UnsafePointer {
return true
}
// Composite types.
switch kind {
case abi.Array:
return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case abi.Chan:
// Special case:
// x is a bidirectional channel value, T is a channel type,
// and x's type V and T have identical element types.
if V.ChanDir() == abi.BothDir && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) {
return true
}
// Otherwise continue test for identical underlying type.
return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case abi.Func:
t := (*funcType)(unsafe.Pointer(T))
v := (*funcType)(unsafe.Pointer(V))
if t.OutCount != v.OutCount || t.InCount != v.InCount {
return false
}
for i := 0; i < t.NumIn(); i++ {
if !haveIdenticalType(t.In(i), v.In(i), cmpTags) {
return false
}
}
for i := 0; i < t.NumOut(); i++ {
if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) {
return false
}
}
return true
case Interface:
t := (*interfaceType)(unsafe.Pointer(T))
v := (*interfaceType)(unsafe.Pointer(V))
if len(t.Methods) == 0 && len(v.Methods) == 0 {
return true
}
// Might have the same methods but still
// need a run time conversion.
return false
case abi.Map:
return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case Ptr, abi.Slice:
return haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case abi.Struct:
t := (*structType)(unsafe.Pointer(T))
v := (*structType)(unsafe.Pointer(V))
if len(t.Fields) != len(v.Fields) {
return false
}
if t.PkgPath.Name() != v.PkgPath.Name() {
return false
}
for i := range t.Fields {
tf := &t.Fields[i]
vf := &v.Fields[i]
if tf.Name.Name() != vf.Name.Name() {
return false
}
if !haveIdenticalType(tf.Typ, vf.Typ, cmpTags) {
return false
}
if cmpTags && tf.Name.Tag() != vf.Name.Tag() {
return false
}
if tf.Offset != vf.Offset {
return false
}
if tf.Embedded() != vf.Embedded() {
return false
}
}
return true
}
return false
}
// toType converts from a *rtype to a Type that can be returned
// to the client of package reflect. In gc, the only concern is that
// a nil *rtype must be replaced by a nil Type, but in gccgo this
// function takes care of ensuring that multiple *rtype for the same
// type are coalesced into a single Type.
func toType(t *abi.Type) Type {
if t == nil {
return nil
}
return toRType(t)
}
// ifaceIndir reports whether t is stored indirectly in an interface value.
func ifaceIndir(t *abi.Type) bool {
return abi.Kind(t.Kind_)&abi.KindDirectIface == 0
}