mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 19:43:44 +00:00
reflect: implement support for array types
This commit is contained in:
committed by
Ron Evans
parent
bbc3046687
commit
c19c738f52
@@ -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
@@ -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
@@ -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()")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user