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 82f48826c6c7 as well as the internal
reflect.Value flag bit changes as of golang commit 90a7c3c86944.

It accomplishes this by doing some inspection at init time and choosing
the appropriate offsets and flag positions accordingly.  There was some
previous logic which dealt with a similar issue for golang commit
ecccf07e7f9d.  However, since the more recent commits essentially reverted
the change and also modify the flag bit positions, it made more sense to
rework the detection logic.  In particular, the new logic examines the
size of the reflect.Value struct to determine the difference and extracts
the kind from the flags to determine if the flags have been changed.

As a result, this commit allows spew to work properly with tip all the
back to Go 1.0.
This commit is contained in:
Dave Collins 2014-10-24 18:49:38 -05:00
parent 3fdaf5cea8
commit 128854244a
2 changed files with 56 additions and 49 deletions

View File

@ -25,58 +25,71 @@ import (
"unsafe" "unsafe"
) )
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the internal const (
// reflect.Value fields. // ptrSize is the size of a pointer on the current arch.
var offsetPtr, offsetScalar, offsetFlag uintptr ptrSize = unsafe.Sizeof((*byte)(nil))
)
// reflectValueOld mirrors the struct layout of the reflect package Value type var (
// before golang commit ecccf07e7f9d. // offsetPtr, offsetScalar, and offsetFlag are the offsets for the
var reflectValueOld struct { // internal reflect.Value fields. These values are valid before golang
typ unsafe.Pointer // commit ecccf07e7f9d which changed the format. The are also valid
val unsafe.Pointer // after commit 82f48826c6c7 which changed the format again to mirror
flag uintptr // the original format. Code in the init function updates these offsets
} // as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
// reflectValueNew mirrors the struct layout of the reflect package Value type // flagKindWidth and flagKindShift indicate various bits that the
// after golang commit ecccf07e7f9d. // reflect package uses internally to track kind information.
var reflectValueNew struct { //
typ unsafe.Pointer // flagRO indicates whether or not the value field of a reflect.Value is
ptr unsafe.Pointer // read-only.
scalar uintptr //
flag uintptr // 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() { func init() {
// Older versions of reflect.Value stored small integers directly in the // Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Newer versions // ptr field (which is named val in the older versions). Versions
// added a new field named scalar for this purpose which unfortuantely // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
// comes before the flag field. Further the new field is before the // scalar for this purpose which unfortunately came before the flag
// flag field, so the offset of the flag field is different as well. // 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 // 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 // and checks if the size of the reflect.Value struct indicates it has
// old style reflect.Value is being used. Otherwise it's the new style. // the scalar field. When it does, the offsets are updated accordingly.
v := 0xf00 vv := reflect.ValueOf(0xf00)
vv := reflect.ValueOf(v) if unsafe.Sizeof(vv) == (ptrSize * 4) {
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetScalar = ptrSize * 2
unsafe.Offsetof(reflectValueOld.val)) offsetFlag = ptrSize * 3
// 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 // Commit 90a7c3c86944 changed the flag positions such that the low
// data or a pointer to the data. // order bits are the kind. This code extracts the kind from the flags
const flagIndir = 1 << 1 // 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
}
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses // unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and // the typical safety restrictions preventing access to unaddressable and

View File

@ -81,12 +81,6 @@ func TestInvalidReflectValue(t *testing.T) {
} }
} }
// flagRO, flagKindShift and flagKindWidth indicate various bit flags that the
// reflect package uses internally to track kind and state information.
const flagRO = 1 << 0
const flagKindShift = 4
const flagKindWidth = 5
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to // changeKind uses unsafe to intentionally change the kind of a reflect.Value to
// the maximum kind value which does not exist. This is needed to test the // the maximum kind value which does not exist. This is needed to test the
// 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