go 反射

1,354 阅读16分钟

前提

介绍 go 中的反射之前,我们需要先介绍一下 interface。go 中的 interface 分类两种,eface 和 iface。

eface

go 中所有的类型的数据都可以转成 eface ,对应 reflect 中的 emptyInterface

type eface struct {
	_type *_type // 类型信息
	data  unsafe.Pointer // 指向的值
}

type _type struct {
	size       uintptr  // 类型占用内容大小
	ptrdata    uintptr  // 包含所有指针的内存前缀的大小
	hash       uint32   // 类型 hash
	tflag      tflag    // 标记位,主要用于反射
	align      uint8    // 对齐字段
	fieldAlign uint8   // 当前结构字段的对齐字节数
	kind       uint8   // 基础类型枚举值
	equal func(unsafe.Pointer, unsafe.Pointer) bool // 比较两个形参对应对象的类型是否相等
	gcdata    *byte // GC 类型的数据
	str       nameOff // 类型名称字符串在二进制文件段中的偏移量
	ptrToThis typeOff // 类型元信息指针在二进制文件段中的偏移量
}

rutime.runtime2.go 文件

eface 主要包括类型信息和值的指针,非常好理解。其中 data 指针指向的内存包含了类型和值的信息,也就是说 data 指针指向了 eface 本身

iface

iface 主要用来表示实现了 interface 的数据,对应 reflect 中的 nonEmptyInterface

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type itab struct {
	inter *interfacetype // 接口静态类型, 接口的抽象表示,也就是静态的接口,不是实际的 struct
	_type *_type // 实际类型
	hash  uint32 // copy of _type.hash. Used for type switches. 和 _type 中的 hash 一样,用来类型断言
	_     [4]byte
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. 接口实现的函数,跟接口类型保持一致
}
type interfacetype struct {
	typ     _type // interface 的类型
	pkgpath name // 包信息
	mhdr    []imethod // interface 的 method 抽象,不是实际的被实现的 method
}

runtime.runtime2.go 文件

iface 类型字段中包含类型的动态和静态信息,以及包和一些函数信息。

eface 和 iface 的转换

我们知道 任何指针都可以转换成 unsafe.Pointer, unsafe.Pointer 也可以转换成任意的指针

在使用 unsafe.Pointer 进行转换时,不会进行类型检查,只是粗暴的把类型替换,指针的地址和实际的值都不会变化。

假如使用 unsafe.Pointer 进行转换的两种数据类型差异很大,转换之后使用的时候也会出错。这也是叫 unsafe 的原因。

对于 eface 和 iface:假如 eface 的类型是 Interface,那么 eface 中的 _type 字段和 iface 中的 inter 是可以通过 unsafe.Pointer 进行转换的。

首先,两者都是指针,所以可以转成 unsafe.Pointer。其次如果我们仔细观察,会发现 inter 的第一个字段类型是 _type

所以可以进行转换,并且转换之后 interfacetype 的其他字段也是有值的。说明 eface 的 type 类型并不是只有显示定义的字段,还存在一些隐藏的字段,根据类型的不同,拥有不同的字段。

reflect.Type

首先现看一下 reflect.TypeOf 代码,Type 是 interface , *rtype 实现了 Type

func TypeOf(i interface{}) Type {
	// 转成 eface
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}

上面我们介绍 eface 的时候说了, runtime.eface 和 reflect.emptyInterface 是一样的,所以这里直接通过 unsafe.Pointer 转换成 emptyInterface ,然后取其中的 typ 属性。 reflect.Type 相关的操作都是基于 *rtype

go 中的基本类型总共 26 种,在反射中也有枚举体现

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
)

在操作 reflect.Type 时很多类型都有对应的专属 type,不过只有 Interface 类型的数据会有一些特殊逻辑。

下面是各种不同类型对应的不同结构体

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) chan 的方向
}

type funcType struct {
	rtype
	inCount  uint16  // 输入参数
	outCount uint16 // top bit is set if last input parameter is ... 输出参数
}

type structType struct {
	rtype
	pkgPath name
	fields  []structField // sorted by offset
}

type ptrType struct {
	rtype // 指针的类型
	elem *rtype // pointer element (pointed at) 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
}

