bypass.go

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