blob: 792994785e36ca74c5545a0d93a2cdecda006678 [file] [log] [blame]
William Kurkianea869482019-04-09 15:16:11 -04001// 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
23package spew
24
25import (
26 "reflect"
27 "unsafe"
28)
29
30const (
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
39type flag uintptr
40
41var (
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.
54const 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.
59var 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
71var 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.
80func 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.
93func 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.
105func 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}