Golang反射

Posted by 淦 Blog on January 30, 2023

使用方法

两种基本类型

1
2
func ValueOf(i interface{}) Value
func TypeOf(i interface{}) Type

这两个函数的参数都是空接口interface{},内部存储了即将被反射的变量。 可以将reflect.Value看作反射的值,reflect.Type看作反射的实际类型。其中,reflect.Type是一个接口,包含和类型有关的许多方法签名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
type Type interface {
	Align() int
	FieldAlign() int
	Method(int) Method
	MethodByName(string) (Method, bool)
	NumMethod() int
	Name() string
	PkgPath() string
	Size() uintptr
	String() string
	Kind() Kind
	Implements(u Type) bool
	AssignableTo(u Type) bool
	ConvertibleTo(u Type) bool
	Comparable() bool
	Bits() int
	ChanDir() ChanDir
	IsVariadic() bool
	Elem() Type
	Field(i int) StructField
	FieldByIndex(index []int) StructField
	FieldByName(name string) (StructField, bool)
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	In(i int) Type
	Key() Type
	Len() int
	NumField() int
	NumIn() int
	NumOut() int
	Out(i int) Type
	common() *rtype
	uncommon() *uncommonType
}

reflect.Value是一个结构体,其内部包含了很多方法。reflect.Value类型中的Type方法可以获取当前反射的类型。

1
func (v value) Type() Type

因此,reflect.Value可以转换为reflect.Type。reflect.Value与reflect.Type都具有Kind方法,可以获取标识类型的Kind,其底层是unit。Go语言中的内置类型都可以用唯一的整数进行标识。

转换为接口

reflect.Value中的Interface方法以空接口的形式返回reflect.Value中的值。如果要进一步获取空接口的真实值,可以通过接口的断言语法对接口进行转换。

1
2
3
4
5
var num float64 = 1.2345
pointer := reflect.ValueOf(&num)
value := reflect.ValueOf(num)
convertPointer := pointer.Interface().(*float64)
convertValue := value.Interface().(float64)

除了使用接口进行转换,reflect.Value还提供了一些转换到具体类型的方法,这些特殊的方法可以加快转换的速度。另外,这些方法经过了特殊的处理,因此不管反射内部类型是int8、int16,还是int32,通过Int方法后都将转换为int64。

1
2
3
func (v Value) String() string
func (v Value) Int() int64
func (v Value) Float() float64

这些方法在使用时要注意,如果要转换的类型与实际类型不相符,则会在运行时报错。

Elem()间接访问

reflect.Value提供了Elem方法返回指针或接口指向的数据。

1
func (v Value) Elem() Value

如果Value存储的不是指针或接口,则使用Elem方法时会出错。

1
2
3
4
5
6
7
8
9
10
var z = 123
var y = &z
var x interface{} = y
v := reflect.ValueOf(&x)
vx := v.Elem()
fmt.Println(vx.Kind())  // interface{}
vy := vx.Elem()
fmt.Println(vy.Kind())   // ptr
vz := vy.Elem()
fmt.Println(vz.Kind(), vz.Int()) // int 123

reflect.Type类型仍然有Elem方法,但是该方法只用于获取类型。该方法不仅仅可以返回指针和接口指向的类型,还可以返回数组、通道、切片、指针、哈希表存储的类型。

1
2
3
4
5
6
7
8
9
10
type A = [16]int64
var c <-chan map[A][]byte
tc := reflect.TypeOf(c)
fmt.Println(tc.Kind())    // chan
fmt.Println(tc.ChanDir()) // <-chan
tm := tc.Elem()
ta, tb := tm.Key(), tm.Elem()
fmt.Println(tm.Kind(), ta.Kind(), tb.Kind()) // map array slice
tx, ty := ta.Elem(), tb.Elem()
fmt.Println(tx.Kind(), ty.Kind()) // int64 uint8

修改反射的值

要求反射中的类型必须是指针。

1
func (v Value) Set(x Value)