以上所有的类型都可以通过 *rtype 转换

下面我们通过分析 reflect.Type 的几个函数,窥一斑而见全豹,来聊一聊反射是怎么取 _type 中的数据,进一步了解各个类型的不同之处。

Method

获取函数需要区分是否为 Interface 类型,假如是 Interface 类型,把 *rtype 转成 *interfaceType 。因为 interfaceType 类型中包含了函数信息字段,所以直接根据索引取一下就行了,这块逻辑是 easy case。

func (t *interfaceType) Method(i int) (m Method) {
	if i < 0 || i >= len(t.methods) {
		return
	}
	p := &t.methods[i]
	// 构造 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
}

type imethod struct {
	name nameOff // name of method
	typ  typeOff // .(*FuncType) underneath  指向 method 的 type 类型
}

interface 类型取 method 的代码

假如是其他类型,就需要导出函数信息了。因为函数信息并没有显示的以字段的形式,所以需要组成临时的结构体取一下

func (t *rtype) exportedMethods() []method {
	// 获取存储函数列表的 uncommon
	ut := t.uncommon()
	if ut == nil {
		return nil
	}
	// 获取导出的函数列表
	return ut.exportedMethods()
}


func (t *rtype) uncommon() *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
	}
}

func (t *uncommonType) exportedMethods() []method {
	if t.xcount == 0 {
		return nil
	}
	return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.xcount > 0"))[:t.xcount:t.xcount]
}

uncommon() 函数

上面的 uncommonType 是存储函数信息的结构体

// 储存函数的类型
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) 函数类型
	ifn  textOff // fn used in interface call (one-word receiver)
	tfn  textOff // fn used for normal method call
}

uncommonType 类型

有了 uncommonType 就可以获取函数的类型信息了

func (t *rtype) Method(i int) (m Method) {
	// 假如是 interface
	if t.Kind() == Interface {
		tt := (*interfaceType)(unsafe.Pointer(t))
		return tt.Method(i)
	}
	// 获取导出的函数列表
	methods := t.exportedMethods()
	if i < 0 || i >= len(methods) {
		panic("reflect: Method index out of range")
	}
	p := methods[i]
	// 取函数的名字
	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()))
	// 先把当前类型加到 in 里
	in = append(in, t)
	// 再把参数类型加到 in 里
	for _, arg := range ft.in() {
		in = append(in, arg)
	}
	// 返回值类型数组
	out := make([]Type, 0, len(ft.out()))
	for _, ret := range ft.out() {
		out = append(out, ret)
	}

	// 构建函数
	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
}

*rtype 的 Method 方法

从上面的代码可以看出来,在取到 method 之后,会根据 method 转成 Method,而 Method 会把接收者当做第一个入参,所以需要 FuncOf 重新组建一个 *rtype, 当做 Method 的 Type。所以假如想使用 Method.Func 调用 Call 方法时,需要把接收者当做第一个参数传入。

FuncOf 方法比较长,这里就不贴代码了。简单说明一下主要逻辑

  • 新建一个 funcType
  • 赋值入参和出参,以及 hash 值(夹杂着可变函数的判断)
  • 从缓存中找相同类型的 funcType,有就返回
  • 从链接器等中查找,不管有没有都会放进缓存然后返回

Field

获取 Field 的类型必须是 Struct

func (t *rtype) Field(i int) StructField {
	if t.Kind() != Struct {
		panic("reflect: Field of non-struct type " + t.String())
	}
	// 转成 structType
	tt := (*structType)(unsafe.Pointer(t))
	return tt.Field(i)
}

*rtype 的 Field 方法

可以看到先判断类型,然后转成 *structType

type structType struct {
	rtype
	pkgPath name
	fields  []structField // sorted by offset
}

structType 结构体

structType获取 Field 的方法非常简单

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()
	}
	if tag := p.name.tag(); tag != "" {
		f.Tag = StructTag(tag)
	}
	f.Offset = p.offset()
	f.Index = []int{i}
	return
}

Implements

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)
}

*rtype 的 Implements 方法

这个方法含义是:t 是否实现了 u

上面要求 u 的类型必须是 Interface,在 go 中,我们一般这么获取 u 这种类型: reflect.TypeOf((*fmt.Stringer)(nil)).Elem()

