github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/test/go-spew/spew/bypass.go (about) 1 // Copyright (c) 2015-2016 Dave Collins <dave@davec.name> 2 // 3 // Permission to use, copy, modify, and distribute this software for any 4 // purpose with or without fee is hereby granted, provided that the above 5 // copyright notice and this permission notice appear in all copies. 6 // 7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15 // NOTE: Due to the following build constraints, this file will only be compiled 16 // when the code is not running on Google App Engine, compiled by GopherJS, and 17 // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 // tag is deprecated and thus should not be used. 19 // +build !js,!appengine,!safe,!disableunsafe 20 21 package spew 22 23 import ( 24 "reflect" 25 "unsafe" 26 ) 27 28 const ( 29 // UnsafeDisabled is a build-time constant which specifies whether or 30 // not access to the unsafe package is available. 31 UnsafeDisabled = false 32 33 // ptrSize is the size of a pointer on the current arch. 34 ptrSize = unsafe.Sizeof((*byte)(nil)) 35 ) 36 37 var ( 38 // offsetPtr, offsetScalar, and offsetFlag are the offsets for the 39 // internal reflect.Value fields. These values are valid before golang 40 // commit ecccf07e7f9d which changed the format. The are also valid 41 // after commit 82f48826c6c7 which changed the format again to mirror 42 // the original format. Code in the init function updates these offsets 43 // as necessary. 44 offsetPtr = uintptr(ptrSize) 45 offsetScalar = uintptr(0) 46 offsetFlag = uintptr(ptrSize * 2) 47 48 // flagKindWidth and flagKindShift indicate various bits that the 49 // reflect package uses internally to track kind information. 50 // 51 // flagRO indicates whether or not the value field of a reflect.Value is 52 // read-only. 53 // 54 // flagIndir indicates whether the value field of a reflect.Value is 55 // the actual data or a pointer to the data. 56 // 57 // These values are valid before golang commit 90a7c3c86944 which 58 // changed their positions. Code in the init function updates these 59 // flags as necessary. 60 flagKindWidth = uintptr(5) 61 flagKindShift = uintptr(flagKindWidth - 1) 62 flagRO = uintptr(1 << 0) 63 flagIndir = uintptr(1 << 1) 64 ) 65 66 func init() { 67 // Older versions of reflect.Value stored small integers directly in the 68 // ptr field (which is named val in the older versions). Versions 69 // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named 70 // scalar for this purpose which unfortunately came before the flag 71 // field, so the offset of the flag field is different for those 72 // versions. 73 // 74 // This code constructs a new reflect.Value from a known small integer 75 // and checks if the size of the reflect.Value struct indicates it has 76 // the scalar field. When it does, the offsets are updated accordingly. 77 vv := reflect.ValueOf(0xf00) 78 if unsafe.Sizeof(vv) == (ptrSize * 4) { 79 offsetScalar = ptrSize * 2 80 offsetFlag = ptrSize * 3 81 } 82 83 // Commit 90a7c3c86944 changed the flag positions such that the low 84 // order bits are the kind. This code extracts the kind from the flags 85 // field and ensures it's the correct type. When it's not, the flag 86 // order has been changed to the newer format, so the flags are updated 87 // accordingly. 88 upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) 89 upfv := *(*uintptr)(upf) 90 flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift) 91 if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) { 92 flagKindShift = 0 93 flagRO = 1 << 5 94 flagIndir = 1 << 6 95 96 // Commit adf9b30e5594 modified the flags to separate the 97 // flagRO flag into two bits which specifies whether or not the 98 // field is embedded. This causes flagIndir to move over a bit 99 // and means that flagRO is the combination of either of the 100 // original flagRO bit and the new bit. 101 // 102 // This code detects the change by extracting what used to be 103 // the indirect bit to ensure it's set. When it's not, the flag 104 // order has been changed to the newer format, so the flags are 105 // updated accordingly. 106 if upfv&flagIndir == 0 { 107 flagRO = 3 << 5 108 flagIndir = 1 << 7 109 } 110 } 111 } 112 113 // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 114 // the typical safety restrictions preventing access to unaddressable and 115 // unexported data. It works by digging the raw pointer to the underlying 116 // value out of the protected value and generating a new unprotected (unsafe) 117 // reflect.Value to it. 118 // 119 // This allows us to check for implementations of the Stringer and error 120 // interfaces to be used for pretty printing ordinarily unaddressable and 121 // inaccessible values such as unexported struct fields. 122 func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { 123 indirects := 1 124 vt := v.Type() 125 upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) 126 rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) 127 if rvf&flagIndir != 0 { 128 vt = reflect.PtrTo(v.Type()) 129 indirects++ 130 } else if offsetScalar != 0 { 131 // The value is in the scalar field when it's not one of the 132 // reference types. 133 switch vt.Kind() { 134 case reflect.Uintptr: 135 case reflect.Chan: 136 case reflect.Func: 137 case reflect.Map: 138 case reflect.Ptr: 139 case reflect.UnsafePointer: 140 default: 141 upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 142 offsetScalar) 143 } 144 } 145 146 pv := reflect.NewAt(vt, upv) 147 rv = pv 148 for i := 0; i < indirects; i++ { 149 rv = rv.Elem() 150 } 151 return rv 152 }