只有当反射中存储的实际值是指针时才能赋值,否则是没有意义的,因为在反射之前,实际值被转换为了空接口,如果空接口中存储的值是一个副本,那么修改它会引起混淆,因此Go语言禁止这样做。这和禁止用值类型去调用指针接收者的方法的原理是一样的。为了避免这种错误,reflect.value提供了CanSet方法用于获取当前的反射值是否可以赋值。

1
2
3
4
5
6
7
var num float64 = 1.2345
pointe := reflect.ValueOf(&num)
newValue := pointe.Elem()
if newValue.CanSet() {
	newValue.SetFloat(1.6789)
}
fmt.Println(num)

结构体与反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) ReflectCallFunc() {
	fmt.Println("jonson ReflectCallFunc")
}

func main() {
	user := User{1, "jonson", 25}
	getType := reflect.TypeOf(user)
	fmt.Println(getType.Name()) // User
	getValue := reflect.ValueOf(user)
	fmt.Println(getValue) // {1, "jonson", 25}
}

遍历结构体字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) ReflectCallFunc() {
	fmt.Println("jonson ReflectCallFunc")
}

func main() {
	user := User{1, "jonson", 25}
	getType := reflect.TypeOf(user)
	getValue := reflect.ValueOf(user)
	for i := 0; i < getType.NumField(); i++ {
		field := getType.Field(i)
		value := getValue.Field(i)
		fmt.Printf("%s: %v = %v \n", field.Name, field.Type, value.Interface())
	}

	/*
			Id: int = 1
		    Name: string = jonson
		    Age: int = 25
	*/
}

通过reflect.Type类型的NumField函数获取结构体中字段的个数。reflect.Type与reflect.Value都有Field方法,reflect.Type的Field方法主要用于获取结构体的元信息,其返回StructField结构,该结构包含字段名、所在包名、Tag名等基础信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// A StructField describes a single field in a struct.
type StructField struct {
	// Name is the field name.
	Name string

	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

reflect.Value的Field方法主要返回结构体字段的值类型。

修改结构体字段

1
2
3
4
5
6
7
8
9
10
var s struct {
	X int
	y float64
}

vs := reflect.ValueOf(&s).Elem()
vx := vs.Field(0)
vb := reflect.ValueOf(123)
vx.Set(vb)
fmt.Println(s.X) //123

需要先通过Elem方法获取指针指向的结构体值类型,才能调用field 方法。注意,未导出的字段y是不能被赋值的。

嵌套结构体的赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type S struct {
	s int
}

type children struct {
	Age int
}

type Nested struct {
	X     int
	Child children
	S
}

func main() {
	var nested Nested
	vs := reflect.ValueOf(&nested).Elem()
	vz := vs.Field(1)
	vz.Set(reflect.ValueOf(children{Age: 19}))
	fmt.Println(nested) // {0 {19} {0}}
	vz = vs.Field(2)
	vz.Set(reflect.ValueOf(S{s: 20}))
	fmt.Println(nested) // {0 {19} {20}}
}

注:

  • Child字段对应的children结构体的所有字段都是可导出的。
  • 嵌入S结构体必须是可导出的

结构体方法与动态调用

要获取任意类型对应的方法,可以使用reflect.Type提供的Method方法,Method方法需要传递方法的 index序号。

1
func (t Type) Method(int) Method

如果 index 序号超出了范围,则会在运行时报错。该方法在大部分时候用于遍历反射结构体的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) ReflectCallFunc() {
	fmt.Println("jonson ReflectCallFunc")
}

func main() {
	user := User{1, "jonson", 25}
	getType := reflect.TypeOf(user)
	for i := 0; i < getType.NumMethod(); i++ {
		m := getType.Method(i)
		fmt.Printf("%s: %v\n", m.Name, m.Type) // ReflectCallFunc: func(main.User)
	}
}

在实践中,更多时候使用reflect.Value的MethodByName方法,参数为方法名并返回代表该方法的reflect.Value对象。如果该方法不存在,则会返回空。

通过 Type方法将reflect.Value转换为reflect.Type,reflect.Type接口中有一系列方法可以获取函数的参数个数、返回值个数、方法个数等属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) RefCallAgrs(age int, name string) error {
	return nil
}

