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// +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}
153