forked from bytedance/mockey
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.go
134 lines (122 loc) · 3.81 KB
/
utils.go
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
* Copyright 2022 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mockey
import (
"reflect"
"unsafe"
"github.com/bytedance/mockey/internal/tool"
)
// GetMethod resolve a certain public method from an instance.
func GetMethod(instance interface{}, methodName string) interface{} {
if typ := reflect.TypeOf(instance); typ != nil {
if m, ok := getNestedMethod(reflect.ValueOf(instance), methodName); ok {
return m.Func.Interface()
}
if m, ok := typ.MethodByName(methodName); ok {
return m.Func.Interface()
}
if m, ok := getFieldMethod(instance, methodName); ok {
return m
}
}
tool.Assert(false, "can't reflect instance method :%v", methodName)
return nil
}
// getFieldMethod gets a functional field's value as an instance
// The return instance is not original field but a new function object points to
// the same function.
// for example:
//
// type Fn func()
// type Foo struct {
// privateField Fn
// }
// func NewFoo() Foo { return Foo{ privateField: func() { /*do nothing*/ } }}
//
// getFieldMethod(NewFoo(),"privateField") will return a function object which
// points to the anonymous function in NewFoo
func getFieldMethod(instance interface{}, fieldName string) (interface{}, bool) {
v := reflect.Indirect(reflect.ValueOf(instance))
if v.Kind() != reflect.Struct {
return nil, false
}
field := v.FieldByName(fieldName)
if !field.IsValid() || field.Kind() != reflect.Func {
return nil, false
}
carrier := reflect.MakeFunc(field.Type(), nil)
type function struct {
_ uintptr
fnAddr *uintptr
}
*(*function)(unsafe.Pointer(&carrier)).fnAddr = field.Pointer()
return carrier.Interface(), true
}
// GetPrivateMethod ...
// Deprecated, use GetMethod instead.
func GetPrivateMethod(instance interface{}, methodName string) interface{} {
m, ok := reflect.TypeOf(instance).MethodByName(methodName)
if ok {
return m.Func.Interface()
}
tool.Assert(false, "can't reflect instance method :%v", methodName)
return nil
}
// GetNestedMethod resolves a certain public method in anonymous structs, it will
// look for the specific method in every anonymous struct field recursively.
// Deprecated, use GetMethod instead.
func GetNestedMethod(instance interface{}, methodName string) interface{} {
if typ := reflect.TypeOf(instance); typ != nil {
if m, ok := getNestedMethod(reflect.ValueOf(instance), methodName); ok {
return m.Func.Interface()
}
}
tool.Assert(false, "can't reflect instance method :%v", methodName)
return nil
}
func getNestedMethod(val reflect.Value, methodName string) (reflect.Method, bool) {
typ := val.Type()
kind := typ.Kind()
if kind == reflect.Ptr || kind == reflect.Interface {
val = val.Elem()
}
if !val.IsValid() {
return reflect.Method{}, false
}
typ = val.Type()
kind = typ.Kind()
if kind == reflect.Struct {
for i := 0; i < typ.NumField(); i++ {
if !typ.Field(i).Anonymous {
// there is no need to acquire non-anonymous method
continue
}
if m, ok := getNestedMethod(val.Field(i), methodName); ok {
return m, true
}
}
}
// a struct receiver is prior to the corresponding pointer receiver
if m, ok := typ.MethodByName(methodName); ok {
return m, true
}
return reflect.PtrTo(typ).MethodByName(methodName)
}
// GetGoroutineId ...
// Deprecated
func GetGoroutineId() int64 {
return tool.GetGoroutineID()
}