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