| // 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" and "unsafe". |
| package reflectlite |
| |
| import ( |
| "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() *rtype |
| uncommon() *uncommonType |
| } |
| |
| /* |
| * These data structures are known to the compiler (../../cmd/internal/gc/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 uint |
| |
| const ( |
| Invalid Kind = iota |
| Bool |
| Int |
| Int8 |
| Int16 |
| Int32 |
| Int64 |
| Uint |
| Uint8 |
| Uint16 |
| Uint32 |
| Uint64 |
| Uintptr |
| Float32 |
| Float64 |
| Complex64 |
| Complex128 |
| Array |
| Chan |
| Func |
| Interface |
| Map |
| Ptr |
| Slice |
| String |
| Struct |
| UnsafePointer |
| ) |
| |
| // tflag is used by an rtype to signal what extra type information is |
| // available in the memory directly following the rtype value. |
| // |
| // tflag values must be kept in sync with copies in: |
| // go/types.cc |
| // runtime/type.go |
| type tflag uint8 |
| |
| const ( |
| // tflagRegularMemory means that equal and hash functions can treat |
| // this type as a single region of t.size bytes. |
| tflagRegularMemory tflag = 1 << 3 |
| ) |
| |
| // rtype is the common implementation of most values. |
| // It is embedded in other struct types. |
| // |
| // rtype must be kept in sync with ../runtime/type.go:/^type._type. |
| type rtype struct { |
| size uintptr |
| ptrdata uintptr // number of bytes in the type that can contain pointers |
| hash uint32 // hash of type; avoids computation in hash tables |
| tflag tflag // extra type information flags |
| align uint8 // alignment of variable with this type |
| fieldAlign uint8 // alignment of struct field with this type |
| kind uint8 // enumeration for C |
| // function for comparing objects of this type |
| // (ptr to object A, ptr to object B) -> ==? |
| equal func(unsafe.Pointer, unsafe.Pointer) bool |
| gcdata *byte // garbage collection data |
| string *string // string form; unnecessary but undeniably useful |
| *uncommonType // (relatively) uncommon fields |
| ptrToThis *rtype // type for pointer to this type, may be zero |
| } |
| |
| // Method on non-interface type |
| type method struct { |
| name *string // name of method |
| pkgPath *string // nil for exported Names; otherwise import path |
| mtyp *rtype // method type (without receiver) |
| typ *rtype // .(*FuncType) underneath (with receiver) |
| tfn unsafe.Pointer // fn used for normal method call |
| } |
| |
| // 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 struct { |
| name *string // name of type |
| pkgPath *string // import path; nil for built-in types like int, string |
| methods []method // methods associated with type |
| } |
| |
| // chanDir represents a channel type's direction. |
| type chanDir int |
| |
| const ( |
| recvDir chanDir = 1 << iota // <-chan |
| sendDir // chan<- |
| bothDir = recvDir | sendDir // chan |
| ) |
| |
| // arrayType represents a fixed array type. |
| type arrayType struct { |
| rtype |
| elem *rtype // array element type |
| slice *rtype // slice type |
| len uintptr |
| } |
| |
| // chanType represents a channel type. |
| type chanType struct { |
| rtype |
| elem *rtype // channel element type |
| dir uintptr // channel direction (chanDir) |
| } |
| |
| // funcType represents a function type. |
| type funcType struct { |
| rtype |
| dotdotdot bool // last input parameter is ... |
| in []*rtype // input parameter types |
| out []*rtype // output parameter types |
| } |
| |
| // imethod represents a method on an interface type |
| type imethod struct { |
| name *string // name of method |
| pkgPath *string // nil for exported Names; otherwise import path |
| typ *rtype // .(*FuncType) underneath |
| } |
| |
| // interfaceType represents an interface type. |
| type interfaceType struct { |
| rtype |
| methods []imethod // sorted by hash |
| } |
| |
| // mapType represents a map type. |
| type mapType struct { |
| rtype |
| key *rtype // map key type |
| elem *rtype // map element (value) type |
| bucket *rtype // internal bucket structure |
| keysize uint8 // size of key slot |
| valuesize uint8 // size of value slot |
| bucketsize uint16 // size of bucket |
| flags uint32 |
| } |
| |
| // ptrType represents a pointer type. |
| type ptrType struct { |
| rtype |
| elem *rtype // pointer element (pointed at) type |
| } |
| |
| // sliceType represents a slice type. |
| type sliceType struct { |
| rtype |
| elem *rtype // slice element type |
| } |
| |
| // Struct field |
| type structField struct { |
| name *string // name is always non-empty |
| pkgPath *string // nil for exported Names; otherwise import path |
| typ *rtype // type of field |
| tag *string // nil if no tag |
| offsetEmbed uintptr // byte offset of field<<1 | isAnonymous |
| } |
| |
| func (f *structField) offset() uintptr { |
| return f.offsetEmbed >> 1 |
| } |
| |
| func (f *structField) embedded() bool { |
| return f.offsetEmbed&1 != 0 |
| } |
| |
| // structType represents a struct type. |
| type structType struct { |
| rtype |
| fields []structField // sorted by offset |
| } |
| |
| /* |
| * The compiler knows the exact layout of all the data structures above. |
| * The compiler does not know about the data structures and methods below. |
| */ |
| |
| const ( |
| kindDirectIface = 1 << 5 |
| kindGCProg = 1 << 6 // Type.gc points to GC program |
| kindMask = (1 << 5) - 1 |
| ) |
| |
| func (t *uncommonType) exportedMethods() []method { |
| allm := t.methods |
| allExported := true |
| for _, m := range allm { |
| if m.pkgPath != nil { |
| allExported = false |
| break |
| } |
| } |
| var methods []method |
| if allExported { |
| methods = allm |
| } else { |
| methods = make([]method, 0, len(allm)) |
| for _, m := range allm { |
| if m.pkgPath == nil { |
| methods = append(methods, m) |
| } |
| } |
| methods = methods[:len(methods):len(methods)] |
| } |
| |
| return methods |
| } |
| |
| func (t *uncommonType) uncommon() *uncommonType { |
| return t |
| } |
| |
| func (t *uncommonType) PkgPath() string { |
| if t == nil || t.pkgPath == nil { |
| return "" |
| } |
| return *t.pkgPath |
| } |
| |
| func (t *uncommonType) Name() string { |
| if t == nil || t.name == nil { |
| return "" |
| } |
| return *t.name |
| } |
| |
| func (t *rtype) String() string { |
| // For gccgo, strip out quoted strings. |
| s := *t.string |
| var q bool |
| r := make([]byte, len(s)) |
| j := 0 |
| for i := 0; i < len(s); i++ { |
| if s[i] == '\t' { |
| q = !q |
| } else if !q { |
| r[j] = s[i] |
| j++ |
| } |
| } |
| return string(r[:j]) |
| } |
| |
| func (t *rtype) Size() uintptr { return t.size } |
| |
| func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) } |
| |
| func (t *rtype) pointers() bool { return t.ptrdata != 0 } |
| |
| func (t *rtype) common() *rtype { return t } |
| |
| func (t *rtype) exportedMethods() []method { |
| ut := t.uncommon() |
| if ut == nil { |
| return nil |
| } |
| return ut.exportedMethods() |
| } |
| |
| func (t *rtype) NumMethod() int { |
| if t.Kind() == Interface { |
| tt := (*interfaceType)(unsafe.Pointer(t)) |
| return tt.NumMethod() |
| } |
| return len(t.exportedMethods()) |
| } |
| |
| func (t *rtype) PkgPath() string { |
| return t.uncommonType.PkgPath() |
| } |
| |
| func (t *rtype) hasName() bool { |
| return t.uncommonType != nil && t.uncommonType.name != nil |
| } |
| |
| func (t *rtype) Name() string { |
| return t.uncommonType.Name() |
| } |
| |
| func (t *rtype) chanDir() chanDir { |
| if t.Kind() != Chan { |
| panic("reflect: chanDir of non-chan type") |
| } |
| tt := (*chanType)(unsafe.Pointer(t)) |
| return chanDir(tt.dir) |
| } |
| |
| func (t *rtype) Elem() Type { |
| switch t.Kind() { |
| case Array: |
| tt := (*arrayType)(unsafe.Pointer(t)) |
| return toType(tt.elem) |
| case Chan: |
| tt := (*chanType)(unsafe.Pointer(t)) |
| return toType(tt.elem) |
| case Map: |
| tt := (*mapType)(unsafe.Pointer(t)) |
| return toType(tt.elem) |
| case Ptr: |
| tt := (*ptrType)(unsafe.Pointer(t)) |
| return toType(tt.elem) |
| case Slice: |
| tt := (*sliceType)(unsafe.Pointer(t)) |
| return toType(tt.elem) |
| } |
| panic("reflect: Elem of invalid type") |
| } |
| |
| func (t *rtype) In(i int) Type { |
| if t.Kind() != Func { |
| panic("reflect: In of non-func type") |
| } |
| tt := (*funcType)(unsafe.Pointer(t)) |
| return toType(tt.in[i]) |
| } |
| |
| func (t *rtype) Key() Type { |
| if t.Kind() != Map { |
| panic("reflect: Key of non-map type") |
| } |
| tt := (*mapType)(unsafe.Pointer(t)) |
| return toType(tt.key) |
| } |
| |
| func (t *rtype) Len() int { |
| if t.Kind() != Array { |
| panic("reflect: Len of non-array type") |
| } |
| tt := (*arrayType)(unsafe.Pointer(t)) |
| return int(tt.len) |
| } |
| |
| func (t *rtype) NumField() int { |
| if t.Kind() != Struct { |
| panic("reflect: NumField of non-struct type") |
| } |
| tt := (*structType)(unsafe.Pointer(t)) |
| return len(tt.fields) |
| } |
| |
| func (t *rtype) NumIn() int { |
| if t.Kind() != Func { |
| panic("reflect: NumIn of non-func type") |
| } |
| tt := (*funcType)(unsafe.Pointer(t)) |
| return len(tt.in) |
| } |
| |
| func (t *rtype) NumOut() int { |
| if t.Kind() != Func { |
| panic("reflect: NumOut of non-func type") |
| } |
| tt := (*funcType)(unsafe.Pointer(t)) |
| return len(tt.out) |
| } |
| |
| func (t *rtype) Out(i int) Type { |
| if t.Kind() != Func { |
| panic("reflect: Out of non-func type") |
| } |
| tt := (*funcType)(unsafe.Pointer(t)) |
| return toType(tt.out[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) |
| } |
| |
| // NumMethod returns the number of interface methods in the type's method set. |
| func (t *interfaceType) NumMethod() int { return len(t.methods) } |
| |
| // 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 interface{}) Type { |
| eface := *(*emptyInterface)(unsafe.Pointer(&i)) |
| return toType(eface.typ) |
| } |
| |
| 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.(*rtype), t) |
| } |
| |
| func (t *rtype) AssignableTo(u Type) bool { |
| if u == nil { |
| panic("reflect: nil type passed to Type.AssignableTo") |
| } |
| uu := u.(*rtype) |
| return directlyAssignable(uu, t) || implements(uu, t) |
| } |
| |
| func (t *rtype) Comparable() bool { |
| switch t.Kind() { |
| case Bool, Int, Int8, Int16, Int32, Int64, |
| Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, |
| Float32, Float64, Complex64, Complex128, |
| Chan, Interface, Ptr, String, UnsafePointer: |
| return true |
| |
| case Func, Map, Slice: |
| return false |
| |
| case Array: |
| return (*arrayType)(unsafe.Pointer(t)).elem.Comparable() |
| |
| case Struct: |
| tt := (*structType)(unsafe.Pointer(t)) |
| for i := range tt.fields { |
| if !tt.fields[i].typ.Comparable() { |
| return false |
| } |
| } |
| return true |
| |
| default: |
| panic("reflectlite: impossible") |
| } |
| } |
| |
| // implements reports whether the type V implements the interface type T. |
| func implements(T, V *rtype) bool { |
| if T.Kind() != Interface { |
| return false |
| } |
| t := (*interfaceType)(unsafe.Pointer(T)) |
| if len(t.methods) == 0 { |
| return true |
| } |
| |
| // 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] |
| vm := &v.methods[j] |
| if *vm.name == *tm.name && (vm.pkgPath == tm.pkgPath || (vm.pkgPath != nil && tm.pkgPath != nil && *vm.pkgPath == *tm.pkgPath)) && rtypeEqual(toType(vm.typ).common(), toType(tm.typ).common()) { |
| if i++; i >= len(t.methods) { |
| return true |
| } |
| } |
| } |
| return false |
| } |
| |
| v := V.uncommon() |
| if v == nil { |
| return false |
| } |
| i := 0 |
| for j := 0; j < len(v.methods); j++ { |
| tm := &t.methods[i] |
| vm := &v.methods[j] |
| if *vm.name == *tm.name && (vm.pkgPath == tm.pkgPath || (vm.pkgPath != nil && tm.pkgPath != nil && *vm.pkgPath == *tm.pkgPath)) && rtypeEqual(toType(vm.mtyp).common(), toType(tm.typ).common()) { |
| 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 *rtype) bool { |
| // x's type V is identical to T? |
| if rtypeEqual(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 Type, cmpTags bool) bool { |
| if cmpTags { |
| return T == V |
| } |
| |
| if T.Name() != V.Name() || T.Kind() != V.Kind() { |
| return false |
| } |
| |
| return haveIdenticalUnderlyingType(T.common(), V.common(), false) |
| } |
| |
| func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { |
| if rtypeEqual(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 Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer { |
| return true |
| } |
| |
| // Composite types. |
| switch kind { |
| case Array: |
| return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) |
| |
| case 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() == 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 Func: |
| t := (*funcType)(unsafe.Pointer(T)) |
| v := (*funcType)(unsafe.Pointer(V)) |
| if t.dotdotdot != v.dotdotdot || len(t.in) != len(v.in) || len(t.out) != len(v.out) { |
| return false |
| } |
| for i, typ := range t.in { |
| if !haveIdenticalType(typ, v.in[i], cmpTags) { |
| return false |
| } |
| } |
| for i, typ := range t.out { |
| if !haveIdenticalType(typ, 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 Map: |
| return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) |
| |
| case Ptr, Slice: |
| return haveIdenticalType(T.Elem(), V.Elem(), cmpTags) |
| |
| case Struct: |
| t := (*structType)(unsafe.Pointer(T)) |
| v := (*structType)(unsafe.Pointer(V)) |
| if len(t.fields) != len(v.fields) { |
| return false |
| } |
| for i := range t.fields { |
| tf := &t.fields[i] |
| vf := &v.fields[i] |
| if tf.name != vf.name && (tf.name == nil || vf.name == nil || *tf.name != *vf.name) { |
| return false |
| } |
| if tf.pkgPath != vf.pkgPath && (tf.pkgPath == nil || vf.pkgPath == nil || *tf.pkgPath != *vf.pkgPath) { |
| return false |
| } |
| if !haveIdenticalType(tf.typ, vf.typ, cmpTags) { |
| return false |
| } |
| if cmpTags && tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) { |
| return false |
| } |
| if tf.offsetEmbed != vf.offsetEmbed { |
| 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 *rtype) Type { |
| if t == nil { |
| return nil |
| } |
| return t |
| } |
| |
| // ifaceIndir reports whether t is stored indirectly in an interface value. |
| func ifaceIndir(t *rtype) bool { |
| return t.kind&kindDirectIface == 0 |
| } |