From 65ca732a33a40c2a5b9e036f236f975e7e85cf6c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 8 Jan 2014 02:01:15 -0600 Subject: [PATCH] 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. --- spew/common.go | 52 +++++++++++++++++++++++++++++++++--- spew/internal_test.go | 3 +-- test_coverage.txt | 61 ++++++++++++++++++++++--------------------- 3 files changed, 80 insertions(+), 36 deletions(-) diff --git a/spew/common.go b/spew/common.go index 0e59eca..bc26e7e 100644 --- a/spew/common.go +++ b/spew/common.go @@ -25,13 +25,55 @@ import ( "unsafe" ) -// reflectValue mirrors the struct layout of the reflect package Value type. -var reflectValue struct { +// offsetPtr, offsetScalar, and offsetFlag are the offsets for the internal +// 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 val unsafe.Pointer 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 // data or a pointer to the data. const flagIndir = 1 << 1 @@ -48,11 +90,13 @@ const flagIndir = 1 << 1 func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { indirects := 1 vt := v.Type() - upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + unsafe.Offsetof(reflectValue.val)) - rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + unsafe.Offsetof(reflectValue.flag))) + 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 { + upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetScalar) } pv := reflect.NewAt(vt, upv) diff --git a/spew/internal_test.go b/spew/internal_test.go index d8c9761..faac638 100644 --- a/spew/internal_test.go +++ b/spew/internal_test.go @@ -92,8 +92,7 @@ const flagKindWidth = 5 // fallback code which punts to the standard fmt library for new types that // might get added to the language. func changeKind(v *reflect.Value, readOnly bool) { - rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + - unsafe.Offsetof(reflectValue.flag))) + rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag)) *rvf = *rvf | ((1<