]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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. | |
15c0b25d AP |
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 | |
bae9f6d2 JC |
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 | ||
15c0b25d AP |
39 | type flag uintptr |
40 | ||
bae9f6d2 | 41 | var ( |
15c0b25d AP |
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 | |
bae9f6d2 JC |
49 | ) |
50 | ||
15c0b25d AP |
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) | |
bae9f6d2 | 55 | |
15c0b25d AP |
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") | |
bae9f6d2 | 75 | } |
15c0b25d AP |
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)) | |
bae9f6d2 JC |
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. | |
15c0b25d AP |
93 | func unsafeReflectValue(v reflect.Value) reflect.Value { |
94 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { | |
95 | return v | |
bae9f6d2 | 96 | } |
15c0b25d AP |
97 | flagFieldPtr := flagField(&v) |
98 | *flagFieldPtr &^= flagRO | |
99 | *flagFieldPtr |= flagAddr | |
100 | return v | |
101 | } | |
bae9f6d2 | 102 | |
15c0b25d AP |
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 | } | |
bae9f6d2 | 143 | } |
15c0b25d | 144 | panic("reflect.Value read-only flag has changed semantics") |
bae9f6d2 | 145 | } |