func main() {
	user := User{1, "jonson", 25}
	ref := reflect.ValueOf(user)
	tf := ref.MethodByName("RefCallAgrs").Type()
	fmt.Println(tf.NumIn(), tf.NumOut(), ref.NumMethod()) // 2 1 1
}

获取代表方法的reflect.Value对象后,可以通过call方法在运行时调用方法。

1
func (v Value) Call(in []value) []Value

Call方法的参数为实际方法中传入参数的reflect.Value切片。因此,对于无参数的调用,参数需要构造一个长度为0的切片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) ReflectCallFunc() {
	fmt.Println("jonson ReflectCallFunc")
}

func main() {
	user := User{1, "jonson", 25}
	methodValue := reflect.ValueOf(user).MethodByName("ReflectCallFunc")
	args := make([]reflect.Value, 0)
	methodValue.Call(args)
}

对于有参数的调用,需要先构造出reflect.Value类型的参数切片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) RefCallAgrs(age int, name string) {
	fmt.Println(age, name)
}

func main() {
	user := User{1, "jonson", 25}
	methodValue := reflect.ValueOf(user).MethodByName("RefCallAgrs")
	args := []reflect.Value{reflect.ValueOf(18), reflect.ValueOf("json")}
	methodValue.Call(args)
}

如果参数是一个指针类型,那么只需要构造指针类型的reflect.Value即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) RefCallPoint(age int, name *string) {
	fmt.Println(age, *name)
}

func main() {
	user := User{1, "jonson", 25}
	methodValue := reflect.ValueOf(user).MethodByName("RefCallPoint")
	name := "json"
	args := []reflect.Value{reflect.ValueOf(18), reflect.ValueOf(&name)}
	methodValue.Call(args)
}

和接口一样,如果方法是指针接收者,那么反射动态调用者的类型也必须是指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type User struct {
	Id   int
	Name string
	Age  int
}

func (u *User) RefPointMethod() {
	fmt.Println("hello world")
}

func main() {
	user := User{1, "jonson", 25}
	methodValue := reflect.ValueOf(&user).MethodByName("RefPointMethod")
	args := make([]reflect.Value, 0)
	methodValue.Call(args)
}

对于方法有返回值的情况,call方法会返回reflect.Value切片。获取返回值的反射类型后,通过将返回值转换为空接口即可进行下一步操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type User struct {
	Id   int
	Name string
	Age  int
}

func (u *User) PointMethodReturn(name string, age int) (string, int) {
	return name, age
}

func main() {
	var user User
	methodValue := reflect.ValueOf(&user).MethodByName("PointMethodReturn")
	args := []reflect.Value{reflect.ValueOf("jonson"), reflect.ValueOf(30)}
	res := methodValue.Call(args)
	fmt.Println(res[0].String()) // jonson
	fmt.Println(res[1].Int())    // 30
}

在运行时创建结构体

除了使用reflect.TypeOf 函数数生成成已知类型的反射类型,还可以使用reflect标准库中的ArrayOf、SliceOf等函数生成一些在编译时完全不存在的类型或对象。对于结构体,需要使用reflect.StructOf函数在运行时生成特定的结构体对象。

1
func StructOf(fields []StructField) Type

