bypass.go (4715B)
1 // Copyright (c) 2015-2016 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, compiled by GopherJS, and 17 // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 // tag is deprecated and thus should not be used. 19 // Go versions prior to 1.4 are disabled because they use a different layout 20 // for interfaces which make the implementation of unsafeReflectValue more complex. 21 // +build !js,!appengine,!safe,!disableunsafe,go1.4 22 23 package spew 24 25 import ( 26 "reflect" 27 "unsafe" 28 ) 29 30 const ( 31 // UnsafeDisabled is a build-time constant which specifies whether or 32 // not access to the unsafe package is available. 33 UnsafeDisabled = false 34 35 // ptrSize is the size of a pointer on the current arch. 36 ptrSize = unsafe.Sizeof((*byte)(nil)) 37 ) 38 39 type flag uintptr 40 41 var ( 42 // flagRO indicates whether the value field of a reflect.Value 43 // is read-only. 44 flagRO flag 45 46 // flagAddr indicates whether the address of the reflect.Value's 47 // value may be taken. 48 flagAddr flag 49 ) 50 51 // flagKindMask holds the bits that make up the kind 52 // part of the flags field. In all the supported versions, 53 // it is in the lower 5 bits. 54 const flagKindMask = flag(0x1f) 55 56 // Different versions of Go have used different 57 // bit layouts for the flags type. This table 58 // records the known combinations. 59 var okFlags = []struct { 60 ro, addr flag 61 }{{ 62 // From Go 1.4 to 1.5 63 ro: 1 << 5, 64 addr: 1 << 7, 65 }, { 66 // Up to Go tip. 67 ro: 1<<5 | 1<<6, 68 addr: 1 << 8, 69 }} 70 71 var flagValOffset = func() uintptr { 72 field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 73 if !ok { 74 panic("reflect.Value has no flag field") 75 } 76 return field.Offset 77 }() 78 79 // flagField returns a pointer to the flag field of a reflect.Value. 80 func flagField(v *reflect.Value) *flag { 81 return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 82 } 83 84 // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 85 // the typical safety restrictions preventing access to unaddressable and 86 // unexported data. It works by digging the raw pointer to the underlying 87 // value out of the protected value and generating a new unprotected (unsafe) 88 // reflect.Value to it. 89 // 90 // This allows us to check for implementations of the Stringer and error 91 // interfaces to be used for pretty printing ordinarily unaddressable and 92 // inaccessible values such as unexported struct fields. 93 func unsafeReflectValue(v reflect.Value) reflect.Value { 94 if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 95 return v 96 } 97 flagFieldPtr := flagField(&v) 98 *flagFieldPtr &^= flagRO 99 *flagFieldPtr |= flagAddr 100 return v 101 } 102 103 // Sanity checks against future reflect package changes 104 // to the type or semantics of the Value.flag field. 105 func init() { 106 field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 107 if !ok { 108 panic("reflect.Value has no flag field") 109 } 110 if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { 111 panic("reflect.Value flag field has changed kind") 112 } 113 type t0 int 114 var t struct { 115 A t0 116 // t0 will have flagEmbedRO set. 117 t0 118 // a will have flagStickyRO set 119 a t0 120 } 121 vA := reflect.ValueOf(t).FieldByName("A") 122 va := reflect.ValueOf(t).FieldByName("a") 123 vt0 := reflect.ValueOf(t).FieldByName("t0") 124 125 // Infer flagRO from the difference between the flags 126 // for the (otherwise identical) fields in t. 127 flagPublic := *flagField(&vA) 128 flagWithRO := *flagField(&va) | *flagField(&vt0) 129 flagRO = flagPublic ^ flagWithRO 130 131 // Infer flagAddr from the difference between a value 132 // taken from a pointer and not. 133 vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") 134 flagNoPtr := *flagField(&vA) 135 flagPtr := *flagField(&vPtrA) 136 flagAddr = flagNoPtr ^ flagPtr 137 138 // Check that the inferred flags tally with one of the known versions. 139 for _, f := range okFlags { 140 if flagRO == f.ro && flagAddr == f.addr { 141 return 142 } 143 } 144 panic("reflect.Value read-only flag has changed semantics") 145 }