声明一个空的 interface 指针,然后取它的 Elem。为什么这种方法可以取到呢,就需要看一下 Elem 方法了

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 " + t.String())
}

*rtype 的 Elem 方法

按照上面的代码,我们知道 t 现在的 Kind 是指针,所以转成了 ptrType,然后取 ptrType.elem 字段作为类型。所以 elem字段指向的应该是静态类型。以上 Array、Chan 等等的 elem 都是静态类型。

接着 implements 的逻辑。implements 的方法主要有两个分支判断,参数顺序有点变化,下面代码里的 V 其实 t,下面代码里的 t 其实是 u

  • 假如 t 是 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)
    			// 函数名称和类型一样就行
    			// 得保证相同的入参和出参的函数用的一个 rtype
    			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()
    					}
    					if tmPkgPath != vmPkgPath {
    						continue
    					}
    				}
    				if i++; i >= len(t.methods) {
    					return true
    				}
    			}
    		}
    		return false
    	}
    
  • 假如不是 Interface 类型

    // 不是 interface,判断实际的函数
    	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()
    				}
    				if tmPkgPath != vmPkgPath {
    					continue
    				}
    			}
    			if i++; i >= len(t.methods) {
    				return true
    			}
    		}
    	}
    

我们可以看到上面两块代码的逻辑一致且简单,都是判断类型和名称是否一致,假如是非导出的函数需要再判断包名是否一致

AssignableTo

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)
}

*rtype 的 AssignableTo 方法

含义:t 是否可以赋值给 u

是否实现的逻辑我们已经看过了,直接看 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.
	if T.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)
}

reflect.type.go 中的 directlyAssignable 方法

解释一下上面的逻辑:

  • *rtype相等,可赋值
  • Kind 必须相等,并且其中一个类型的 name 必须为空
  • 类型为 chan 时,需要判断 Elem 和 chan 的方向

关于类型的名称,有一些基本类型是有名称,一些没有

  • bool - Complex128 为 1-16 有name
  • array 17 无 name
  • chan 50-18 无 name
  • func 为 51-19 无 name
  • interface 20 有 name
  • map 53-21 无 name
  • ptr 54-22 无 name
  • slice 23 无 name
  • string 24 有 name
  • struct 25 匿名的无 name,其他的有
  • unsafe.Pointer 58-26 有

以 type 关键字声明的类型都是有 name 的,所以 struct 类型的匿名数据才会没有 name

接着看 haveIdenticalUnderlyingType 方法

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))
		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
		}
		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
		}
		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.offsetEmbed != vf.offsetEmbed {
				return false
			}
		}
		return true
	}

其中简单类型只要 kind 一致就可以,复杂类型各有各的判断,不过逻辑都很简单

reflect.Value

reflect.Value 主要通过 reflect.ValueOf 获取。reflect.ValueOf 主要是调用 unpackEface方法开箱成 Value

func unpackEface(i interface{}) Value {
	// 转成 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

unpackEface 函数

跟 reflect.TypeOf 很像,都是先转成 emptyInterface。这里有一个逻辑 if ifaceIndir(t) { f |= flagIndir},假如 t 的原始 kind 位于 32 等于 0,那么就是间接的。大部分类型都是间接的,只有以下几种类型为直接的:

  • map
  • chan
  • func
  • ptr
  • unsafe.Pointer

对于直接或者间接的类型,在装箱成 Interface 的时候(也就是调用 Interface() 方法时)会有不同的处理逻辑。

同分析 reflect.Type 一样,我们分析 reflect.Value 也通过几个关键方法,来看一看 reflect.Value 的实现

Method

获取 method 的 Value

func (v Value) Method(i int) Value {
	if v.typ == nil {
		panic(&ValueError{"reflect.Value.Method", Invalid})
	}
	// 本身就是个 method
	if v.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")
	}
	// 加上函数相关的 flag 补码
	fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
	fl |= flag(Func)
	fl |= flag(i)<<flagMethodShift | flagMethod
	// 这里的 type 并不是 Method 的 type, 而是方法接收者的 type
	return Value{v.typ, v.ptr, fl}
}

Value 的 Method 方法