reflect.StructOf函数参数是StructField的切片,StructField代表结构体中的字段。其中,Name代表该字段名,Type代表该字段的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// A StructField describes a single field in a struct.
type StructField struct {
	// Name is the field name.
	Name string

	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

该函数可变参数中的类型依次构建为结构体的字段,并返回结构体变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func MakeStruct(vals ...interface{}) reflect.Value {
	var sfs []reflect.StructField
	for k, v := range vals {
		t := reflect.TypeOf(v)
		sf := reflect.StructField{
			Name: fmt.Sprintf("F%d", (k + 1)),
			Type: t,
		}
		sfs = append(sfs, sf)
	}
	st := reflect.StructOf(sfs)
	so := reflect.New(st)
	return so
}

func main() {
	sr := MakeStruct(0, "", []int{})
	sr.Elem().Field(0).SetInt(20)
	sr.Elem().Field(1).SetString("reflect me")
	v := []int{1, 2, 3}
	sr.Elem().Field(2).Set(reflect.ValueOf(v))
	fmt.Println(sr) // &{20 reflect me [1 2 3]}
}

函数与反射

实现函数的动态调用,和方法的调用是相同的,同样使用了reflect.Call。

1
2
3
4
5
6
7
8
9
10
11
func Handler2(args int, reply *int) {
	fmt.Println(args, *reply)  // 1 2
}

func main() {
	v2 := reflect.ValueOf(Handler2)
	args := reflect.ValueOf(1)
	reply := 2
	replyv := reflect.ValueOf(&reply)
	v2.Call([]reflect.Value{args, replyv})
}

反射与其他类型

对于其他的一些类型,可以通过XXXof方法构造特定的reflect.Type类型。

1
2
3
4
5
6
7
ta := reflect.ArrayOf(5, reflect.TypeOf(123)) // [5]int
tc := reflect.ChanOf(reflect.SendDir, ta) // chan<- [5]int
tp := reflect.PtrTo(ta) // *[5]int
ts := reflect.SliceOf(tp)  // []*[5]int
tm := reflect.MapOf(ta, tc)  // map[[5]int]chan<- [5]int
tf := reflect.FuncOf([]reflect.Type{ta}, []reflect.Type{tp, tc}, false) // func([5]int) (*[5]int, chan<- [5]int)
tt := reflect.StructOf([]reflect.StructField) // struct {Age string}

根据reflect.Type生成对应的reflect.Value,Reflect 包中提供了对应类型的makeXXX方法。

1
2
3
4
5
func MakeChan(typ Type, buffer int) Value
func MakeSlice(typ Type, len, cap int) Value
func MakeMap(typ Type) Value
func MakeMapWithSize(typ Type, n int) Value
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value

底层原理

reflect.Type和reflect.Value详解

将传递进来的接口变量转换为底层的实际空接口emptyInterface,并获取空接口的类型值。reflect.Type实质上是空接口结构体中的typ字段,其是rtype类型,Go语言中任何具体类型的底层结构都包含这一类型。

1
2
3
4
5
6
7
8
9
10
11
12
// 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 {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}

reflect. ValueOf函数的核心是调用了unpackEface函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// unpackEface converts the empty interface i to a Value.
func unpackEface(i any) Value {
	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}
}

reflect.Value包含了接口中存储的值及类型,除此之外还包含了特殊的flag标志,flag 标记以位图的形式存储了反射类型的元数据。

Interface方法原理

reflect.Value存储了空接口的类型和值,将reflect.Value转换为空接口调用了packEface函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// packEface converts v to the empty interface.
func packEface(v Value) any {
	t := v.typ
	var i any
	e := (*emptyInterface)(unsafe.Pointer(&i))
	// First, fill in the data portion of the interface.
	switch {
	case ifaceIndir(t):
		if v.flag&flagIndir == 0 {
			panic("bad indir")
		}
		// Value is indirect, and so is the interface we're making.
		ptr := v.ptr
		if v.flag&flagAddr != 0 {
			// TODO: pass safe boolean from valueInterface so
			// we don't need to copy if safe==true?
			c := unsafe_New(t)
			typedmemmove(t, c, ptr)
			ptr = c
		}
		e.word = ptr
	case v.flag&flagIndir != 0:
		// Value is indirect, but interface is direct. We need
		// to load the data at v.ptr into the interface data word.
		e.word = *(*unsafe.Pointer)(v.ptr)
	default:
		// Value is direct, and so is the interface.
		e.word = v.ptr
	}
	// Now, fill in the type portion. We're very careful here not
	// to have any operation between the e.word and e.typ assignments
	// that would let the garbage collector observe the partially-built
	// interface value.
	e.typ = t
	return i
}
  • e := (*emptyInterface)(unsafe.Pointer(&i))构建了一个空接口,e.typ=t将reflect.Value中的类型赋值给空接口中的类型。但是对于接口中的值e.word的处理仍然有所区别,原因和之前介绍的一样,有些值是间接获得的。
  • 返回的类型是一个新的副本,这样不会对原始的切片造成任何干扰。当出现这种情况时,case ifaceIndir(t)为true,会生成一个新的值。
  • 存储在内部的实际指向数据的指针e.word = (unsafe.Pointer)(v.ptr)

