| // 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") |
| } |