暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

golang源码分析:gomonkey(2)

        在介绍完如何使用和核心函数后,这里通过分析源码,看看它的实现原理。常见接口的定义位于:
github.com/agiledragon/gomonkey@v2.0.2+incompatible/patch.go
    func ApplyFunc(target, double interface{}) *Patches {
        return create().ApplyFunc(target, double)
    }
      func ApplyMethod(target reflect.Type, methodName stringdouble interface{}) *Patches {
          return create().ApplyMethod(target, methodName, double)
      }
        func ApplyGlobalVar(target, double interface{}) *Patches {
            return create().ApplyGlobalVar(target, double)
        }
          func ApplyFuncVar(target, double interface{}) *Patches {
              return create().ApplyFuncVar(target, double)
          }
                  首先通过create创建patch对象,然后应用对应的方法:
            func create() *Patches {
                return &Patches{originals: make(map[reflect.Value][]byte), values: make(map[reflect.Value]reflect.Value), valueHolders: make(map[reflect.Value]reflect.Value)}
            }
                 NewPatches也是调用同样的方法
              func NewPatches() *Patches {
                  return create()
              }
                  其结构体定义如下:
                type Patches struct {
                    originals    map[reflect.Value][]byte
                    values       map[reflect.Value]reflect.Value
                    valueHolders map[reflect.Value]reflect.Value
                }
                       对应具体ApplyMethod方法,首先通过反射方法ApplyMethod根据方法名称获取对应的方法,接着通过反射方法ValueOf获取替换模板的reflect.Value,最后通过ApplyCore来实现替换。
                  func (this *Patches) ApplyMethod(target reflect.Type, methodName string, double interface{}) *Patches {
                      m, ok := target.MethodByName(methodName)
                      if !ok {
                          panic("retrieve method by name failed")
                      }
                      d := reflect.ValueOf(double)
                      return this.ApplyCore(m.Func, d)
                  }
                  同理
                    func (this *Patches) ApplyFunc(target, double interface{}) *Patches {
                        t := reflect.ValueOf(target)
                        d := reflect.ValueOf(double)
                        return this.ApplyCore(t, d)
                    }
                            ApplyGlobalVar不同的是,它替换的是变量,所以使用的反射方法不一样
                      func (this *Patches) ApplyGlobalVar(target, double interface{}) *Patches {
                          t := reflect.ValueOf(target)
                          if t.Type().Kind() != reflect.Ptr {
                              panic("target is not a pointer")
                          }
                          this.values[t] = reflect.ValueOf(t.Elem().Interface())
                          d := reflect.ValueOf(double)
                          t.Elem().Set(d)
                          return this
                      }
                              接着看下ApplyCore方法,首先使用check方法检查下两者是不是都是方法,两者的类型是否一样。然后通过replace方法,用输入值替换原始值,并把原始值存储在originals变量里。
                        func (this *Patches) ApplyCore(target, double reflect.Value) *Patches {
                            this.check(target, double)
                            if _, ok := this.originals[target]; ok {
                                panic("patch has been existed")
                            }
                            this.valueHolders[double] = double
                            original := replace(*(*uintptr)(getPointer(target)), uintptr(getPointer(double)))
                            this.originals[target] = original
                            return this
                        }
                        替换的时候使用了指针转化,获取函数的原始地址
                          func getPointer(v reflect.Valueunsafe.Pointer {
                              return (*funcValue)(unsafe.Pointer(&v)).p
                          }
                            replace的过程就是指针的替换
                            func replace(target, double uintptr) []byte {
                                code := buildJmpDirective(double)
                                bytes := entryAddress(target, len(code))
                                original := make([]bytelen(bytes))
                                copy(original, bytes)
                                modifyBinary(target, code)
                                return original
                            }
                            最后使用modifyBinary进行原始值的替换
                              func modifyBinary(target uintptr, bytes []byte) {
                                  function := entryAddress(target, len(bytes))


                                  page := entryAddress(pageStart(target), syscall.Getpagesize())
                                  err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
                                  if err != nil {
                                      panic(err)
                                  }
                                  copy(function, bytes)


                                  err = syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC)
                                  if err != nil {
                                      panic(err)
                                  }
                              }
                              使用 unix.Mprotect 函数来修改内存区域的保护属性。
                                      至此核心流程介绍完了,批量逻辑是类似的
                                func (this *Patches) ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches {
                                    funcType := reflect.TypeOf(target)
                                    t := reflect.ValueOf(target)
                                    d := getDoubleFunc(funcType, outputs)
                                    return this.ApplyCore(t, d)
                                }
                                只不过,它将目标值进行了封装,变成了一个函数,函数内部其实也是用了反射逻辑
                                  func getDoubleFunc(funcType reflect.Type, outputs []OutputCell) reflect.Value {
                                      if funcType.NumOut() != len(outputs[0].Values) {
                                          panic(fmt.Sprintf("func type has %v return values, but only %v values provided as double",
                                              funcType.NumOut(), len(outputs[0].Values)))
                                      }
                                      slice := make([]Params, 0)
                                      for _, output := range outputs {
                                          t := 0
                                          if output.Times <= 1 {
                                              t = 1
                                          } else {
                                              t = output.Times
                                          }
                                          for j := 0; j < t; j++ {
                                              slice = append(slice, output.Values)
                                          }
                                      }
                                      i := 0
                                      len := len(slice)
                                      return reflect.MakeFunc(funcType, func(_ []reflect.Value) []reflect.Value {
                                          if i < len {
                                              i++
                                              return GetResultValues(funcType, slice[i-1]...)
                                          }
                                          panic("double seq is less than call seq")
                                      })
                                  }
                                          整体代码不多,只不过用了很多反射函数,本质上就是一个地址替换。

                                  文章转载自golang算法架构leetcode技术php,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                  评论