反射类型转换为内置类型原理

根据值的类型和值的指针得到实际值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Int returns v's underlying value, as an int64.
// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64.
func (v Value) Int() int64 {
	k := v.kind()
	p := v.ptr
	switch k {
	case Int:
		return int64(*(*int)(p))
	case Int8:
		return int64(*(*int8)(p))
	case Int16:
		return int64(*(*int16)(p))
	case Int32:
		return int64(*(*int32)(p))
	case Int64:
		return *(*int64)(p)
	}
	panic(&ValueError{"reflect.Value.Int", v.kind()})
}

Elem方法释疑

对于指针来说,如果flag标识了reflect.Value是间接的,则会返回数据真实的地址(*unsafe.Pointer)(ptr),而对于直接的指针,则退回本身即可,并且会将flag修改为flagAddr,即可赋值的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Elem returns the value that the interface v contains
// or that the pointer v points to.
// It panics if v's Kind is not Interface or Pointer.
// It returns the zero Value if v is nil.
func (v Value) Elem() Value {
	k := v.kind()
	switch k {
	case Interface:
		var eface any
		if v.typ.NumMethod() == 0 {
			eface = *(*any)(v.ptr)
		} else {
			eface = (any)(*(*interface {
				M()
			})(v.ptr))
		}
		x := unpackEface(eface)
		if x.flag != 0 {
			x.flag |= v.flag.ro()
		}
		return x
	case Pointer:
		ptr := v.ptr
		if v.flag&flagIndir != 0 {
			if ifaceIndir(v.typ) {
				// This is a pointer to a not-in-heap object. ptr points to a uintptr
				// in the heap. That uintptr is the address of a not-in-heap object.
				// In general, pointers to not-in-heap objects can be total junk.
				// But Elem() is asking to dereference it, so the user has asserted
				// that at least it is a valid pointer (not just an integer stored in
				// a pointer slot). So let's check, to make sure that it isn't a pointer
				// that the runtime will crash on if it sees it during GC or write barriers.
				// Since it is a not-in-heap pointer, all pointers to the heap are
				// forbidden! That makes the test pretty easy.
				// See issue 48399.
				if !verifyNotInHeapPtr(*(*uintptr)(ptr)) {
					panic("reflect: reflect.Value.Elem on an invalid notinheap pointer")
				}
			}
			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
		fl := v.flag&flagRO | flagIndir | flagAddr
		fl |= flag(typ.Kind())
		return Value{typ, ptr, fl}
	}
	panic(&ValueError{"reflect.Value.Elem", v.kind()})
}

动态调用剖析

反射提供的核心动能是动态的调用方法或函数,这在RPC远程过程调用中使用频繁。

MethodByName方法可以根据方法名找到化表方法的reflect.Value对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// MethodByName returns a function value corresponding to the method
// of v with the given name.
// The arguments to a Call on the returned function should not include
// a receiver; the returned function will always use v as the receiver.
// It returns the zero Value if no method was found.
func (v Value) MethodByName(name string) Value {
	if v.typ == nil {
		panic(&ValueError{"reflect.Value.MethodByName", Invalid})
	}
	if v.flag&flagMethod != 0 {
		return Value{}
	}
	m, ok := v.typ.MethodByName(name)
	if !ok {
		return Value{}
	}
	return v.Method(m.Index)
}

核心调用了类型typ字段的MethodByName方法,用于找到当前方法名的index序号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func (t *rtype) MethodByName(name string) (m Method, ok bool) {
	if t.Kind() == Interface {
		tt := (*interfaceType)(unsafe.Pointer(t))
		return tt.MethodByName(name)
	}
	ut := t.uncommon()
	if ut == nil {
		return Method{}, false
	}
	// TODO(mdempsky): Binary search.
	for i, p := range ut.exportedMethods() {
		if t.nameOff(p.name).name() == name {
			return t.Method(i), true
		}
	}
	return Method{}, false
}

t.uncommon方法根据类型还原出特定的类型,接着调用Value.Method方法。值得注意的是,该方法返回的Value仍然是方法的接收者,只是flag设置了flagMethod,并且在flag 中标识了当前method的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Method returns a function value corresponding to v's i'th method.
// The arguments to a Call on the returned function should not include
// a receiver; the returned function will always use v as the receiver.
// Method panics if i is out of range or if v is a nil interface value.
func (v Value) Method(i int) Value {
	if v.typ == nil {
		panic(&ValueError{"reflect.Value.Method", Invalid})
	}
	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")
	}
	fl := v.flag.ro() | (v.flag & flagIndir)
	fl |= flag(Func)
	fl |= flag(i)<<flagMethodShift | flagMethod
	return Value{v.typ, v.ptr, fl}
}