Method 方法的代码很简单,就是把 flag 加上一些标志位。type 和 ptr 都没有变化,这么做的意义会 Call 方法中了解到。

Call

func (v Value) Call(in []Value) []Value {
	v.mustBe(Func)
	v.mustBeExported()
	return v.call("Call", in)
}

Value 的 Call 方法

我们看到 Call 的实现主要是依赖 call 方法, call 方法刚开始的逻辑是这样的:

func (v Value) call(op string, in []Value) []Value {
// 不管 v.type 是啥,先转成 funcType,反正可以转
	t := (*funcType)(unsafe.Pointer(v.typ))
	var (
		fn       unsafe.Pointer
		rcvr     Value
		rcvrtype *rtype
	)
	// v 其实是接收者
	if v.flag&flagMethod != 0 {
		rcvr = v
		rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
	} else if v.flag&flagIndir != 0 {
		fn = *(*unsafe.Pointer)(v.ptr)
	} else {
		fn = v.ptr
	}

假如是使用 Method 方法获取到的 Value,需要 methodReceiver 方法处理一下。

func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *funcType, fn unsafe.Pointer) {
	i := methodIndex
	if v.typ.Kind() == Interface {
		// 先转成 接口类型
		tt := (*interfaceType)(unsafe.Pointer(v.typ))
		if uint(i) >= uint(len(tt.methods)) {
			panic("reflect: internal error: invalid method index")
		}
		// 取接口里的 method
		m := &tt.methods[i]
		if !tt.nameOff(m.name).isExported() {
			panic("reflect: " + op + " of unexported method")
		}
		// 转成 iface
		iface := (*nonEmptyInterface)(v.ptr)
		if iface.itab == nil {
			panic("reflect: " + op + " of method on nil interface value")
		}
		// 动态类型的 type
		rcvrtype = iface.itab.typ
		// 动态类型指向的 fn 指针
		fn = unsafe.Pointer(&iface.itab.fun[i])
		// 静态类型的 funcType, 因为和动态类型的一样
		t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ)))
	} else {
		rcvrtype = v.typ
		// 可导出的方法集合
		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
}

type method struct {
	name nameOff // name of method
	mtyp typeOff // method type (without receiver) 函数类型
	ifn  textOff // fn used in interface call (one-word receiver)
	tfn  textOff // fn used for normal method call
}

methodReceiver 方法

methodReceiver 会获取对应 method 的类型、指针以及接收者的类型。

call 方法接着会做一些可变参数的判断以及组合传入的参数值,然后调用 runtime.call (实际是 runtime.reflectcall)

call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))

上面的方法才会真正的调用函数。然后把返回值组合成 []Value 返回,返回值的组合逻辑主要涉及指针地址的位移操作,这里就不累述了。

Convert

func (v Value) Convert(t Type) Value {
	if v.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)
}

Value 的 Convert 函数

表示 v 转成类型为 t 的 Value。reflect.TypeOf().ConvertibleTo 也是调用 convertOp 来实现的。包括 brom 中 model下字段的反射赋值也是调用的这个方法。但是这个方法有在数字转成字符串的时候会有问题。

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
}

我们先看数字转成字符串,拿其中的 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
}

这里直接把 v.Int() 转成了 string,很明显是错误的,在 go 中是不能把 int 直接转成 string 的,我们一般都是调用 strconv.Itoa() 转成十进制的数字。有兴趣的可以看一下 strconv.Itoa(),相当于把数字,一点一点拆成 string

其他类型的转换应该都是正常的,先判断类型能否转换,然后返回不同的转换函数

Elem

接下来看一下 Elem 方法,这个方法主要针对 ptr 和 interface

func (v Value) Elem() 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() // 害怕在类型转换的时候转成 eface,丢失了一些类型信息
			})(v.ptr))
		}
		x := unpackEface(eface)
		if x.flag != 0 {
			x.flag |= v.flag.ro()
		}
		// interface 实际的类型
		return x
	case Ptr:
		ptr := v.ptr
		// 指针的指针, ptr 需要重新取一下
		if v.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
		// 更改 flag
		fl := v.flag&flagRO | flagIndir | flagAddr
		fl |= flag(typ.Kind())
		return Value{typ, ptr, fl}
	}
	panic(&ValueError{"reflect.Value.Elem", v.kind()})
}

