github.com/bytedance/mockey@v1.2.10/internal/unsafereflect/type.go (about)

     1  /*
     2   * Copyright 2023 ByteDance Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   * Unsafe reflect package for mockey, copy most code from go/src/reflect/type.go,
    17   * allow to export the address of private member methods.
    18   */
    19  
    20  package unsafereflect
    21  
    22  import (
    23  	"reflect"
    24  	"unsafe"
    25  )
    26  
    27  func MethodByName(target interface{}, name string) (typ reflect.Type, fn unsafe.Pointer, ok bool) {
    28  	r := reflect.TypeOf(target)
    29  	rt := (*rtype)((*struct {
    30  		_    uintptr
    31  		data unsafe.Pointer
    32  	})(unsafe.Pointer(&r)).data)
    33  
    34  	for _, p := range rt.methods() {
    35  		if rt.nameOff(p.name).name() == name {
    36  			return toType(rt.typeOff(p.mtyp)), rt.Method(p), true
    37  		}
    38  	}
    39  	return nil, nil, false
    40  }
    41  
    42  // copy from src/reflect/type.go
    43  // rtype is the common implementation of most values.
    44  // It is embedded in other struct types.
    45  //
    46  // rtype must be kept in sync with src/runtime/type.go:/^type._type.
    47  type rtype struct {
    48  	size       uintptr
    49  	ptrdata    uintptr // number of bytes in the type that can contain pointers
    50  	hash       uint32  // hash of type; avoids computation in hash tables
    51  	tflag      tflag   // extra type information flags
    52  	align      uint8   // alignment of variable with this type
    53  	fieldAlign uint8   // alignment of struct field with this type
    54  	kind       uint8   // enumeration for C
    55  
    56  	// In go 1.13 equal was replaced with "alg *typeAlg".
    57  	// Since size(func) == size(ptr), the total size of rtype
    58  	// and alignment of other field keeps the same, we do not
    59  	// need to make an adaption for go1.13.
    60  	equal     func(unsafe.Pointer, unsafe.Pointer) bool
    61  	gcdata    *byte   // garbage collection data
    62  	str       nameOff // string form
    63  	ptrToThis typeOff // type for pointer to this type, may be zero
    64  }
    65  
    66  func (t *rtype) Method(p method) (fn unsafe.Pointer) {
    67  	tfn := t.textOff(p.tfn)
    68  	fn = unsafe.Pointer(&tfn)
    69  	return
    70  }
    71  
    72  const kindMask = (1 << 5) - 1
    73  
    74  func (t *rtype) Kind() reflect.Kind { return reflect.Kind(t.kind & kindMask) }
    75  
    76  type (
    77  	tflag   uint8
    78  	nameOff int32 // offset to a name
    79  	typeOff int32 // offset to an *rtype
    80  	textOff int32 // offset from top of text section
    81  )
    82  
    83  // resolveNameOff resolves a name offset from a base pointer.
    84  // The (*rtype).nameOff method is a convenience wrapper for this function.
    85  // Implemented in the runtime package.
    86  //
    87  //go:linkname resolveNameOff reflect.resolveNameOff
    88  func resolveNameOff(unsafe.Pointer, int32) unsafe.Pointer
    89  
    90  func (t *rtype) nameOff(off nameOff) name {
    91  	return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
    92  }
    93  
    94  // resolveTypeOff resolves an *rtype offset from a base type.
    95  // The (*rtype).typeOff method is a convenience wrapper for this function.
    96  //
    97  //go:linkname resolveTypeOff reflect.resolveTypeOff
    98  func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
    99  
   100  func (t *rtype) typeOff(off typeOff) *rtype {
   101  	return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off)))
   102  }
   103  
   104  // toType convert rtype to reflect.Type
   105  //
   106  // The conversion is not guaranteed to be successful.
   107  // If conversion failed, response will be nil
   108  func toType(r *rtype) reflect.Type {
   109  	var vt interface{}
   110  	*(*uintptr)(unsafe.Pointer(&vt)) = uintptr(unsafe.Pointer(r))
   111  	return reflect.TypeOf(vt)
   112  }
   113  
   114  // resolveTextOff resolves a function pointer offset from a base type.
   115  // The (*rtype).textOff method is a convenience wrapper for this function.
   116  // Implemented in the runtime package.
   117  //
   118  //go:linkname resolveTextOff reflect.resolveTextOff
   119  func resolveTextOff(unsafe.Pointer, int32) unsafe.Pointer
   120  
   121  func (t *rtype) textOff(off textOff) unsafe.Pointer {
   122  	return resolveTextOff(unsafe.Pointer(t), int32(off))
   123  }
   124  
   125  const tflagUncommon tflag = 1 << 0
   126  
   127  // uncommonType is present only for defined types or types with methods
   128  type uncommonType struct {
   129  	pkgPath nameOff // import path; empty for built-in types like int, string
   130  	mcount  uint16  // number of methods
   131  	xcount  uint16  // number of exported methods
   132  	moff    uint32  // offset from this uncommontype to [mcount]method
   133  	_       uint32  // unused
   134  }
   135  
   136  // ptrType represents a pointer type.
   137  type ptrType struct {
   138  	rtype
   139  	elem *rtype // pointer element (pointed at) type
   140  }
   141  
   142  // funcType represents a function type.
   143  type funcType struct {
   144  	rtype
   145  	inCount  uint16
   146  	outCount uint16 // top bit is set if last input parameter is ...
   147  }
   148  
   149  func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
   150  	return unsafe.Pointer(uintptr(p) + x)
   151  }
   152  
   153  // interfaceType represents an interface type.
   154  type interfaceType struct {
   155  	rtype
   156  	pkgPath name      // import path
   157  	methods []imethod // sorted by hash
   158  }
   159  
   160  type imethod struct {
   161  	_ nameOff // unused name of method
   162  	_ typeOff // unused .(*FuncType) underneath
   163  }
   164  
   165  func (t *rtype) methods() []method {
   166  	if t.tflag&tflagUncommon == 0 {
   167  		return nil
   168  	}
   169  	switch t.Kind() {
   170  	case reflect.Ptr:
   171  		return (*struct {
   172  			ptrType
   173  			u uncommonType
   174  		})(unsafe.Pointer(t)).u.methods()
   175  	case reflect.Func:
   176  		return (*struct {
   177  			funcType
   178  			u uncommonType
   179  		})(unsafe.Pointer(t)).u.methods()
   180  	case reflect.Interface:
   181  		return (*struct {
   182  			interfaceType
   183  			u uncommonType
   184  		})(unsafe.Pointer(t)).u.methods()
   185  	case reflect.Struct:
   186  		return (*struct {
   187  			structType
   188  			u uncommonType
   189  		})(unsafe.Pointer(t)).u.methods()
   190  	default:
   191  		return nil
   192  	}
   193  }
   194  
   195  // Method on non-interface type
   196  type method struct {
   197  	name nameOff // name of method
   198  	mtyp typeOff // method type (without receiver), not valid for private methods
   199  	_    textOff // unused fn used in interface call (one-word receiver)
   200  	tfn  textOff // fn used for normal method call
   201  }
   202  
   203  func (t *uncommonType) methods() []method {
   204  	if t.mcount == 0 {
   205  		return nil
   206  	}
   207  	return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.mcount > 0"))[:t.mcount:t.mcount]
   208  }
   209  
   210  // Struct field
   211  type structField struct {
   212  	_ name    // unused name is always non-empty
   213  	_ *rtype  // unused type of field
   214  	_ uintptr // unused byte offset of field
   215  }
   216  
   217  // structType
   218  type structType struct {
   219  	rtype
   220  	pkgPath name
   221  	fields  []structField // sorted by offset
   222  }
   223  
   224  type _String struct {
   225  	Data unsafe.Pointer
   226  	Len  int
   227  }