动态调用的核心方法是Call方法,其参数为reflect.Value数组,返回的也是reflect.Value数组。

1
func (v Value) Call(in []Value) []Value

Call方法的第1步是获取函数的指针,对于方法的调用要略微复杂一些,会调用methodReceiver方法获取调用者的实际类型、函数类型,以及函数指针的位置。

1
2
3
4
5
6
7
8
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
}

第2步是进行有效性验证,例如函数的输入大小和个数是否与传入的参数匹配,传入的参数能否赋值给函数参数等。

第3步是调用funcLayout函数,用于构建函数参数及返回值的栈帧布局,其中frametype代表调用时需要的内存大小,用于内存分配。retOffset 用于标识函数参数及返回值在内存中的位置。

1
frametype, framePool, abi := funcLayout(t, rcvrtype)

framePool是一个内存缓存池, 用于在没有返回值的场景中复用内存。但是如果函数中有返回值,则不能复用内存,这是为了防止发生内存泄漏。

1
2
3
4
5
6
7
8
9
10
11
// Allocate a chunk of memory for frame if needed.
var stackArgs unsafe.Pointer
if frametype.size != 0 {
	if nout == 0 {
		stackArgs = framePool.Get().(unsafe.Pointer)
	} else {
		// Can't use pool if the function has return values.
		// We will leak pointer to args in ret, so its lifetime is not scoped.
		stackArgs = unsafe_New(frametype)
	}
}

如果是方法调用,那么栈中的第一个参数是接收者的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Handle receiver.
inStart := 0
if rcvrtype != nil {
	// Guaranteed to only be one word in size,
	// so it will only take up exactly 1 abiStep (either
	// in a register or on the stack).
	switch st := abi.call.steps[0]; st.kind {
	case abiStepStack:
		storeRcvr(rcvr, stackArgs)
	case abiStepIntReg, abiStepPointer:
		// Even pointers can go into the uintptr slot because
		// they'll be kept alive by the Values referenced by
		// this frame. Reflection forces these to be heap-allocated,
		// so we don't need to worry about stack copying.
		storeRcvr(rcvr, unsafe.Pointer(&regArgs.Ints[st.ireg]))
	case abiStepFloatReg:
		storeRcvr(rcvr, unsafe.Pointer(&regArgs.Floats[st.freg]))
	default:
		panic("unknown ABI parameter kind")
	}
	inStart = 1
}

然后将输入参数放入栈中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Handle arguments.
for i, v := range in {
	v.mustBeExported()
	targ := t.In(i).(*rtype)
	// TODO(mknyszek): Figure out if it's possible to get some
	// scratch space for this assignment check. Previously, it
	// was possible to use space in the argument frame.
	v = v.assignTo("reflect.Value.Call", targ, nil)
stepsLoop:
	for _, st := range abi.call.stepsForValue(i + inStart) {
		switch st.kind {
		case abiStepStack:
			// Copy values to the "stack."
			addr := add(stackArgs, st.stkOff, "precomputed stack arg offset")
			if v.flag&flagIndir != 0 {
				typedmemmove(targ, addr, v.ptr)
			} else {
				*(*unsafe.Pointer)(addr) = v.ptr
			}
			// There's only one step for a stack-allocated value.
			break stepsLoop
		case abiStepIntReg, abiStepPointer:
			// Copy values to "integer registers."
			if v.flag&flagIndir != 0 {
				offset := add(v.ptr, st.offset, "precomputed value offset")
				if st.kind == abiStepPointer {
					// Duplicate this pointer in the pointer area of the
					// register space. Otherwise, there's the potential for
					// this to be the last reference to v.ptr.
					regArgs.Ptrs[st.ireg] = *(*unsafe.Pointer)(offset)
				}
				intToReg(&regArgs, st.ireg, st.size, offset)
			} else {
				if st.kind == abiStepPointer {
					// See the comment in abiStepPointer case above.
					regArgs.Ptrs[st.ireg] = v.ptr
				}
				regArgs.Ints[st.ireg] = uintptr(v.ptr)
			}
		case abiStepFloatReg:
			// Copy values to "float registers."
			if v.flag&flagIndir == 0 {
				panic("attempted to copy pointer to FP register")
			}
			offset := add(v.ptr, st.offset, "precomputed value offset")
			floatToReg(&regArgs, st.freg, st.size, offset)
		default:
			panic("unknown ABI part kind")
		}
	}
}

