153 lines
5.6 KiB
Go
153 lines
5.6 KiB
Go
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
|
//
|
|
// Permission to use, copy, modify, and distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// copyright notice and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
|
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
|
// tag is deprecated and thus should not be used.
|
|
// +build !js,!appengine,!safe,!disableunsafe
|
|
|
|
package spew
|
|
|
|
import (
|
|
"reflect"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
// UnsafeDisabled is a build-time constant which specifies whether or
|
|
// not access to the unsafe package is available.
|
|
UnsafeDisabled = false
|
|
|
|
// ptrSize is the size of a pointer on the current arch.
|
|
ptrSize = unsafe.Sizeof((*byte)(nil))
|
|
)
|
|
|
|
var (
|
|
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
|
// internal reflect.Value fields. These values are valid before golang
|
|
// commit ecccf07e7f9d which changed the format. The are also valid
|
|
// after commit 82f48826c6c7 which changed the format again to mirror
|
|
// the original format. Code in the init function updates these offsets
|
|
// as necessary.
|
|
offsetPtr = uintptr(ptrSize)
|
|
offsetScalar = uintptr(0)
|
|
offsetFlag = uintptr(ptrSize * 2)
|
|
|
|
// flagKindWidth and flagKindShift indicate various bits that the
|
|
// reflect package uses internally to track kind information.
|
|
//
|
|
// flagRO indicates whether or not the value field of a reflect.Value is
|
|
// read-only.
|
|
//
|
|
// flagIndir indicates whether the value field of a reflect.Value is
|
|
// the actual data or a pointer to the data.
|
|
//
|
|
// These values are valid before golang commit 90a7c3c86944 which
|
|
// changed their positions. Code in the init function updates these
|
|
// flags as necessary.
|
|
flagKindWidth = uintptr(5)
|
|
flagKindShift = uintptr(flagKindWidth - 1)
|
|
flagRO = uintptr(1 << 0)
|
|
flagIndir = uintptr(1 << 1)
|
|
)
|
|
|
|
func init() {
|
|
// Older versions of reflect.Value stored small integers directly in the
|
|
// ptr field (which is named val in the older versions). Versions
|
|
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
|
// scalar for this purpose which unfortunately came before the flag
|
|
// field, so the offset of the flag field is different for those
|
|
// versions.
|
|
//
|
|
// This code constructs a new reflect.Value from a known small integer
|
|
// and checks if the size of the reflect.Value struct indicates it has
|
|
// the scalar field. When it does, the offsets are updated accordingly.
|
|
vv := reflect.ValueOf(0xf00)
|
|
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
|
offsetScalar = ptrSize * 2
|
|
offsetFlag = ptrSize * 3
|
|
}
|
|
|
|
// Commit 90a7c3c86944 changed the flag positions such that the low
|
|
// order bits are the kind. This code extracts the kind from the flags
|
|
// field and ensures it's the correct type. When it's not, the flag
|
|
// order has been changed to the newer format, so the flags are updated
|
|
// accordingly.
|
|
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
|
upfv := *(*uintptr)(upf)
|
|
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
|
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
|
flagKindShift = 0
|
|
flagRO = 1 << 5
|
|
flagIndir = 1 << 6
|
|
|
|
// Commit adf9b30e5594 modified the flags to separate the
|
|
// flagRO flag into two bits which specifies whether or not the
|
|
// field is embedded. This causes flagIndir to move over a bit
|
|
// and means that flagRO is the combination of either of the
|
|
// original flagRO bit and the new bit.
|
|
//
|
|
// This code detects the change by extracting what used to be
|
|
// the indirect bit to ensure it's set. When it's not, the flag
|
|
// order has been changed to the newer format, so the flags are
|
|
// updated accordingly.
|
|
if upfv&flagIndir == 0 {
|
|
flagRO = 3 << 5
|
|
flagIndir = 1 << 7
|
|
}
|
|
}
|
|
}
|
|
|
|
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
|
// the typical safety restrictions preventing access to unaddressable and
|
|
// unexported data. It works by digging the raw pointer to the underlying
|
|
// value out of the protected value and generating a new unprotected (unsafe)
|
|
// reflect.Value to it.
|
|
//
|
|
// This allows us to check for implementations of the Stringer and error
|
|
// interfaces to be used for pretty printing ordinarily unaddressable and
|
|
// inaccessible values such as unexported struct fields.
|
|
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
|
indirects := 1
|
|
vt := v.Type()
|
|
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
|
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
|
if rvf&flagIndir != 0 {
|
|
vt = reflect.PtrTo(v.Type())
|
|
indirects++
|
|
} else if offsetScalar != 0 {
|
|
// The value is in the scalar field when it's not one of the
|
|
// reference types.
|
|
switch vt.Kind() {
|
|
case reflect.Uintptr:
|
|
case reflect.Chan:
|
|
case reflect.Func:
|
|
case reflect.Map:
|
|
case reflect.Ptr:
|
|
case reflect.UnsafePointer:
|
|
default:
|
|
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
|
offsetScalar)
|
|
}
|
|
}
|
|
|
|
pv := reflect.NewAt(vt, upv)
|
|
rv = pv
|
|
for i := 0; i < indirects; i++ {
|
|
rv = rv.Elem()
|
|
}
|
|
return rv
|
|
}
|