github.com/hedzr/evendeep@v0.4.8/internal/tool/unsafe.go (about) 1 //go:build !appengine && !disableunsafe 2 // +build !appengine,!disableunsafe 3 4 package tool 5 6 import ( 7 "reflect" 8 "unsafe" 9 ) 10 11 const ( 12 // UnsafeDisabled is a build-time constant which specifies whether or 13 // not access to the unsafe package is available. 14 UnsafeDisabled = false 15 16 // ptrSize is the size of a pointer on the current arch. 17 ptrSize = unsafe.Sizeof((*byte)(nil)) 18 ) 19 20 //nolint:gochecknoglobals //i do 21 var ( 22 // offsetPtr, offsetScalar, and offsetFlag are the offsets for the 23 // internal reflect.Value fields. These values are valid before golang 24 // commit ecccf07e7f9d which changed the format. The are also valid 25 // after commit 82f48826c6c7 which changed the format again to mirror 26 // the original format. Code in the init function updates these offsets 27 // as necessary. 28 offsetPtr = ptrSize 29 offsetScalar = uintptr(0) 30 offsetFlag = ptrSize * 2 31 32 // flagKindWidth and flagKindShift indicate various bits that the 33 // reflect package uses internally to track kind information. 34 // 35 // flagRO indicates whether or not the value field of a reflect.Value is 36 // read-only. 37 // 38 // flagIndir indicates whether the value field of a reflect.Value is 39 // the actual data or a pointer to the data. 40 // 41 // These values are valid before golang commit 90a7c3c86944 which 42 // changed their positions. Code in the init function updates these 43 // flags as necessary. 44 flagKindWidth = uintptr(5) //nolint:gomnd //i do 45 flagKindShift = uintptr(flagKindWidth - 1) //nolint:unconvert //i do 46 flagRO = uintptr(1 << 0) //nolint:unused //i do 47 flagIndir = uintptr(1 << 1) 48 ) 49 50 func init() { //nolint:gochecknoinits //no need 51 // Older versions of reflect.Value stored small integers directly in the 52 // ptr field (which is named val in the older versions). Versions 53 // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named 54 // scalar for this purpose which unfortunately came before the flag 55 // field, so the offset of the flag field is different for those 56 // versions. 57 // 58 // This code constructs a new reflect.Value from a known small integer 59 // and checks if the size of the reflect.Value struct indicates it has 60 // the scalar field. When it does, the offsets are updated accordingly. 61 vv := reflect.ValueOf(0xf00) //nolint:gomnd //no need 62 if unsafe.Sizeof(vv) == (ptrSize * 4) { //nolint:gomnd //no need 63 offsetScalar = ptrSize * 2 //nolint:gomnd //no need 64 offsetFlag = ptrSize * 3 //nolint:gomnd //no need 65 } 66 67 // Commit 90a7c3c86944 changed the flag positions such that the low 68 // order bits are the kind. This code extracts the kind from the flags 69 // field and ensures it's the correct type. When it's not, the flag 70 // order has been changed to the newer format, so the flags are updated 71 // accordingly. 72 upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) 73 upfv := *(*uintptr)(upf) 74 flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift) 75 if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) { 76 flagKindShift = 0 77 flagRO = 1 << 5 //nolint:gomnd //no need 78 flagIndir = 1 << 6 //nolint:gomnd //no need 79 80 // Commit adf9b30e5594 modified the flags to separate the 81 // flagRO flag into two bits which specifies whether or not the 82 // field is embedded. This causes flagIndir to move over a bit 83 // and means that flagRO is the combination of either of the 84 // original flagRO bit and the new bit. 85 // 86 // This code detects the change by extracting what used to be 87 // the indirect bit to ensure it's set. When it's not, the flag 88 // order has been changed to the newer format, so the flags are 89 // updated accordingly. 90 if upfv&flagIndir == 0 { 91 flagRO = 3 << 5 //nolint:gomnd //no need 92 flagIndir = 1 << 7 //nolint:gomnd //no need 93 } 94 } 95 } 96 97 // UnsafeReflectValue converts the passed reflect.Value into a one that bypasses 98 // the typical safety restrictions preventing access to unaddressable and 99 // unexported data. It works by digging the raw pointer to the underlying 100 // value out of the protected value and generating a new unprotected (unsafe) 101 // reflect.Value to it. 102 // 103 // This allows us to check for implementations of the Stringer and error 104 // interfaces to be used for pretty printing ordinarily unaddressable and 105 // inaccessible values such as unexported struct fields. 106 func UnsafeReflectValue(v reflect.Value) (rv reflect.Value) { 107 indirects := 1 108 vt := v.Type() 109 upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) 110 rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) 111 if rvf&flagIndir != 0 { 112 vt = reflect.PtrTo(v.Type()) 113 indirects++ 114 } else if offsetScalar != 0 { 115 // The value is in the scalar field when it's not one of the 116 // reference types. 117 switch vt.Kind() { //nolint:exhaustive //no need 118 case reflect.Uintptr: 119 case reflect.Chan: 120 case reflect.Func: 121 case reflect.Map: 122 case reflect.Ptr: 123 case reflect.UnsafePointer: 124 default: 125 upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 126 offsetScalar) 127 } 128 } 129 130 pv := reflect.NewAt(vt, upv) 131 rv = pv 132 for i := 0; i < indirects; i++ { 133 rv = rv.Elem() 134 } 135 return rv 136 }