Add logic to deal with reflect pkg changes on tip.

This commit adds logic to gracefully handle the new internal reflect.Value
structure on tip as of golang commit ecccf07e7f9d.  It accomplishes this
by doing some inspection at init time and choosing the appropriate offsets
as well as modifying which offset is used for the value accordingly.  As a
result, this allows spew to work properly with both the current release
version of Go as well as tip.

Fixes #15.
This commit is contained in:
Dave Collins 2014-01-08 02:01:15 -06:00
parent bde46cf02b
commit 65ca732a33
3 changed files with 80 additions and 36 deletions

View File

@ -25,13 +25,55 @@ import (
"unsafe" "unsafe"
) )
// reflectValue mirrors the struct layout of the reflect package Value type. // offsetPtr, offsetScalar, and offsetFlag are the offsets for the internal
var reflectValue struct { // reflect.Value fields.
var offsetPtr, offsetScalar, offsetFlag uintptr
// reflectValueOld mirrors the struct layout of the reflect package Value type
// before golang commit ecccf07e7f9d.
var reflectValueOld struct {
typ unsafe.Pointer typ unsafe.Pointer
val unsafe.Pointer val unsafe.Pointer
flag uintptr flag uintptr
} }
// reflectValueNew mirrors the struct layout of the reflect package Value type
// after golang commit ecccf07e7f9d.
var reflectValueNew struct {
typ unsafe.Pointer
ptr unsafe.Pointer
scalar uintptr
flag uintptr
}
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Newer versions
// added a new field named scalar for this purpose which unfortuantely
// comes before the flag field. Further the new field is before the
// flag field, so the offset of the flag field is different as well.
// This code constructs a new reflect.Value from a known small integer
// and checks if the val field within it matches. When it matches, the
// old style reflect.Value is being used. Otherwise it's the new style.
v := 0xf00
vv := reflect.ValueOf(v)
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) +
unsafe.Offsetof(reflectValueOld.val))
// Assume the old style by default.
offsetPtr = unsafe.Offsetof(reflectValueOld.val)
offsetScalar = 0
offsetFlag = unsafe.Offsetof(reflectValueOld.flag)
// Use the new style offsets if the ptr field doesn't match the value
// since it must be in the new scalar field.
if int(*(*uintptr)(upv)) != v {
offsetPtr = unsafe.Offsetof(reflectValueNew.ptr)
offsetScalar = unsafe.Offsetof(reflectValueNew.scalar)
offsetFlag = unsafe.Offsetof(reflectValueNew.flag)
}
}
// flagIndir indicates whether the value field of a reflect.Value is the actual // flagIndir indicates whether the value field of a reflect.Value is the actual
// data or a pointer to the data. // data or a pointer to the data.
const flagIndir = 1 << 1 const flagIndir = 1 << 1
@ -48,11 +90,13 @@ const flagIndir = 1 << 1
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
indirects := 1 indirects := 1
vt := v.Type() vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + unsafe.Offsetof(reflectValue.val)) upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + unsafe.Offsetof(reflectValue.flag))) rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 { if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type()) vt = reflect.PtrTo(v.Type())
indirects++ indirects++
} else if offsetScalar != 0 {
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetScalar)
} }
pv := reflect.NewAt(vt, upv) pv := reflect.NewAt(vt, upv)

View File

@ -92,8 +92,7 @@ const flagKindWidth = 5
// fallback code which punts to the standard fmt library for new types that // fallback code which punts to the standard fmt library for new types that
// might get added to the language. // might get added to the language.
func changeKind(v *reflect.Value, readOnly bool) { func changeKind(v *reflect.Value, readOnly bool) {
rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag))
unsafe.Offsetof(reflectValue.flag)))
*rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift) *rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift)
if readOnly { if readOnly {
*rvf |= flagRO *rvf |= flagRO

View File

@ -6,48 +6,40 @@ github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39) github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30) github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18) github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12) github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (12/12)
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11) github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9) github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8) github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7) github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5) github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4) github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4) github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4) github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4) github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3) github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1) github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1) github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1) github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1) github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1) github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1) github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1) github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1) github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
@ -56,5 +48,14 @@ github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1) github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (494/494) github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)