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 21package spew 22 23import ( 24 "reflect" 25 "unsafe" 26) 27 28const ( 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 37var ( 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 66func 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. 122func 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} 153