gostub源码分析
Posted golang算法架构leetcode技术php
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gostub源码分析相关的知识,希望对你有一定的参考价值。
GoMock非常优秀,但是对于普通的函数打桩来说也有一些缺点:
必须引入额外的抽象(interface)
打桩过程比较重
既有代码必须适配新增的抽象
那么有没有轻量级的打桩方法呢?答案是gostub
全局变量可通过GoStub框架打桩
过程可通过GoStub框架打桩
函数可通过GoStub框架打桩
1,gostub 的用法
变量打桩
var counter = 100
stubs := gostub.Stub(&counter, 200)
函数打桩
func Exec(cmd string, args ...string) (string, error) {}
这种函数没法直接用gostub的,Golang支持闭包,这使得函数可以作为另一个函数的参数或返回值,而且可以赋值给一个变量。
需要改成
var Exec = func(cmd string, args ...string) (string, error) {}
现在我们可以对Exec函数打桩了,代码如下所示:
stubs := Stub(&Exec, func(cmd string, args ...string) (string, error) {
return "xxx-vethName100-yyy", nil
})
defer stubs.Reset()
其实GoStub框架专门提供了StubFunc函数用于函数打桩
stubs := StubFunc(&Exec,"xxx-vethName100-yyy", nil)
defer stubs.Reset()
对于Golang的库函数或第三方的库函数,可以定义库函数的变量作为适配层:
package adaptervar
Stat = os.Statvar
2,gostub的源码分析
gostub 源码很简单,只有gostub.go一个源文件
核心原理就是利用反射进行值的替换
以函数打桩的接口函数为例:
func StubFunc(funcVarToStub interface{}, stubVal ...interface{}) *Stubs {
return New().StubFunc(funcVarToStub, stubVal...)
}
其实底层和变量替换的实现是差不多的。
核心的数据如下,存储了最初的原始变量,便于在stub结束后数据的恢复,用stubs 变量存储了被替换的变量和替换后变量的映射。
type Stubs struct {
// stubs is a map from the variable pointer (being stubbed) to the original value.
stubs map[reflect.Value]reflect.Value
origEnv map[string]envVal
}
核心的stub函数如下:
1,先通过反射获取被替换变量的值,和即将替代的值
2,把原始的值用刚刚讲的map存起来
3,修改被替换变量的值
4,用到了反射的核心函数有
reflect.ValueOf //获取interface的值
v.Elem().Interface()//获取interface 包含的值的接口
v.Elem().Set(stub) //修改interface 包含的值
func (s *Stubs) Stub(varToStub interface{}, stubVal interface{}) *Stubs {
v := reflect.ValueOf(varToStub)
stub := reflect.ValueOf(stubVal)
if _, ok := s.stubs[v]; !ok {
// Store the original value if this is the first time varPtr is being stubbed.
s.stubs[v] = reflect.ValueOf(v.Elem().Interface())
}
// *varToStub = stubVal
v.Elem().Set(stub)
return s
}
恢复最初定义变量或者函数
func (s *Stubs) Reset() {
for v, originalVal := range s.stubs {
v.Elem().Set(originalVal)
}
}
对于函数替换过程稍微有点复杂,需要对函数的内容进行重新构造,具体代码如下
func FuncReturning(funcType reflect.Type, results ...interface{}) reflect.Value {
return reflect.MakeFunc(funcType, func(_ []reflect.Value) []reflect.Value {
return resultValues
})
}
构造函数的过程用到了反射的MakeFunc方法
reflect.MakeFunc //用给定函数来构造出funcType类型的函数,底层和c函数的转换类似,就是函数指针的转换。
以上是关于gostub源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段