Платформа ЦРНП "Мирокод" для разработки проектов
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.
145 lines
4.6 KiB
145 lines
4.6 KiB
// Copyright (c) 2015-2016 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. |
|
// Go versions prior to 1.4 are disabled because they use a different layout |
|
// for interfaces which make the implementation of unsafeReflectValue more complex. |
|
// +build !js,!appengine,!safe,!disableunsafe,go1.4 |
|
|
|
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)) |
|
) |
|
|
|
type flag uintptr |
|
|
|
var ( |
|
// flagRO indicates whether the value field of a reflect.Value |
|
// is read-only. |
|
flagRO flag |
|
|
|
// flagAddr indicates whether the address of the reflect.Value's |
|
// value may be taken. |
|
flagAddr flag |
|
) |
|
|
|
// flagKindMask holds the bits that make up the kind |
|
// part of the flags field. In all the supported versions, |
|
// it is in the lower 5 bits. |
|
const flagKindMask = flag(0x1f) |
|
|
|
// Different versions of Go have used different |
|
// bit layouts for the flags type. This table |
|
// records the known combinations. |
|
var okFlags = []struct { |
|
ro, addr flag |
|
}{{ |
|
// From Go 1.4 to 1.5 |
|
ro: 1 << 5, |
|
addr: 1 << 7, |
|
}, { |
|
// Up to Go tip. |
|
ro: 1<<5 | 1<<6, |
|
addr: 1 << 8, |
|
}} |
|
|
|
var flagValOffset = func() uintptr { |
|
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") |
|
if !ok { |
|
panic("reflect.Value has no flag field") |
|
} |
|
return field.Offset |
|
}() |
|
|
|
// flagField returns a pointer to the flag field of a reflect.Value. |
|
func flagField(v *reflect.Value) *flag { |
|
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) |
|
} |
|
|
|
// 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) reflect.Value { |
|
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { |
|
return v |
|
} |
|
flagFieldPtr := flagField(&v) |
|
*flagFieldPtr &^= flagRO |
|
*flagFieldPtr |= flagAddr |
|
return v |
|
} |
|
|
|
// Sanity checks against future reflect package changes |
|
// to the type or semantics of the Value.flag field. |
|
func init() { |
|
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") |
|
if !ok { |
|
panic("reflect.Value has no flag field") |
|
} |
|
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { |
|
panic("reflect.Value flag field has changed kind") |
|
} |
|
type t0 int |
|
var t struct { |
|
A t0 |
|
// t0 will have flagEmbedRO set. |
|
t0 |
|
// a will have flagStickyRO set |
|
a t0 |
|
} |
|
vA := reflect.ValueOf(t).FieldByName("A") |
|
va := reflect.ValueOf(t).FieldByName("a") |
|
vt0 := reflect.ValueOf(t).FieldByName("t0") |
|
|
|
// Infer flagRO from the difference between the flags |
|
// for the (otherwise identical) fields in t. |
|
flagPublic := *flagField(&vA) |
|
flagWithRO := *flagField(&va) | *flagField(&vt0) |
|
flagRO = flagPublic ^ flagWithRO |
|
|
|
// Infer flagAddr from the difference between a value |
|
// taken from a pointer and not. |
|
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") |
|
flagNoPtr := *flagField(&vA) |
|
flagPtr := *flagField(&vPtrA) |
|
flagAddr = flagNoPtr ^ flagPtr |
|
|
|
// Check that the inferred flags tally with one of the known versions. |
|
for _, f := range okFlags { |
|
if flagRO == f.ro && flagAddr == f.addr { |
|
return |
|
} |
|
} |
|
panic("reflect.Value read-only flag has changed semantics") |
|
}
|
|
|