The premise
Before we introduce reflection in GO, we need to introduce interface. There are two types of interfaces in GO, eface and iface.
eface
All types of data in Go can be converted to eface, which corresponds to emptyInterface in Reflect
type eface struct {
_type *_type // Type information
data unsafe.Pointer // The value to point to
}
type _type struct {
size uintptr // The size of the content occupied by the type
ptrdata uintptr // The size of the memory prefix containing all Pointers
hash uint32 / / type hash
tflag tflag // Flag bit, mainly used for reflection
align uint8 // Align fields
fieldAlign uint8 // The number of aligned bytes for the current structure field
kind uint8 // Base type enumeration value
equal func(unsafe.Pointer, unsafe.Pointer) bool// Compare the types of the objects corresponding to the two parameters
gcdata *byte // Data of GC type
str nameOff // The offset of the type name string in the binary file segment
ptrToThis typeOff // The offset of the type meta-information pointer in the binary file segment
}
Copy the code
Rutime. Runtime2. Go file
Eface consists mainly of Pointers to type information and values, which is easy to understand. The memory to which the data pointer points contains information about the type and value, that is, the data pointer points to eface itself
iface
Iface is mainly used to represent data that implements interface, corresponding to nonEmptyInterface in Reflect
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype // Static type of interface. An abstract representation of an interface is a static interface, not an actual struct
_type *_type // The actual type
hash uint32 // copy of _type.hash. Used for type switches. Like hash in _type, used for type assertion
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. The functions implemented by the interface are consistent with the type of the interface
}
type interfacetype struct {
typ _type // Interface type
pkgpath name / / package information
mhdr []imethod // The method abstraction of the interface is not the actual implemented method
}
Copy the code
The runtime. Runtime2. Go file
The iFace type field contains dynamic and static information for the type, as well as package and some function information.
Conversion of eFace and iFace
Unsafe. Pointer The _type field in eface and the inter field in iface can be converted via unsafe.Pointer if eface is of type Interface.
First, both are Pointers, so the alternative is unbroadening.Pointer.
Secondly, if we look carefully, we can find that the first field type of inter is _type.
Given the phenomenon, we venture a simple enough logic here: the in-memory model of type information in GO has fixed scope and rules.
For example, the first field type must be _type, and different types can have different fields depending on the type in addition to the _type.
So if an Interface type is turned to eface, then eface._type (*_type) can be turned to * interfaceType via unbroadening. Because the memory address to which the pointer *_type points contains interfaceType data.
Why can the pointer * interfaceType be converted to *_type? Since the first field of interfaceType is the _type, go can take the size of the _type from where the pointer starts.
reflect.Type
Let’s first look at the reflect.typeof code
func TypeOf(i interface{}) Type {
/ / to eface
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
Copy the code
Eface and reflect.emptyInterface are the same, so unsafe.Pointer converts emptyInterface directly, And then take the TYP property. The operations related to reflect.Type are based on *rtype
There are 26 basic types in GO, which are also enumerated in reflection
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
)
Copy the code
In reflects. Type, many types have specific types, but only Interface data has special logic.
Here are the different structures that correspond to the different types
type arrayType struct {
rtype
elem *rtype // array element type
slice *rtype // slice type
len uintptr
}
type chanType struct {
rtype
elem *rtype // channel element type
dir uintptr // Channel direction (ChanDir) Indicates the direction of chan
}
type funcType struct {
rtype
inCount uint16 // Enter parameters
outCount uint16 // top bit is set if last input parameter is ... Output parameters
}
type structType struct {
rtype
pkgPath name
fields []structField // sorted by offset
}
type ptrType struct {
rtype // The type of pointer
elem *rtype // pointer element (pointed at) type The type of the element to which the pointer points (static type)
}
type sliceType struct {
rtype
elem *rtype // slice element type
}
type mapType struct {
rtype
key *rtype // map key type
elem *rtype // map element (value) type
bucket *rtype // internal bucket structure
// function for hashing keys (ptr to key, seed) -> hash
hasher func(unsafe.Pointer, uintptr) uintptr
keysize uint8 // size of key slot
valuesize uint8 // size of value slot
bucketsize uint16 // size of bucket
flags uint32
}
type interfaceType struct {
rtype
pkgPath name // import path
methods []imethod // sorted by hash
}
Copy the code
All of the above types can pass*rtype
conversion
We’ll take a look at some of the reflects. Type functions to see how reflection takes the value of the _type and how each Type differs.
Method
The getfunction needs to distinguish whether it is of type Interface. If it is of type Interface, convert * rType to *interfaceType. Because the interfaceType type contains the function information field, so directly according to the index is ok, this logic is easy case.
func (t *interfaceType) Method(i int) (m Method) {
if i < 0 || i >= len(t.methods) {
return
}
p := &t.methods[i]
/ / construction method
pname := t.nameOff(p.name)
m.Name = pname.name()
if! pname.isExported() { m.PkgPath = pname.pkgPath()if m.PkgPath == "" {
m.PkgPath = t.pkgPath.name()
}
}
m.Type = toType(t.typeOff(p.typ))
m.Index = i
return
}
Copy the code
Interface type method code
If it is any other type, the function information needs to be exported. Since the function information is not displayed as a field, it needs to form a temporary structure to fetch it from memory
func (t *rtype) uncommon(a) *uncommonType {
if t.tflag&tflagUncommon == 0 {
return nil
}
switch t.Kind() {
case Struct:
return &(*structTypeUncommon)(unsafe.Pointer(t)).u
case Ptr:
type u struct {
ptrType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Func:
type u struct {
funcType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Slice:
type u struct {
sliceType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Array:
type u struct {
arrayType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Chan:
type u struct {
chanType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Map:
type u struct {
mapType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Interface:
type u struct {
interfaceType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
default:
type u struct {
rtype
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
}
}
Copy the code
Uncommon () function
The uncommonType above is the structure that stores the function information
// Store the type of the function
type uncommonType struct {
pkgPath nameOff // import path; empty for built-in types like int, string
mcount uint16 // number of methods
xcount uint16 // Number of exported methods
moff uint32 // offset from this uncommonType to [McOunt]method
_ uint32 // unused
}
type method struct {
name nameOff // name of method
mtyp typeOff // method type (without receiver) Function type
ifn textOff // fn used in interface call (one-word receiver)
tfn textOff // fn used for normal method call
}
Copy the code
UncommonType type
With uncommonType, you can get the type information of the function
func (t *rtype) Method(i int) (m Method) {
// If it is interface
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.Method(i)
}
// Get the list of exported functions
methods := t.exportedMethods()
if i < 0 || i >= len(methods) {
panic("reflect: Method index out of range")
}
p := methods[i]
// Take the function name
pname := t.nameOff(p.name)
m.Name = pname.name()
fl := flag(Func)
mtyp := t.typeOff(p.mtyp)
ft := (*funcType)(unsafe.Pointer(mtyp))
in := make([]Type, 0.1+len(ft.in()))
// Add the current type to in
in = append(in, t)
// Add the parameter type to in
for _, arg := range ft.in() {
in = append(in, arg)
}
// Returns an array of value types
out := make([]Type, 0.len(ft.out()))
for _, ret := range ft.out() {
out = append(out, ret)
}
// Build the function
mt := FuncOf(in, out, ft.IsVariadic())
m.Type = mt
tfn := t.textOff(p.tfn)
fn := unsafe.Pointer(&tfn)
m.Func = Value{mt.(*rtype), fn, fl}
m.Index = i
return m
}
Copy the code
* Rtype Method Method
FuncOf takes the receiver as the first input parameter, so it needs to re-form * rType as the Type of method. So if you want to Call the Call Method using method.func, you need to pass in the receiver as the first argument.
The FuncOf method is longer, so I won’t post the code here. Briefly explain the main logic
- Create a new
funcType
- Assign input and output parameters, and
hash
Value (mixed with variable function judgment) - Find funcTypes of the same type in the cache and return them if they exist
- I’m going to look it up in the linker, whatever it is, put it in the cache and return it
Field
The type to get the Field must be Struct
func (t *rtype) Field(i int) StructField {
ift.Kind() ! = Struct {panic("reflect: Field of non-struct type " + t.String())
}
/ / to structType
tt := (*structType)(unsafe.Pointer(t))
return tt.Field(i)
}
Copy the code
* Rtype Field method
As you can see, the type is determined and then converted to *structType
type structType struct {
rtype
pkgPath name
fields []structField // sorted by offset
}
Copy the code
StructType structure
The structType method for getting a Field is very simple
func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) {
panic("reflect: Field index out of bounds")
}
p := &t.fields[i]
f.Type = toType(p.typ)
f.Name = p.name.name()
f.Anonymous = p.embedded()
if! p.name.isExported() { f.PkgPath = t.pkgPath.name() }iftag := p.name.tag(); tag ! ="" {
f.Tag = StructTag(tag)
}
f.Offset = p.offset()
f.Index = []int{i}
return
}
Copy the code
Implements
func (t *rtype) Implements(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.Implements")}ifu.Kind() ! = Interface {panic("reflect: non-interface type passed to Type.Implements")}return implements(u.(*rtype), t)
}
Copy the code
*rtype Implements method
This method means: whether t implements u
In go, we get u as Interface: reflect.typeof ((* fmt.stringer)(nil)).elem ()
Declare an empty interface pointer and take its Elem. Why is this method available, you need to look at the Elem method
func (t *rtype) Elem(a) 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 " + t.String())
}
Copy the code
* Elem method of rtype
From the code above, we know that t’s Kind is now a pointer, so we convert it to ptrType, and then take the ptrType.elem field as the type. So the ELEm field should point to a static type. The above elem for Array, Chan, and so on are static types.
Then the logic of implements. Implements (); implements (); implements (); implements (); implements (); implements ()
-
Let’s say t is of type Interface
if V.Kind() == Interface { v := (*interfaceType)(unsafe.Pointer(V)) i := 0 for j := 0; j < len(v.methods); j++ { tm := &t.methods[i] tmName := t.nameOff(tm.name) vm := &v.methods[j] vmName := V.nameOff(vm.name) // The function name is the same as the type // an rtype must be used for functions with the same input and output parameters if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if! tmName.isExported() { tmPkgPath := tmName.pkgPath()if tmPkgPath == "" { tmPkgPath = t.pkgPath.name() } vmPkgPath := vmName.pkgPath() if vmPkgPath == "" { vmPkgPath = v.pkgPath.name() } iftmPkgPath ! = vmPkgPath {continue}}if i++; i >= len(t.methods) { return true}}}return false } Copy the code
-
Suppose it’s not Interface
// Instead of interface, determine the actual function 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 := t.nameOff(tm.name) vm := vmethods[j] vmName := V.nameOff(vm.name) if vmName.name() == tmName.name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) { if! tmName.isExported() { tmPkgPath := tmName.pkgPath()if tmPkgPath == "" { tmPkgPath = t.pkgPath.name() } vmPkgPath := vmName.pkgPath() if vmPkgPath == "" { vmPkgPath = V.nameOff(v.pkgPath).name() } iftmPkgPath ! = vmPkgPath {continue}}if i++; i >= len(t.methods) { return true}}}Copy the code
We can see that the above two pieces of code have the same logic and simple, both are to determine whether the type and name are the same, if the non-exported functions need to determine whether the package name is the same
AssignableTo
func (t *rtype) AssignableTo(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.AssignableTo")
}
uu := u.(*rtype)
// Determine the type
return directlyAssignable(uu, t) || implements(uu, t)
}
Copy the code
* AssignableTo method of rtype
Whether t can be assigned to u
DirectlyAssignable () : directlyAssignable (
func directlyAssignable(T, V *rtype) 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.
ifT.hasName() && V.hasName() || T.Kind() ! = V.Kind() {return false
}
if T.Kind() == Chan && specialChannelAssignability(T, V) {
return true
}
// x's type T and V must have identical underlying types.
return haveIdenticalUnderlyingType(T, V, true)}Copy the code
The directlyAssignable method in reflect.type.go
To explain the above logic:
*rtype
Equal, assignableKind
Must be equal, and the name of one of the types must be null- A type of
chan
“, need to judgeElem
和chan
The direction of the
In terms of type names, some basic types have names and some don’t
- Bool – Complex128 ranges from 1 to 16 and has a name
- 17 no name array
- Chan, 50-18 without a name
- Func is 51-19. No name
- Interface 20 have a name
- The map 53-21 without a name
- PTR 54-22 without a name
- Slice 23 no name
- A string of 24 has a name
- Struct 25 anonymous no name, other
- The unsafe. 58-26 has a Pointer
Struct anonymous data does not have a name. Struct anonymous data does not have a name
Then see haveIdenticalUnderlyingType method
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:
return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case Func:
t := (*funcType)(unsafe.Pointer(T))
v := (*funcType)(unsafe.Pointer(V))
ift.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
}
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
}
ift.pkgPath.name() ! = v.pkgPath.name() {return false
}
for i := range t.fields {
tf := &t.fields[i]
vf := &v.fields[i]
iftf.name.name() ! = vf.name.name() {return false
}
if! haveIdenticalType(tf.typ, vf.typ, cmpTags) {return false
}
ifcmpTags && tf.name.tag() ! = vf.name.tag() {return false
}
iftf.offsetEmbed ! = vf.offsetEmbed {return false}}return true
}
Copy the code
Simple types can be as long as kind is consistent, and complex types have their own judgments, but the logic is very simple
reflect.Value
Reflect. Value can be obtained from reflect.ValueOf. Reflect. ValueOf mainly calls the unpackEface method to unbox a Value
func unpackEface(i interface{}) Value {
/ / to eface
e := (*emptyInterface)(unsafe.Pointer(&i))
// NOTE: don't read e.word until we know whether it is really a pointer or not.
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}
func ifaceIndir(t *rtype) bool {
return t.kind&kindDirectIface == 0
}
kindDirectIface = 1 << 5 / / 32
Copy the code
UnpackEface function
Similar to Reflect. TypeOf, which is converted to emptyInterface first. If there is a logic ifaceIndir | = flagIndir} {f (t), if the original kind in 32 t equals 0, then is indirect. Most types are indirect, with only the following types being direct:
- map
- chan
- func
- ptr
- unsafe.Pointer
For direct or indirect types, there is a different processing logic when boxed into interfaces (that is, when the Interface() method is called).
As with reflect.Type, we analyze reflect.Value in a few key ways, and take a look at the implementation of reflect.Value
Method
Get the Value of method
func (v Value) Method(i int) Value {
if v.typ == nil {
panic(&ValueError{"reflect.Value.Method", Invalid})
}
// is itself a method
ifv.flag&flagMethod ! =0 || uint(i) >= uint(v.typ.NumMethod()) {
panic("reflect: Method index out of range")}if v.typ.Kind() == Interface && v.IsNil() {
panic("reflect: Method on nil interface value")}// Add the function-dependent flag complement
fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
fl |= flag(Func)
fl |= flag(i)<<flagMethodShift | flagMethod
// Type is not the type of Method, but the type of Method receiver
return Value{v.typ, v.ptr, fl}
}
Copy the code
Value Method Method of the Value
The code for the Method Method is simple, just add flag bits to it. Neither type nor PTR has changed, and the meaning of this will be seen in the Call method.
Call
func (v Value) Call(in []Value) []Value {
v.mustBe(Func)
v.mustBeExported()
return v.call("Call", in)
}
Copy the code
The Call method of Value
We see that the Call implementation relies primarily on the Call method. The initial logic of the Call method looks like this:
func (v Value) call(op string, in []Value) []Value {
// Whatever v.type is, convert it to funcType first
t := (*funcType)(unsafe.Pointer(v.typ))
var (
fn unsafe.Pointer
rcvr Value
rcvrtype *rtype
)
// v is actually the receiver
ifv.flag&flagMethod ! =0 {
rcvr = v
rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
} else ifv.flag&flagIndir ! =0 {
fn = *(*unsafe.Pointer)(v.ptr)
} else {
fn = v.ptr
}
Copy the code
If the Value is obtained using the Method Method, the methodReceiver Method needs to handle it.
func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *funcType, fn unsafe.Pointer) {
i := methodIndex
if v.typ.Kind() == Interface {
// Change to the interface type first
tt := (*interfaceType)(unsafe.Pointer(v.typ))
if uint(i) >= uint(len(tt.methods)) {
panic("reflect: internal error: invalid method index")}// retrieve method from the interface
m := &tt.methods[i]
if! tt.nameOff(m.name).isExported() {panic("reflect: " + op + " of unexported method")}/ / to iface
iface := (*nonEmptyInterface)(v.ptr)
if iface.itab == nil {
panic("reflect: " + op + " of method on nil interface value")}// Dynamic type type
rcvrtype = iface.itab.typ
// Fn pointer to the dynamic type
fn = unsafe.Pointer(&iface.itab.fun[i])
FuncType statically, because it is the same as funcType dynamically
t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ)))
} else {
rcvrtype = v.typ
// A collection of methods that can be exported
ms := v.typ.exportedMethods()
if uint(i) >= uint(len(ms)) {
panic("reflect: internal error: invalid method index")
}
m := ms[i]
if! v.typ.nameOff(m.name).isExported() {panic("reflect: " + op + " of unexported method")
}
ifn := v.typ.textOff(m.ifn)
fn = unsafe.Pointer(&ifn)
t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp)))
}
return
}
Copy the code
MethodReceiver method
The methodReceiver gets the corresponding method type, pointer, and receiver type.
The call method then does some variable parameter judgment and combines the parameter values passed in, and then calls Runtime. call.
call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
The above method actually calls the function. Then combine the return Value into []Value. The combination logic of return Value mainly involves the displacement operation of pointer address, which will not be described here.
Convert
func (v Value) Convert(t Type) Value {
ifv.flag&flagMethod ! =0 {
v = makeMethodValue("Convert", v)
}
op := convertOp(t.common(), v.typ)
if op == nil {
panic("reflect.Value.Convert: value of type " + v.typ.String() + " cannot be converted to type " + t.String())
}
return op(v, t)
}
Copy the code
Value Convert function
Indicates that v is converted to a Value of type t. Reflect.typeof ().convertibleto is also implemented by calling convertOp. This method is also called for reflection assignments including fields under Model in Brom. This method has problems converting numbers to strings.
func convertOp(dst, src *rtype) func(Value, Type) Value {
switch src.Kind() {
case Int, Int8, Int16, Int32, Int64:
switch dst.Kind() {
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return cvtInt
case Float32, Float64:
return cvtIntFloat
case String:
return cvtIntString
}
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
switch dst.Kind() {
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return cvtUint
case Float32, Float64:
return cvtUintFloat
case String:
return cvtUintString
}
case Float32, Float64:
switch dst.Kind() {
case Int, Int8, Int16, Int32, Int64:
return cvtFloatInt
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return cvtFloatUint
case Float32, Float64:
return cvtFloat
}
case Complex64, Complex128:
switch dst.Kind() {
case Complex64, Complex128:
return cvtComplex
}
case String:
if dst.Kind() == Slice && dst.Elem().PkgPath() == "" {
switch dst.Elem().Kind() {
case Uint8:
return cvtStringBytes
case Int32:
return cvtStringRunes
}
}
case Slice:
if dst.Kind() == String && src.Elem().PkgPath() == "" {
switch src.Elem().Kind() {
case Uint8:
return cvtBytesString
case Int32:
return cvtRunesString
}
}
case Chan:
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
return cvtDirect
}
}
// dst and src have same underlying type.
if haveIdenticalUnderlyingType(dst, src, false) {
return cvtDirect
}
// dst and src are non-defined pointer types with same underlying base type.
if dst.Kind() == Ptr && dst.Name() == "" &&
src.Kind() == Ptr && src.Name() == "" &&
haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) {
return cvtDirect
}
if implements(dst, src) {
if src.Kind() == Interface {
return cvtI2I
}
return cvtT2I
}
return nil
}
Copy the code
So let’s look at the conversion of numbers to strings, so let’s look at cvtIntString
func cvtIntString(v Value, t Type) Value {
return makeString(v.flag.ro(), string(v.Int()), t)
}
func makeString(f flag, v string, t Type) Value {
ret := New(t).Elem()
ret.SetString(v)
ret.flag = ret.flag&^flagAddr | f
return ret
}
Copy the code
In go, you cannot convert an int toa string. We usually call strconv.itoa () to convert a decimal number. If you’re interested, take a look at strconv.itoa (), which is the equivalent of breaking numbers into strings, bit by bit
All other conversions should be normal, checking whether the type can be converted and then returning a different conversion function
Elem
Next, take a look at the Elem method, which focuses on PTR and interface
func (v Value) Elem(a) Value {
k := v.kind()
switch k {
case Interface:
var eface interface{}
if v.typ.NumMethod() == 0 {
eface = *(*interface{})(v.ptr)
} else {
eface = (interface({}) (* *interface {
M() // Fear of losing some type information when cast to eface
})(v.ptr))
}
x := unpackEface(eface)
ifx.flag ! =0 {
x.flag |= v.flag.ro()
}
// interface Specifies the actual type
return x
case Ptr:
ptr := v.ptr
// PTR needs to refetch the pointer to the pointer
ifv.flag&flagIndir ! =0 {
ptr = *(*unsafe.Pointer)(ptr)
}
// The returned value's address is v's value.
if ptr == nil {
return Value{}
}
tt := (*ptrType)(unsafe.Pointer(v.typ))
typ := tt.elem
/ / change the flag
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
Copy the code
Let’s say logic of type PTR:
- First to
ptrType
- take
ptrType.elem
Element type as newValue
We know thatelem
Refers to the static type of the element - then
flag
Bit or some flag bit
There is an if judgment, if v.lag&flagindir! = 0 {PTR = *(*unsafe.Pointer)(PTR)} we didn’t say that. When does it get into this if logic?
Now, if the data is a pointer to a pointer, and we call Elem, flag takes the flagIndir bit, we see that only TYP and flag change, PTR does not change, so PTR is still a pointer to a pointer. If we now call the Elem Value again, it will go into the if block above.
What’s going on in the if block? Convert PTR to a pointer to a pointer, and then evaluate, and you get a pointer. That is, PTR is now a normal pointer to a value. Remember this logic, which we’ll see when Value is boxed as interface.
Logic of type Interface:
- To empty
interface
If you have a function, you need to convert it to a bandM()
Methods the emptyinterface
Fear of losing type information - The empty
interface
Out of the box intoValue
Here,Value
It becomes dynamic
Interface
Interface returns the actual Value of Value
func (v Value) Interface(a) (i interface{}) {
return valueInterface(v, true)}func valueInterface(v Value, safe bool) interface{} {
if v.flag == 0 {
panic(&ValueError{"reflect.Value.Interface", Invalid})
}
ifsafe && v.flag&flagRO ! =0 {
panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")}ifv.flag&flagMethod ! =0 {
v = makeMethodValue("Interface", v)
}
if v.kind() == Interface {
// Special case: return the element inside the interface.
// Empty interface has one layout, all interfaces with
// methods have a second layout.
if v.NumMethod() == 0 {
return* (*interface{})(v.ptr)
}
return* (*interface {
M()
})(v.ptr)
}
return packEface(v)
}
Copy the code
The main thing is to call the packEface method and box the Value into Interface
func packEface(v Value) interface{} {
t := v.typ
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
switch {
// Indirect memory
case ifaceIndir(t):
if v.flag&flagIndir == 0 {
panic("bad indir")
}
ptr := v.ptr
// the description is addressable
ifv.flag&flagAddr ! =0 {
c := unsafe_New(t)
typedmemmove(t, c, ptr)
// Make a new address assignment
ptr = c
}
// it is not addressable, indicating that it is a copy of the value
e.word = ptr
casev.flag&flagIndir ! =0:
e.word = *(*unsafe.Pointer)(v.ptr)
default:
// Value is direct, and so is the interface. It's a pointer
e.word = v.ptr
}
e.typ = t
return i
}
Copy the code
If the address is indirect:
- Addressable,
new
A new pointerptr
Is copied to the new pointer and assigned toe.word
. Change returnedinterface
It doesn’t affect the original value - Unaddressable, indicating a value copy, directly assigned
e.word
Direct address with the indirect flag in the flag bit:
- A pointer to a pointer, then evaluates to a pointer and assigns to
e.word
There is a problem with the type of direct address, such as map. We declare A map pointer to A and then do B:= reflect.valueof (A).elem ().elem (). And B’s PTR is now a pointer to map, because the second Elem has converted the pointer to a pointer. Then we call b.interface (), at which point we find:
B
是map
, is the direct typeB
The flag bit of theflagIndir
The indirect sign- perform
e.word = *(*unsafe.Pointer)(v.ptr)
operation
Even though unsafe.Pointer can be converted to any Pointer, the address of the Pointer will definitely change.
But! But! This code works!! So this piece of code eventually points to the correctmap
值
So let’s guess, for direct types, Pointers are Pointers that can be converted to Pointers. But the following single test panic, can not prove this logic:
a := map[string]string{"cc": "dd"}
c := unsafe.Pointer(&a)
fmt.Println(c)
d := *(*unsafe.Pointer)(c)
fmt.Println(d)
f := *(*map[string]string)(d)
fmt.Println(f)
Copy the code
It’s a piece of logic I can’t figure out!