Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
5.6 KiB
152 lines
5.6 KiB
// 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 |
|
}
|
|
|