github.com/tencent/goom@v1.0.1/internal/iface/make_interface.go (about) 1 package iface 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/tencent/goom/internal/bytecode/stub" 8 "github.com/tencent/goom/internal/hack" 9 ) 10 11 // IContext 接口 Mock 代码函数的接收体 12 // 避免被 mock 的接口变量为 nil, 无法通过单测逻辑中 mock==nil 的判断 13 type IContext struct { 14 // Data 可以传递任意数据 15 Data interface{} 16 // 代理上下文数据 17 p *PContext 18 } 19 20 // Cancel 取消接口代理 21 func (c *IContext) Cancel() { 22 *c.p.originIface = *c.p.originIfaceValue 23 c.p.canceled = true 24 } 25 26 // Canceled 是否已经被取消 27 func (c *IContext) Canceled() bool { 28 return c.p.canceled 29 } 30 31 // Cached 获取缓存数据 32 func (c *IContext) Cached(key string) (v *hack.Iface, ok bool) { 33 v, ok = c.p.ifaceCache[key] 34 return 35 } 36 37 // Cache 缓存数据 38 func (c *IContext) Cache(key string, value *hack.Iface) { 39 c.p.ifaceCache[key] = value 40 } 41 42 // NewContext 构造上下文 43 func NewContext() *IContext { 44 return &IContext{ 45 Data: nil, 46 p: &PContext{ 47 ifaceCache: make(map[string]*hack.Iface, 32), 48 }, 49 } 50 } 51 52 // PContext 代理上下文 53 // 适配 proxy 包的 Context 54 type PContext struct { 55 // ifaceCache iface 缓存 56 ifaceCache map[string]*hack.Iface 57 // originIface 原始接口地址 58 originIface *hack.Iface 59 // originIfaceValue 原始接口值 60 originIfaceValue *hack.Iface 61 // proxyFunc 代理函数, 需要内存持续持有 62 proxyFunc reflect.Value 63 // canceled 是否已经被取消 64 canceled bool 65 } 66 67 // PFunc 代理函数类型的签名 68 type PFunc func(args []reflect.Value) (results []reflect.Value) 69 70 // notImplement 未实现的接口方法被调用的函数, 未配置 mock 的接口方法默认会跳转到调用此函数 71 func notImplement() { 72 panic("method not implements. (please write a mocker on it)") 73 } 74 75 // MakeInterface 构造 interface 对象, 包含 receive、funcTab 等数据 76 func MakeInterface(ctx *IContext, funcTabIndex int, itabFunc uintptr, typ reflect.Type) *hack.Iface { 77 funcTabData := [hack.MaxMethod]uintptr{} 78 notImplements := reflect.ValueOf(notImplement).Pointer() 79 for i := 0; i < hack.MaxMethod; i++ { 80 funcTabData[i] = notImplements 81 } 82 funcTabData[funcTabIndex] = itabFunc 83 84 // 伪造 iface 85 structType := reflect.TypeOf(&IContext{}) 86 return &hack.Iface{ 87 Tab: &hack.Itab{ 88 Inter: (*uintptr)((*hack.Iface)(unsafe.Pointer(&typ)).Data), 89 Type: (*uintptr)((*hack.Iface)(unsafe.Pointer(&structType)).Data), 90 Fun: funcTabData, 91 }, 92 Data: unsafe.Pointer(ctx), 93 } 94 } 95 96 // BackUpTo 备份缓存 iface 指针到 IContext 中 97 func BackUpTo(ctx *IContext, iface unsafe.Pointer) { 98 if ctx.p.originIfaceValue == nil { 99 ctx.p.originIface = (*hack.Iface)(iface) 100 originIfaceValue := *(*hack.Iface)(iface) 101 ctx.p.originIfaceValue = &originIfaceValue 102 } 103 } 104 105 // GenCallableMethod 生成可以直接 CALL 的接口方法实现, 带上下文 (rdx) 106 func GenCallableMethod(ctx *IContext, apply interface{}, proxy PFunc) uintptr { 107 var ( 108 methodCaller uintptr 109 err error 110 ) 111 112 if proxy == nil { 113 // 生成桩代码,rdx 寄存器还原 114 applyValue := reflect.ValueOf(apply) 115 mockFuncPtr := (*hack.Value)(unsafe.Pointer(&applyValue)).Ptr 116 methodCaller, err = MakeMethodCaller(mockFuncPtr) 117 } else { 118 // 生成桩代码,rdx 寄存器还原, 生成的调用将跳转到 proxy 函数 119 methodTyp := reflect.TypeOf(apply) 120 mockFunc := reflect.MakeFunc(methodTyp, proxy) 121 callStub := reflect.ValueOf(stub.MakeFuncStub).Pointer() 122 mockFuncPtr := (*hack.Value)(unsafe.Pointer(&mockFunc)).Ptr 123 methodCaller, err = MakeMethodCallerWithCtx(mockFuncPtr, callStub) 124 ctx.p.proxyFunc = mockFunc 125 } 126 127 if err != nil { 128 panic(err) 129 } 130 return methodCaller 131 }