mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 11:37:46 +00:00
all: refactor reflect package
This is a big commit that changes the way runtime type information is stored in
the binary. Instead of compressing it and storing it in a number of sidetables,
it is stored similar to how the Go compiler toolchain stores it (but still more
compactly).
This has a number of advantages:
* It is much easier to add new features to reflect support. They can simply
be added to these structs without requiring massive changes (especially in
the reflect lowering pass).
* It removes the reflect lowering pass, which was a large amount of hard to
understand and debug code.
* The reflect lowering pass also required merging all LLVM IR into one
module, which is terrible for performance especially when compiling large
amounts of code. See issue 2870 for details.
* It is (probably!) easier to reason about for the compiler.
The downside is that it increases code size a bit, especially when reflect is
involved. I hope to fix some of that in later patches.
This commit is contained in:
committed by
Ron Evans
parent
ebb410afd9
commit
4e8453167f
@@ -15,7 +15,7 @@ import "unsafe"
|
||||
type visit struct {
|
||||
a1 unsafe.Pointer
|
||||
a2 unsafe.Pointer
|
||||
typ rawType
|
||||
typ *rawType
|
||||
}
|
||||
|
||||
// Tests for deep equality using reflected types. The map argument tracks
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This stores a varint for each named type. Named types are identified by their
|
||||
// name instead of by their type. The named types stored in this struct are
|
||||
// non-basic types: pointer, struct, and channel.
|
||||
//
|
||||
//go:extern reflect.namedNonBasicTypesSidetable
|
||||
var namedNonBasicTypesSidetable uintptr
|
||||
|
||||
//go:extern reflect.structTypesSidetable
|
||||
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
|
||||
// table.
|
||||
func readStringSidetable(table unsafe.Pointer, index uintptr) string {
|
||||
nameLen, namePtr := readVarint(unsafe.Pointer(uintptr(table) + index))
|
||||
return *(*string)(unsafe.Pointer(&stringHeader{
|
||||
data: namePtr,
|
||||
len: nameLen,
|
||||
}))
|
||||
}
|
||||
|
||||
// readVarint decodes a varint as used in the encoding/binary package.
|
||||
// It has an input pointer and returns the read varint and the pointer
|
||||
// incremented to the next field in the data structure, just after the varint.
|
||||
//
|
||||
// Details:
|
||||
// https://github.com/golang/go/blob/e37a1b1c/src/encoding/binary/varint.go#L7-L25
|
||||
func readVarint(buf unsafe.Pointer) (uintptr, unsafe.Pointer) {
|
||||
var n uintptr
|
||||
shift := uintptr(0)
|
||||
for {
|
||||
// Read the next byte in the buffer.
|
||||
c := *(*byte)(buf)
|
||||
|
||||
// Decode the bits from this byte and add them to the output number.
|
||||
n |= uintptr(c&0x7f) << shift
|
||||
shift += 7
|
||||
|
||||
// Increment the buf pointer (pointer arithmetic!).
|
||||
buf = unsafe.Pointer(uintptr(buf) + 1)
|
||||
|
||||
// Check whether this is the last byte of this varint. The upper bit
|
||||
// (msb) indicates whether any bytes follow.
|
||||
if c>>7 == 0 {
|
||||
return n, buf
|
||||
}
|
||||
}
|
||||
}
|
||||
+217
-158
@@ -2,36 +2,72 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Type information of an interface is stored as a pointer to a global in the
|
||||
// interface type (runtime._interface). This is called a type struct.
|
||||
// It always starts with a byte that contains both the type kind and a few
|
||||
// flags. In most cases it also contains a pointer to another type struct
|
||||
// (ptrTo), that is the pointer type of the current type (for example, type int
|
||||
// also has a pointer to the type *int). The exception is pointer types, to
|
||||
// avoid infinite recursion.
|
||||
//
|
||||
// The layouts specifically look like this:
|
||||
// - basic types (Bool..UnsafePointer):
|
||||
// meta uint8 // actually: kind + flags
|
||||
// ptrTo *typeStruct
|
||||
// - named types (see elemType):
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// underlying *typeStruct // the underlying, non-named type
|
||||
// - channels and slices (see elemType):
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// elementType *typeStruct // the type that you get with .Elem()
|
||||
// - pointer types (see ptrType, this doesn't include chan, map, etc):
|
||||
// meta uint8
|
||||
// elementType *typeStruct
|
||||
// - array types (see arrayType)
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// elem *typeStruct // element type of the array
|
||||
// arrayLen uintptr // length of the array (this is part of the type)
|
||||
// - map types (this is still missing the key and element types)
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// - struct types (see structType):
|
||||
// meta uint8
|
||||
// numField uint16
|
||||
// ptrTo *typeStruct
|
||||
// fields [...]structField // the remaining fields are all of type structField
|
||||
// - interface types (this is missing the interface methods):
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// - signature types (this is missing input and output parameters):
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
//
|
||||
// The type struct is essentially a union of all the above types. Which it is,
|
||||
// can be determined by looking at the meta byte.
|
||||
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// The compiler uses a compact encoding to store type information. Unlike the
|
||||
// main Go compiler, most of the types are stored directly in the type code.
|
||||
//
|
||||
// Type code bit allocation:
|
||||
// xxxxx0: basic types, where xxxxx is the basic type number (never 0).
|
||||
// The higher bits indicate the named type, if any.
|
||||
// nxxx1: complex types, where n indicates whether this is a named type (named
|
||||
// if set) and xxx contains the type kind number:
|
||||
// 0 (0001): Chan
|
||||
// 1 (0011): Interface
|
||||
// 2 (0101): Pointer
|
||||
// 3 (0111): Slice
|
||||
// 4 (1001): Array
|
||||
// 5 (1011): Func
|
||||
// 6 (1101): Map
|
||||
// 7 (1111): Struct
|
||||
// The higher bits are either the contents of the type depending on the
|
||||
// type (if n is clear) or indicate the number of the named type (if n
|
||||
// is set).
|
||||
// Flags stored in the first byte of the struct field byte array. Must be kept
|
||||
// up to date with compiler/interface.go.
|
||||
const (
|
||||
structFieldFlagAnonymous = 1 << iota
|
||||
structFieldFlagHasTag
|
||||
structFieldFlagIsExported
|
||||
)
|
||||
|
||||
type Kind uintptr
|
||||
type Kind uint8
|
||||
|
||||
// Copied from reflect/type.go
|
||||
// https://golang.org/src/reflect/type.go?s=8302:8316#L217
|
||||
// These constants must match basicTypes and the typeKind* constants in
|
||||
// compiler/interface.go
|
||||
const (
|
||||
Invalid Kind = iota
|
||||
Bool
|
||||
@@ -124,11 +160,6 @@ func (k Kind) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// basicType returns a new Type for this kind if Kind is a basic type.
|
||||
func (k Kind) basicType() rawType {
|
||||
return rawType(k << 1)
|
||||
}
|
||||
|
||||
// Copied from reflect/type.go
|
||||
// https://go.dev/src/reflect/type.go?#L348
|
||||
|
||||
@@ -346,8 +377,64 @@ type Type interface {
|
||||
Out(i int) Type
|
||||
}
|
||||
|
||||
// The typecode as used in an interface{}.
|
||||
type rawType uintptr
|
||||
// Constants for the 'meta' byte.
|
||||
const (
|
||||
kindMask = 31 // mask to apply to the meta byte to get the Kind value
|
||||
flagNamed = 32 // flag that is set if this is a named type
|
||||
)
|
||||
|
||||
// The base type struct. All type structs start with this.
|
||||
type rawType struct {
|
||||
meta uint8 // metadata byte, contains kind and flags (see contants above)
|
||||
}
|
||||
|
||||
// All types that have an element type: named, chan, slice, array, map (but not
|
||||
// pointer because it doesn't have ptrTo).
|
||||
type elemType struct {
|
||||
rawType
|
||||
ptrTo *rawType
|
||||
elem *rawType
|
||||
}
|
||||
|
||||
type ptrType struct {
|
||||
rawType
|
||||
elem *rawType
|
||||
}
|
||||
|
||||
type arrayType struct {
|
||||
rawType
|
||||
ptrTo *rawType
|
||||
elem *rawType
|
||||
arrayLen uintptr
|
||||
}
|
||||
|
||||
// Type for struct types. The numField value is intentionally put before ptrTo
|
||||
// for better struct packing on 32-bit and 64-bit architectures. On these
|
||||
// architectures, the ptrTo field still has the same offset as in all the other
|
||||
// type structs.
|
||||
// The fields array isn't necessarily 1 structField long, instead it is as long
|
||||
// as numFields. The array is given a length of 1 to satisfy the Go type
|
||||
// checker.
|
||||
type structType struct {
|
||||
rawType
|
||||
numField uint16
|
||||
ptrTo *rawType
|
||||
fields [1]structField // the remaining fields are all of type structField
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
fieldType *rawType
|
||||
data unsafe.Pointer // various bits of information, packed in a byte array
|
||||
}
|
||||
|
||||
// Equivalent to (go/types.Type).Underlying(): if this is a named type return
|
||||
// the underlying type, else just return the type itself.
|
||||
func (t *rawType) underlying() *rawType {
|
||||
if t.meta&flagNamed != 0 {
|
||||
return (*elemType)(unsafe.Pointer(t)).elem
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func TypeOf(i interface{}) Type {
|
||||
return ValueOf(i).typecode
|
||||
@@ -356,70 +443,45 @@ func TypeOf(i interface{}) Type {
|
||||
func PtrTo(t Type) Type { return PointerTo(t) }
|
||||
|
||||
func PointerTo(t Type) Type {
|
||||
if t.Kind() == Pointer {
|
||||
switch t.Kind() {
|
||||
case Pointer:
|
||||
panic("reflect: cannot make **T type")
|
||||
case Struct:
|
||||
return (*structType)(unsafe.Pointer(t.(*rawType))).ptrTo
|
||||
default:
|
||||
return (*elemType)(unsafe.Pointer(t.(*rawType))).ptrTo
|
||||
}
|
||||
ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5
|
||||
if ptrType>>5 != t {
|
||||
panic("reflect: PointerTo type does not fit")
|
||||
}
|
||||
return ptrType
|
||||
}
|
||||
|
||||
func (t rawType) String() string {
|
||||
func (t *rawType) String() string {
|
||||
return "T"
|
||||
}
|
||||
|
||||
func (t rawType) Kind() Kind {
|
||||
if t%2 == 0 {
|
||||
// basic type
|
||||
return Kind((t >> 1) % 32)
|
||||
} else {
|
||||
return Kind(t>>1)%8 + 19
|
||||
}
|
||||
func (t *rawType) Kind() Kind {
|
||||
return Kind(t.meta & kindMask)
|
||||
}
|
||||
|
||||
// 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 rawType) Elem() Type {
|
||||
func (t *rawType) Elem() Type {
|
||||
return t.elem()
|
||||
}
|
||||
|
||||
func (t rawType) elem() rawType {
|
||||
switch t.Kind() {
|
||||
case Chan, Pointer, Slice:
|
||||
return t.stripPrefix()
|
||||
case Array:
|
||||
index := t.stripPrefix()
|
||||
elem, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(index)))
|
||||
return rawType(elem)
|
||||
func (t *rawType) elem() *rawType {
|
||||
underlying := t.underlying()
|
||||
switch underlying.Kind() {
|
||||
case Pointer:
|
||||
return (*ptrType)(unsafe.Pointer(underlying)).elem
|
||||
case Chan, Slice, Array:
|
||||
return (*elemType)(unsafe.Pointer(underlying)).elem
|
||||
default: // not implemented: Map
|
||||
panic("unimplemented: (reflect.Type).Elem()")
|
||||
}
|
||||
}
|
||||
|
||||
// stripPrefix removes the "prefix" (the low 5 bits of the type code) from
|
||||
// the type code. If this is a named type, it will resolve the underlying type
|
||||
// (which is the data for this named type). If it is not, the lower bits are
|
||||
// simply shifted off.
|
||||
//
|
||||
// The behavior is only defined for non-basic types.
|
||||
func (t rawType) stripPrefix() rawType {
|
||||
// Look at the 'n' bit in the type code (see the top of this file) to see
|
||||
// whether this is a named type.
|
||||
if (t>>4)%2 != 0 {
|
||||
// This is a named type. The data is stored in a sidetable.
|
||||
namedTypeNum := t >> 5
|
||||
n := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)*unsafe.Sizeof(uintptr(0))))
|
||||
return rawType(n)
|
||||
}
|
||||
// Not a named type, so the value is stored directly in the type code.
|
||||
return t >> 5
|
||||
}
|
||||
|
||||
// Field returns the type of the i'th field of this struct type. It panics if t
|
||||
// is not a struct type.
|
||||
func (t rawType) Field(i int) StructField {
|
||||
func (t *rawType) Field(i int) StructField {
|
||||
field := t.rawField(i)
|
||||
return StructField{
|
||||
Name: field.Name,
|
||||
@@ -435,82 +497,87 @@ func (t rawType) Field(i int) StructField {
|
||||
// Type member to an interface.
|
||||
//
|
||||
// For internal use only.
|
||||
func (t rawType) rawField(i int) rawStructField {
|
||||
func (t *rawType) rawField(n int) rawStructField {
|
||||
if t.Kind() != Struct {
|
||||
panic(&TypeError{"Field"})
|
||||
}
|
||||
structIdentifier := t.stripPrefix()
|
||||
|
||||
numField, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
|
||||
if uint(i) >= uint(numField) {
|
||||
descriptor := (*structType)(unsafe.Pointer(t.underlying()))
|
||||
if uint(n) >= uint(descriptor.numField) {
|
||||
panic("reflect: field index out of range")
|
||||
}
|
||||
|
||||
// Iterate over every field in the struct and update the StructField each
|
||||
// time, until the target field has been reached. This is very much not
|
||||
// efficient, but it is easy to implement.
|
||||
// Adding a jump table at the start to jump to the field directly would
|
||||
// make this much faster, but that would also impact code size.
|
||||
field := rawStructField{}
|
||||
offset := uintptr(0)
|
||||
for fieldNum := 0; fieldNum <= i; fieldNum++ {
|
||||
// Read some flags of this field, like whether the field is an
|
||||
// embedded field.
|
||||
flagsByte := *(*uint8)(p)
|
||||
p = unsafe.Pointer(uintptr(p) + 1)
|
||||
// Iterate over all the fields to calculate the offset.
|
||||
// This offset could have been stored directly in the array (to make the
|
||||
// lookup faster), but by calculating it on-the-fly a bit of storage can be
|
||||
// saved.
|
||||
field := &descriptor.fields[0]
|
||||
var offset uintptr = 0
|
||||
for i := 0; i < n; i++ {
|
||||
offset += field.fieldType.Size()
|
||||
|
||||
// Read the type of this struct field.
|
||||
var fieldTypeVal uintptr
|
||||
fieldTypeVal, p = readVarint(p)
|
||||
fieldType := rawType(fieldTypeVal)
|
||||
field.Type = fieldType
|
||||
// Increment pointer to the next field.
|
||||
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
|
||||
|
||||
// Move Offset forward to align it to this field's alignment.
|
||||
// Assume alignment is a power of two.
|
||||
offset = align(offset, uintptr(fieldType.Align()))
|
||||
field.Offset = offset
|
||||
offset += fieldType.Size() // starting (unaligned) offset for next field
|
||||
|
||||
// Read the field name.
|
||||
var nameNum uintptr
|
||||
nameNum, p = readVarint(p)
|
||||
field.Name = readStringSidetable(unsafe.Pointer(&structNamesSidetable), nameNum)
|
||||
|
||||
// The first bit in the flagsByte indicates whether this is an embedded
|
||||
// field.
|
||||
field.Anonymous = flagsByte&1 != 0
|
||||
|
||||
// The second bit indicates whether there is a tag.
|
||||
if flagsByte&2 != 0 {
|
||||
// There is a tag.
|
||||
var tagNum uintptr
|
||||
tagNum, p = readVarint(p)
|
||||
field.Tag = StructTag(readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum))
|
||||
} else {
|
||||
// There is no tag.
|
||||
field.Tag = ""
|
||||
}
|
||||
|
||||
// The third bit indicates whether this field is exported.
|
||||
if flagsByte&4 != 0 {
|
||||
// This field is exported.
|
||||
field.PkgPath = ""
|
||||
} else {
|
||||
// This field is unexported.
|
||||
// TODO: list the real package path here. Storing it should not
|
||||
// significantly impact binary size as there is only a limited
|
||||
// number of packages in any program.
|
||||
field.PkgPath = "<unimplemented>"
|
||||
}
|
||||
// Align the offset for the next field.
|
||||
offset = align(offset, uintptr(field.fieldType.Align()))
|
||||
}
|
||||
|
||||
return field
|
||||
data := field.data
|
||||
|
||||
// Read some flags of this field, like whether the field is an embedded
|
||||
// field. See structFieldFlagAnonymous and similar flags.
|
||||
flagsByte := *(*byte)(data)
|
||||
data = unsafe.Add(data, 1)
|
||||
|
||||
// Read the field name.
|
||||
nameStart := data
|
||||
var nameLen uintptr
|
||||
for *(*byte)(data) != 0 {
|
||||
nameLen++
|
||||
data = unsafe.Add(data, 1) // C: data++
|
||||
}
|
||||
name := *(*string)(unsafe.Pointer(&stringHeader{
|
||||
data: nameStart,
|
||||
len: nameLen,
|
||||
}))
|
||||
|
||||
// Read the field tag, if there is one.
|
||||
var tag string
|
||||
if flagsByte&structFieldFlagHasTag != 0 {
|
||||
data = unsafe.Add(data, 1) // C: data+1
|
||||
tagLen := uintptr(*(*byte)(data))
|
||||
data = unsafe.Add(data, 1) // C: data+1
|
||||
tag = *(*string)(unsafe.Pointer(&stringHeader{
|
||||
data: data,
|
||||
len: tagLen,
|
||||
}))
|
||||
}
|
||||
|
||||
// Set the PkgPath to some (arbitrary) value if the package path is not
|
||||
// exported.
|
||||
pkgPath := ""
|
||||
if flagsByte&structFieldFlagIsExported == 0 {
|
||||
// This field is unexported.
|
||||
// TODO: list the real package path here. Storing it should not
|
||||
// significantly impact binary size as there is only a limited
|
||||
// number of packages in any program.
|
||||
pkgPath = "<unimplemented>"
|
||||
}
|
||||
|
||||
return rawStructField{
|
||||
Name: name,
|
||||
PkgPath: pkgPath,
|
||||
Type: field.fieldType,
|
||||
Tag: StructTag(tag),
|
||||
Anonymous: flagsByte&structFieldFlagAnonymous != 0,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
// Bits returns the number of bits that this type uses. It is only valid for
|
||||
// arithmetic types (integers, floats, and complex numbers). For other types, it
|
||||
// will panic.
|
||||
func (t rawType) Bits() int {
|
||||
func (t *rawType) Bits() int {
|
||||
kind := t.Kind()
|
||||
if kind >= Int && kind <= Complex128 {
|
||||
return int(t.Size()) * 8
|
||||
@@ -520,34 +587,26 @@ func (t rawType) Bits() int {
|
||||
|
||||
// Len returns the number of elements in this array. It panics of the type kind
|
||||
// is not Array.
|
||||
func (t rawType) Len() int {
|
||||
func (t *rawType) Len() int {
|
||||
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)
|
||||
return int((*arrayType)(unsafe.Pointer(t.underlying())).arrayLen)
|
||||
}
|
||||
|
||||
// NumField returns the number of fields of a struct type. It panics for other
|
||||
// type kinds.
|
||||
func (t rawType) NumField() int {
|
||||
func (t *rawType) NumField() int {
|
||||
if t.Kind() != Struct {
|
||||
panic(&TypeError{"NumField"})
|
||||
}
|
||||
structIdentifier := t.stripPrefix()
|
||||
n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
|
||||
return int(n)
|
||||
return int((*structType)(unsafe.Pointer(t.underlying())).numField)
|
||||
}
|
||||
|
||||
// Size returns the size in bytes of a given type. It is similar to
|
||||
// unsafe.Sizeof.
|
||||
func (t rawType) Size() uintptr {
|
||||
func (t *rawType) Size() uintptr {
|
||||
switch t.Kind() {
|
||||
case Bool, Int8, Uint8:
|
||||
return 1
|
||||
@@ -596,7 +655,7 @@ func (t rawType) Size() uintptr {
|
||||
|
||||
// Align returns the alignment of this type. It is similar to calling
|
||||
// unsafe.Alignof.
|
||||
func (t rawType) Align() int {
|
||||
func (t *rawType) Align() int {
|
||||
switch t.Kind() {
|
||||
case Bool, Int8, Uint8:
|
||||
return int(unsafe.Alignof(int8(0)))
|
||||
@@ -648,14 +707,14 @@ func (t rawType) Align() int {
|
||||
|
||||
// FieldAlign returns the alignment if this type is used in a struct field. It
|
||||
// is currently an alias for Align() but this might change in the future.
|
||||
func (t rawType) FieldAlign() int {
|
||||
func (t *rawType) FieldAlign() int {
|
||||
return t.Align()
|
||||
}
|
||||
|
||||
// AssignableTo returns whether a value of type t can be assigned to a variable
|
||||
// of type u.
|
||||
func (t rawType) AssignableTo(u Type) bool {
|
||||
if t == u.(rawType) {
|
||||
func (t *rawType) AssignableTo(u Type) bool {
|
||||
if t == u.(*rawType) {
|
||||
return true
|
||||
}
|
||||
if u.Kind() == Interface {
|
||||
@@ -664,7 +723,7 @@ func (t rawType) AssignableTo(u Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t rawType) Implements(u Type) bool {
|
||||
func (t *rawType) Implements(u Type) bool {
|
||||
if u.Kind() != Interface {
|
||||
panic("reflect: non-interface type passed to Type.Implements")
|
||||
}
|
||||
@@ -672,7 +731,7 @@ func (t rawType) Implements(u Type) bool {
|
||||
}
|
||||
|
||||
// Comparable returns whether values of this type can be compared to each other.
|
||||
func (t rawType) Comparable() bool {
|
||||
func (t *rawType) Comparable() bool {
|
||||
switch t.Kind() {
|
||||
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
||||
return true
|
||||
@@ -713,31 +772,31 @@ func (t rawType) ChanDir() ChanDir {
|
||||
panic("unimplemented: (reflect.Type).ChanDir()")
|
||||
}
|
||||
|
||||
func (t rawType) ConvertibleTo(u Type) bool {
|
||||
func (t *rawType) ConvertibleTo(u Type) bool {
|
||||
panic("unimplemented: (reflect.Type).ConvertibleTo()")
|
||||
}
|
||||
|
||||
func (t rawType) IsVariadic() bool {
|
||||
func (t *rawType) IsVariadic() bool {
|
||||
panic("unimplemented: (reflect.Type).IsVariadic()")
|
||||
}
|
||||
|
||||
func (t rawType) NumIn() int {
|
||||
func (t *rawType) NumIn() int {
|
||||
panic("unimplemented: (reflect.Type).NumIn()")
|
||||
}
|
||||
|
||||
func (t rawType) NumOut() int {
|
||||
func (t *rawType) NumOut() int {
|
||||
panic("unimplemented: (reflect.Type).NumOut()")
|
||||
}
|
||||
|
||||
func (t rawType) NumMethod() int {
|
||||
func (t *rawType) NumMethod() int {
|
||||
panic("unimplemented: (reflect.Type).NumMethod()")
|
||||
}
|
||||
|
||||
func (t rawType) Name() string {
|
||||
func (t *rawType) Name() string {
|
||||
panic("unimplemented: (reflect.Type).Name()")
|
||||
}
|
||||
|
||||
func (t rawType) Key() Type {
|
||||
func (t *rawType) Key() Type {
|
||||
panic("unimplemented: (reflect.Type).Key()")
|
||||
}
|
||||
|
||||
@@ -792,7 +851,7 @@ func (f StructField) IsExported() bool {
|
||||
type rawStructField struct {
|
||||
Name string
|
||||
PkgPath string
|
||||
Type rawType
|
||||
Type *rawType
|
||||
Tag StructTag
|
||||
Anonymous bool
|
||||
Offset uintptr
|
||||
|
||||
+12
-10
@@ -17,7 +17,7 @@ const (
|
||||
)
|
||||
|
||||
type Value struct {
|
||||
typecode rawType
|
||||
typecode *rawType
|
||||
value unsafe.Pointer
|
||||
flags valueFlags
|
||||
}
|
||||
@@ -44,15 +44,15 @@ func Indirect(v Value) Value {
|
||||
}
|
||||
|
||||
//go:linkname composeInterface runtime.composeInterface
|
||||
func composeInterface(rawType, unsafe.Pointer) interface{}
|
||||
func composeInterface(unsafe.Pointer, unsafe.Pointer) interface{}
|
||||
|
||||
//go:linkname decomposeInterface runtime.decomposeInterface
|
||||
func decomposeInterface(i interface{}) (rawType, unsafe.Pointer)
|
||||
func decomposeInterface(i interface{}) (unsafe.Pointer, unsafe.Pointer)
|
||||
|
||||
func ValueOf(i interface{}) Value {
|
||||
typecode, value := decomposeInterface(i)
|
||||
return Value{
|
||||
typecode: typecode,
|
||||
typecode: (*rawType)(typecode),
|
||||
value: value,
|
||||
flags: valueFlagExported,
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func valueInterfaceUnsafe(v Value) interface{} {
|
||||
}
|
||||
v.value = unsafe.Pointer(value)
|
||||
}
|
||||
return composeInterface(v.typecode, v.value)
|
||||
return composeInterface(unsafe.Pointer(v.typecode), v.value)
|
||||
}
|
||||
|
||||
func (v Value) Type() Type {
|
||||
@@ -136,7 +136,7 @@ func (v Value) IsZero() bool {
|
||||
//
|
||||
// RawType returns the raw, underlying type code. It is used in the runtime
|
||||
// package and needs to be exported for the runtime package to access it.
|
||||
func (v Value) RawType() rawType {
|
||||
func (v Value) RawType() *rawType {
|
||||
return v.typecode
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ func (v Value) pointer() unsafe.Pointer {
|
||||
}
|
||||
|
||||
func (v Value) IsValid() bool {
|
||||
return v.typecode != 0
|
||||
return v.typecode != nil
|
||||
}
|
||||
|
||||
func (v Value) CanInterface() bool {
|
||||
@@ -453,7 +453,7 @@ func (v Value) Elem() Value {
|
||||
case Interface:
|
||||
typecode, value := decomposeInterface(*(*interface{})(v.value))
|
||||
return Value{
|
||||
typecode: typecode,
|
||||
typecode: (*rawType)(typecode),
|
||||
value: value,
|
||||
flags: v.flags &^ valueFlagIndirect,
|
||||
}
|
||||
@@ -523,6 +523,8 @@ func (v Value) Field(i int) Value {
|
||||
}
|
||||
}
|
||||
|
||||
var uint8Type = TypeOf(uint8(0)).(*rawType)
|
||||
|
||||
func (v Value) Index(i int) Value {
|
||||
switch v.Kind() {
|
||||
case Slice:
|
||||
@@ -550,7 +552,7 @@ func (v Value) Index(i int) Value {
|
||||
panic("reflect: string index out of range")
|
||||
}
|
||||
return Value{
|
||||
typecode: Uint8.basicType(),
|
||||
typecode: uint8Type,
|
||||
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(uintptr(s.data) + uintptr(i))))),
|
||||
flags: v.flags & valueFlagExported,
|
||||
}
|
||||
@@ -803,7 +805,7 @@ func Zero(typ Type) Value {
|
||||
// new value of the given type.
|
||||
func New(typ Type) Value {
|
||||
return Value{
|
||||
typecode: PtrTo(typ).(rawType),
|
||||
typecode: PtrTo(typ).(*rawType),
|
||||
value: alloc(typ.Size(), nil),
|
||||
flags: valueFlagExported,
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
|
||||
|
||||
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
||||
x := reflect.ValueOf(itf)
|
||||
if x.RawType() == 0 {
|
||||
if x.RawType() == nil {
|
||||
return 0 // nil interface
|
||||
}
|
||||
|
||||
|
||||
@@ -11,17 +11,17 @@ import (
|
||||
)
|
||||
|
||||
type _interface struct {
|
||||
typecode uintptr
|
||||
typecode unsafe.Pointer
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func composeInterface(typecode uintptr, value unsafe.Pointer) _interface {
|
||||
func composeInterface(typecode, value unsafe.Pointer) _interface {
|
||||
return _interface{typecode, value}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func decomposeInterface(i _interface) (uintptr, unsafe.Pointer) {
|
||||
func decomposeInterface(i _interface) (unsafe.Pointer, unsafe.Pointer) {
|
||||
return i.typecode, i.value
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func reflectValueEqual(x, y reflect.Value) bool {
|
||||
// Note: doing a x.Type() == y.Type() comparison would not work here as that
|
||||
// would introduce an infinite recursion: comparing two reflect.Type values
|
||||
// is done with this reflectValueEqual runtime call.
|
||||
if x.RawType() == 0 || y.RawType() == 0 {
|
||||
if x.RawType() == nil || y.RawType() == nil {
|
||||
// One of them is nil.
|
||||
return x.RawType() == y.RawType()
|
||||
}
|
||||
@@ -94,48 +94,13 @@ func interfaceTypeAssert(ok bool) {
|
||||
// lowered to inline IR in the interface lowering pass.
|
||||
// See compiler/interface-lowering.go for details.
|
||||
|
||||
type interfaceMethodInfo struct {
|
||||
signature *uint8 // external *i8 with a name identifying the Go function signature
|
||||
funcptr uintptr // bitcast from the actual function pointer
|
||||
}
|
||||
|
||||
type typecodeID struct {
|
||||
// Depending on the type kind of this typecodeID, this pointer is something
|
||||
// different:
|
||||
// * basic types: null
|
||||
// * named type: the underlying type
|
||||
// * interface: null
|
||||
// * 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
|
||||
|
||||
methodSet *interfaceMethodInfo // nil or a GEP of an array
|
||||
|
||||
// The type that's a pointer to this type, nil if it is already a pointer.
|
||||
// Keeping the type struct alive here is important so that values from
|
||||
// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
|
||||
ptrTo *typecodeID
|
||||
|
||||
// typeAssert is a ptrtoint of a declared interface assert function.
|
||||
// It only exists to make the rtcalls pass easier.
|
||||
typeAssert uintptr
|
||||
}
|
||||
|
||||
// structField is used by the compiler to pass information to the interface
|
||||
// lowering pass. It is not used in the final binary.
|
||||
type structField struct {
|
||||
typecode *typecodeID // type of this struct field
|
||||
name *uint8 // pointer to char array
|
||||
tag *uint8 // pointer to char array, or nil
|
||||
embedded bool
|
||||
typecode unsafe.Pointer // type of this struct field
|
||||
data *uint8 // pointer to byte array containing name, tag, and 'embedded' flag
|
||||
}
|
||||
|
||||
// Pseudo function call used during a type assert. It is used during interface
|
||||
// lowering, to assign the lowest type numbers to the types with the most type
|
||||
// asserts. Also, it is replaced with const false if this type assert can never
|
||||
// happen.
|
||||
func typeAssert(actualType uintptr, assertedType *uint8) bool
|
||||
func typeAssert(actualType unsafe.Pointer, assertedType *uint8) bool
|
||||
|
||||
Reference in New Issue
Block a user