完成调用后,如果函数没有返回,则将args内部全部清空为0,并再次放入framePool 中。

如果有返回值,则清空args中输入参数部分,并将输出包装为ret切片后返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
var ret []Value
if nout == 0 {
	if stackArgs != nil {
		typedmemclr(frametype, stackArgs)
		framePool.Put(stackArgs)
	}
} else {
	if stackArgs != nil {
		// Zero the now unused input area of args,
		// because the Values returned by this function contain pointers to the args object,
		// and will thus keep the args object alive indefinitely.
		typedmemclrpartial(frametype, stackArgs, 0, abi.retOffset)
	}

	// Wrap Values around return values in args.
	ret = make([]Value, nout)
	for i := 0; i < nout; i++ {
		tv := t.Out(i)
		if tv.Size() == 0 {
			// For zero-sized return value, args+off may point to the next object.
			// In this case, return the zero value instead.
			ret[i] = Zero(tv)
			continue
		}
		steps := abi.ret.stepsForValue(i)
		if st := steps[0]; st.kind == abiStepStack {
			// This value is on the stack. If part of a value is stack
			// allocated, the entire value is according to the ABI. So
			// just make an indirection into the allocated frame.
			fl := flagIndir | flag(tv.Kind())
			ret[i] = Value{tv.common(), add(stackArgs, st.stkOff, "tv.Size() != 0"), fl}
			// Note: this does introduce false sharing between results -
			// if any result is live, they are all live.
			// (And the space for the args is live as well, but as we've
			// cleared that space it isn't as big a deal.)
			continue
		}

		// Handle pointers passed in registers.
		if !ifaceIndir(tv.common()) {
			// Pointer-valued data gets put directly
			// into v.ptr.
			if steps[0].kind != abiStepPointer {
				print("kind=", steps[0].kind, ", type=", tv.String(), "\n")
				panic("mismatch between ABI description and types")
			}
			ret[i] = Value{tv.common(), regArgs.Ptrs[steps[0].ireg], flag(tv.Kind())}
			continue
		}

		// All that's left is values passed in registers that we need to
		// create space for and copy values back into.
		//
		// TODO(mknyszek): We make a new allocation for each register-allocated
		// value, but previously we could always point into the heap-allocated
		// stack frame. This is a regression that could be fixed by adding
		// additional space to the allocated stack frame and storing the
		// register-allocated return values into the allocated stack frame and
		// referring there in the resulting Value.
		s := unsafe_New(tv.common())
		for _, st := range steps {
			switch st.kind {
			case abiStepIntReg:
				offset := add(s, st.offset, "precomputed value offset")
				intFromReg(&regArgs, st.ireg, st.size, offset)
			case abiStepPointer:
				s := add(s, st.offset, "precomputed value offset")
				*((*unsafe.Pointer)(s)) = regArgs.Ptrs[st.ireg]
			case abiStepFloatReg:
				offset := add(s, st.offset, "precomputed value offset")
				floatFromReg(&regArgs, st.freg, st.size, offset)
			case abiStepStack:
				panic("register-based return value has stack component")
			default:
				panic("unknown ABI part kind")
			}
		}
		ret[i] = Value{tv.common(), s, flagIndir | flag(tv.Kind())}
	}
}