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