blob: 8a4a6589a2d42ca8efa4a2dc8477f602d8dff328 [file] [log] [blame]
Scott Baker1fe72732019-10-21 10:58:51 -07001// 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// +build !js,!appengine,!safe,!disableunsafe
20
21package spew
22
23import (
24 "reflect"
25 "unsafe"
26)
27
28const (
29 // UnsafeDisabled is a build-time constant which specifies whether or
30 // not access to the unsafe package is available.
31 UnsafeDisabled = false
32
33 // ptrSize is the size of a pointer on the current arch.
34 ptrSize = unsafe.Sizeof((*byte)(nil))
35)
36
37var (
38 // offsetPtr, offsetScalar, and offsetFlag are the offsets for the
39 // internal reflect.Value fields. These values are valid before golang
40 // commit ecccf07e7f9d which changed the format. The are also valid
41 // after commit 82f48826c6c7 which changed the format again to mirror
42 // the original format. Code in the init function updates these offsets
43 // as necessary.
44 offsetPtr = uintptr(ptrSize)
45 offsetScalar = uintptr(0)
46 offsetFlag = uintptr(ptrSize * 2)
47
48 // flagKindWidth and flagKindShift indicate various bits that the
49 // reflect package uses internally to track kind information.
50 //
51 // flagRO indicates whether or not the value field of a reflect.Value is
52 // read-only.
53 //
54 // flagIndir indicates whether the value field of a reflect.Value is
55 // the actual data or a pointer to the data.
56 //
57 // These values are valid before golang commit 90a7c3c86944 which
58 // changed their positions. Code in the init function updates these
59 // flags as necessary.
60 flagKindWidth = uintptr(5)
61 flagKindShift = uintptr(flagKindWidth - 1)
62 flagRO = uintptr(1 << 0)
63 flagIndir = uintptr(1 << 1)
64)
65
66func init() {
67 // Older versions of reflect.Value stored small integers directly in the
68 // ptr field (which is named val in the older versions). Versions
69 // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
70 // scalar for this purpose which unfortunately came before the flag
71 // field, so the offset of the flag field is different for those
72 // versions.
73 //
74 // This code constructs a new reflect.Value from a known small integer
75 // and checks if the size of the reflect.Value struct indicates it has
76 // the scalar field. When it does, the offsets are updated accordingly.
77 vv := reflect.ValueOf(0xf00)
78 if unsafe.Sizeof(vv) == (ptrSize * 4) {
79 offsetScalar = ptrSize * 2
80 offsetFlag = ptrSize * 3
81 }
82
83 // Commit 90a7c3c86944 changed the flag positions such that the low
84 // order bits are the kind. This code extracts the kind from the flags
85 // field and ensures it's the correct type. When it's not, the flag
86 // order has been changed to the newer format, so the flags are updated
87 // accordingly.
88 upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
89 upfv := *(*uintptr)(upf)
90 flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
91 if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
92 flagKindShift = 0
93 flagRO = 1 << 5
94 flagIndir = 1 << 6
95
96 // Commit adf9b30e5594 modified the flags to separate the
97 // flagRO flag into two bits which specifies whether or not the
98 // field is embedded. This causes flagIndir to move over a bit
99 // and means that flagRO is the combination of either of the
100 // original flagRO bit and the new bit.
101 //
102 // This code detects the change by extracting what used to be
103 // the indirect bit to ensure it's set. When it's not, the flag
104 // order has been changed to the newer format, so the flags are
105 // updated accordingly.
106 if upfv&flagIndir == 0 {
107 flagRO = 3 << 5
108 flagIndir = 1 << 7
109 }
110 }
111}
112
113// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
114// the typical safety restrictions preventing access to unaddressable and
115// unexported data. It works by digging the raw pointer to the underlying
116// value out of the protected value and generating a new unprotected (unsafe)
117// reflect.Value to it.
118//
119// This allows us to check for implementations of the Stringer and error
120// interfaces to be used for pretty printing ordinarily unaddressable and
121// inaccessible values such as unexported struct fields.
122func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
123 indirects := 1
124 vt := v.Type()
125 upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
126 rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
127 if rvf&flagIndir != 0 {
128 vt = reflect.PtrTo(v.Type())
129 indirects++
130 } else if offsetScalar != 0 {
131 // The value is in the scalar field when it's not one of the
132 // reference types.
133 switch vt.Kind() {
134 case reflect.Uintptr:
135 case reflect.Chan:
136 case reflect.Func:
137 case reflect.Map:
138 case reflect.Ptr:
139 case reflect.UnsafePointer:
140 default:
141 upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
142 offsetScalar)
143 }
144 }
145
146 pv := reflect.NewAt(vt, upv)
147 rv = pv
148 for i := 0; i < indirects; i++ {
149 rv = rv.Elem()
150 }
151 return rv
152}