我们假如类型为 ptr 的逻辑:

  • 先转成 ptrType
  • 取 ptrType.elem 元素类型作为新 Value 的类型,我们知道 elem 指向的是元素静态类型
  • 然后 flag 位或上一些标志位

这里有一个 if 判断,if v.flag&flagIndir != 0 {ptr = *(*unsafe.Pointer)(ptr)} 我们没有说到。什么时候会进入到这个 if 逻辑里呢?

现在假如数据为指针的指针,然后调用 Elem 之后,flag 上就带上了 flagIndir 的标志位,我们发现只有 typ 和 flag 变了, ptr 没变,也就是说 ptr 现在还是指向指针的指针。假如现在把调用过 Elem 的 Value 再调用一次,这个时候就会进入到上面的 if 逻辑块里了。

if 逻辑块里做了什么呢?把 ptr 转成指针的指针,然后取值,就获取到了指针。也就是说现在 ptr 是一个普通的指向值的指针了。记住这块逻辑,在 Value 装箱成 interface 的时候,我们会介绍到。

假如类型为 Interface 的逻辑:

  • 转成空的 interface,假如有函数,需要转成一个带 M() 方法的空 interface,这里应该是害怕丢失掉类型信息(只能这么解释了)
  • 把空的 interface 开箱成 Value,这里的 Value 就变成动态类型了

Interface

Interface 主要返回 Value 的实际的值

func (v Value) Interface() (i interface{}) {
	return valueInterface(v, true)
}

func valueInterface(v Value, safe bool) interface{} {
	if v.flag == 0 {
		panic(&ValueError{"reflect.Value.Interface", Invalid})
	}
	if safe && v.flag&flagRO != 0 {
		panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
	}
	if v.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)
}

主要是调用 packEface 方法,把 Value 装箱成 Interface

func packEface(v Value) interface{} {
	t := v.typ
	var i interface{}
	e := (*emptyInterface)(unsafe.Pointer(&i))
	switch {
	// 间接内存
	case ifaceIndir(t):
		if v.flag&flagIndir == 0 {
			panic("bad indir")
		}
		ptr := v.ptr
		// 说明可寻址
		if v.flag&flagAddr != 0 {
			c := unsafe_New(t)
			typedmemmove(t, c, ptr)
			// 搞一个新的地址赋值
			ptr = c
		}
		// 不可寻址,说明就是个值的副本, 直接赋值就行
		e.word = ptr
	case v.flag&flagIndir != 0: 
		e.word = *(*unsafe.Pointer)(v.ptr)
	default:
		// Value is direct, and so is the interface. 就是个指针
		e.word = v.ptr
	}
	e.typ = t
	return i
}

假如是间接地址:

  • 可寻址,new 一个新指针,把 ptr 的值拷贝到新指针,然后赋值给 e.word。更改返回的 interface 不会影响到原值
  • 不可寻址,说明就是个值复本,直接赋值 e.word

直接地址,并且标志位里含有 flagIndir 间接标志:

  • 转成指针的指针,然后取值成指针,赋值给 e.word

这里有一个问题,直接地址的类型,比如 map。我们声明一个 map 的指针指针 A,然后对 A 做 B:=reflect.ValueOf(A).Elem().Elem() 操作,根据 Elem 代码,我们了解到现在的 B 的标志位里有 flagIndir 标志,并且 B 现在的 ptr 是指向 map 的指针,因为第二次 Elem 的时候,已经把指针的指针转成指针了。然后我们调用 B.Interface() ,这个时候我们发现:

  • B 是 map ,是直接类型
  • B的标志位里有 flagIndir 间接标志
  • 执行 e.word = *(*unsafe.Pointer)(v.ptr) 操作

但是 B.ptr 是指针,虽然 unsafe.Pointer 可以转成任意的指针,但是对指针按照指针的指针来操作,获取到的指针地址肯定是会变的。

但是!但是!这块代码可以正常执行!!也就是说这块代码最终指向了正确的 map 值

那我们就猜测一下,对于直接类型,指针是可以转成指针的指针的。但是下面的单测却 panic 了,没法证明这个逻辑:

	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) 

这是一块俺没法弄明白的逻辑!