reflect: implement support for array types

This commit is contained in:
Ayke van Laethem
2019-08-16 22:08:44 +02:00
committed by Ron Evans
parent bbc3046687
commit c19c738f52
8 changed files with 184 additions and 20 deletions
+3
View File
@@ -16,6 +16,9 @@ var structTypesSidetable byte
//go:extern reflect.structNamesSidetable
var structNamesSidetable byte
//go:extern reflect.arrayTypesSidetable
var arrayTypesSidetable byte
// readStringSidetable reads a string from the given table (like
// structNamesSidetable) and returns this string. No heap allocation is
// necessary because it makes the string point directly to the raw bytes of the
+22 -2
View File
@@ -142,11 +142,17 @@ func (t Type) Kind() Kind {
}
}
// Elem returns the element type for channel, slice and array types, the
// pointed-to value for pointer types, and the key type for map types.
func (t Type) Elem() Type {
switch t.Kind() {
case Chan, Ptr, Slice:
return t.stripPrefix()
default: // not implemented: Array, Map
case Array:
index := t.stripPrefix()
elem, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(index)))
return Type(elem)
default: // not implemented: Map
panic("unimplemented: (reflect.Type).Elem()")
}
}
@@ -254,8 +260,20 @@ func (t Type) Bits() int {
panic(TypeError{"Bits"})
}
// Len returns the number of elements in this array. It panics of the type kind
// is not Array.
func (t Type) Len() int {
panic("unimplemented: (reflect.Type).Len()")
if t.Kind() != Array {
panic(TypeError{"Len"})
}
// skip past the element type
arrayIdentifier := t.stripPrefix()
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(arrayIdentifier)))
// Read the array length.
arrayLen, _ := readVarint(p)
return int(arrayLen)
}
// NumField returns the number of fields of a struct type. It panics for other
@@ -301,6 +319,8 @@ func (t Type) Size() uintptr {
return unsafe.Sizeof(SliceHeader{})
case Interface:
return unsafe.Sizeof(interfaceHeader{})
case Array:
return t.Elem().Size() * uintptr(t.Len())
case Struct:
numField := t.NumField()
if numField == 0 {
+73 -12
View File
@@ -302,6 +302,8 @@ func (v Value) Slice(i, j int) Value {
panic("unimplemented: (reflect.Value).Slice()")
}
// Len returns the length of this value for slices, strings, arrays, channels,
// and maps. For oter types, it panics.
func (v Value) Len() int {
t := v.Type()
switch t.Kind() {
@@ -309,7 +311,9 @@ func (v Value) Len() int {
return int((*SliceHeader)(v.value).Len)
case String:
return int((*StringHeader)(v.value).Len)
default: // Array, Chan, Map
case Array:
return v.Type().Len()
default: // Chan, Map
panic("unimplemented: (reflect.Value).Len()")
}
}
@@ -392,27 +396,21 @@ func (v Value) Field(i int) Value {
// afterwards, so load the value (from the correct offset) and return
// it.
ptr := unsafe.Pointer(uintptr(v.value) + structField.Offset)
loadedValue := uintptr(0)
shift := uintptr(0)
for i := uintptr(0); i < fieldSize; i++ {
loadedValue |= uintptr(*(*byte)(ptr)) << shift
shift += 8
ptr = unsafe.Pointer(uintptr(ptr) + 1)
}
value := unsafe.Pointer(loadValue(ptr, fieldSize))
return Value{
flags: 0,
typecode: structField.Type,
value: unsafe.Pointer(loadedValue),
value: value,
}
}
// The value was already stored directly in the interface and it still
// is. Cut out the part of the value that we need.
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - fieldSize) * 8)
value := maskAndShift(uintptr(v.value), structField.Offset, fieldSize)
return Value{
flags: flags,
typecode: structField.Type,
value: unsafe.Pointer((uintptr(v.value) >> (structField.Offset * 8)) & mask),
value: unsafe.Pointer(value),
}
}
@@ -444,12 +442,75 @@ func (v Value) Index(i int) Value {
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(s.Data + uintptr(i))))),
}
case Array:
panic("unimplemented: (reflect.Value).Index()")
// Extract an element from the array.
elemType := v.Type().Elem()
elemSize := elemType.Size()
size := v.Type().Size()
if size == 0 {
// The element size is 0 and/or the length of the array is 0.
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
}
}
if elemSize > unsafe.Sizeof(uintptr(0)) {
// The resulting value doesn't fit in a pointer so must be
// indirect. Also, because size != 0 this implies that the array
// length must be != 0, and thus that the total size is at least
// elemSize.
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
value: unsafe.Pointer(addr),
}
}
if size > unsafe.Sizeof(uintptr(0)) {
// The element fits in a pointer, but the array does not.
// Load the value from the pointer.
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
value: unsafe.Pointer(loadValue(unsafe.Pointer(addr), elemSize)),
}
}
// The value fits in a pointer, so extract it with some shifting and
// masking.
offset := elemSize * uintptr(i)
value := maskAndShift(uintptr(v.value), offset, elemSize)
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
value: unsafe.Pointer(value),
}
default:
panic(&ValueError{"Index"})
}
}
// loadValue loads a value that may or may not be word-aligned. The number of
// bytes given in size are loaded. The biggest possible size it can load is that
// of an uintptr.
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
loadedValue := uintptr(0)
shift := uintptr(0)
for i := uintptr(0); i < size; i++ {
loadedValue |= uintptr(*(*byte)(ptr)) << shift
shift += 8
ptr = unsafe.Pointer(uintptr(ptr) + 1)
}
return loadedValue
}
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
func maskAndShift(value, offset, size uintptr) uintptr {
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
return (uintptr(value) >> (offset * 8)) & mask
}
func (v Value) MapKeys() []Value {
panic("unimplemented: (reflect.Value).MapKeys()")
}
+6 -3
View File
@@ -49,10 +49,13 @@ type typecodeID struct {
// * basic types: null
// * named type: the underlying type
// * interface: null
// * chan/pointer/slice: the element type
// * struct: GEP of structField array (to typecode field)
// * array/func/map: TODO
// * chan/pointer/slice/array: the element type
// * struct: bitcast of global with structField array
// * func/map: TODO
references *typecodeID
// The array length, for array types.
length uintptr
}
// structField is used by the compiler to pass information to the interface