aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/davecgh/go-spew/spew/bypass.go
blob: 8a4a6589a2d42ca8efa4a2dc8477f602d8dff328 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                                                         













                                                                                



                                                                               




































































































































                                                                                    
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line.  The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe

package spew

import (
    "reflect"
    "unsafe"
)

const (
    // UnsafeDisabled is a build-time constant which specifies whether or
    // not access to the unsafe package is available.
    UnsafeDisabled = false

    // ptrSize is the size of a pointer on the current arch.
    ptrSize = unsafe.Sizeof((*byte)(nil))
)

var (
    // offsetPtr, offsetScalar, and offsetFlag are the offsets for the
    // internal reflect.Value fields.  These values are valid before golang
    // commit ecccf07e7f9d which changed the format.  The are also valid
    // after commit 82f48826c6c7 which changed the format again to mirror
    // the original format.  Code in the init function updates these offsets
    // as necessary.
    offsetPtr    = uintptr(ptrSize)
    offsetScalar = uintptr(0)
    offsetFlag   = uintptr(ptrSize * 2)

    // flagKindWidth and flagKindShift indicate various bits that the
    // reflect package uses internally to track kind information.
    //
    // flagRO indicates whether or not the value field of a reflect.Value is
    // read-only.
    //
    // flagIndir indicates whether the value field of a reflect.Value is
    // the actual data or a pointer to the data.
    //
    // These values are valid before golang commit 90a7c3c86944 which
    // changed their positions.  Code in the init function updates these
    // flags as necessary.
    flagKindWidth = uintptr(5)
    flagKindShift = uintptr(flagKindWidth - 1)
    flagRO        = uintptr(1 << 0)
    flagIndir     = uintptr(1 << 1)
)

func init() {
    // Older versions of reflect.Value stored small integers directly in the
    // ptr field (which is named val in the older versions).  Versions
    // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
    // scalar for this purpose which unfortunately came before the flag
    // field, so the offset of the flag field is different for those
    // versions.
    //
    // This code constructs a new reflect.Value from a known small integer
    // and checks if the size of the reflect.Value struct indicates it has
    // the scalar field. When it does, the offsets are updated accordingly.
    vv := reflect.ValueOf(0xf00)
    if unsafe.Sizeof(vv) == (ptrSize * 4) {
        offsetScalar = ptrSize * 2
        offsetFlag = ptrSize * 3
    }

    // Commit 90a7c3c86944 changed the flag positions such that the low
    // order bits are the kind.  This code extracts the kind from the flags
    // field and ensures it's the correct type.  When it's not, the flag
    // order has been changed to the newer format, so the flags are updated
    // accordingly.
    upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
    upfv := *(*uintptr)(upf)
    flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
    if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
        flagKindShift = 0
        flagRO = 1 << 5
        flagIndir = 1 << 6

        // Commit adf9b30e5594 modified the flags to separate the
        // flagRO flag into two bits which specifies whether or not the
        // field is embedded.  This causes flagIndir to move over a bit
        // and means that flagRO is the combination of either of the
        // original flagRO bit and the new bit.
        //
        // This code detects the change by extracting what used to be
        // the indirect bit to ensure it's set.  When it's not, the flag
        // order has been changed to the newer format, so the flags are
        // updated accordingly.
        if upfv&flagIndir == 0 {
            flagRO = 3 << 5
            flagIndir = 1 << 7
        }
    }
}

// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and
// unexported data.  It works by digging the raw pointer to the underlying
// value out of the protected value and generating a new unprotected (unsafe)
// reflect.Value to it.
//
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
    indirects := 1
    vt := v.Type()
    upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
    rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
    if rvf&flagIndir != 0 {
        vt = reflect.PtrTo(v.Type())
        indirects++
    } else if offsetScalar != 0 {
        // The value is in the scalar field when it's not one of the
        // reference types.
        switch vt.Kind() {
        case reflect.Uintptr:
        case reflect.Chan:
        case reflect.Func:
        case reflect.Map:
        case reflect.Ptr:
        case reflect.UnsafePointer:
        default:
            upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
                offsetScalar)
        }
    }

    pv := reflect.NewAt(vt, upv)
    rv = pv
    for i := 0; i < indirects; i++ {
        rv = rv.Elem()
    }
    return rv
}