diff --git a/src/internal/reflectlite/deepequal.go b/src/internal/reflectlite/deepequal.go new file mode 100644 index 0000000000..436dc007f8 --- /dev/null +++ b/src/internal/reflectlite/deepequal.go @@ -0,0 +1,196 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Deep equality test via reflection + +package reflectlite + +import "unsafe" + +// During deepValueEqual, must keep track of checks that are +// in progress. The comparison algorithm assumes that all +// checks in progress are true when it reencounters them. +// Visited comparisons are stored in a map indexed by visit. +type visit struct { + a1 unsafe.Pointer + a2 unsafe.Pointer + typ *RawType +} + +// Tests for deep equality using reflected types. The map argument tracks +// comparisons that have already been seen, which allows short circuiting on +// recursive types. +func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool { + if !v1.IsValid() || !v2.IsValid() { + return v1.IsValid() == v2.IsValid() + } + if v1.typecode != v2.typecode { + return false + } + + // We want to avoid putting more in the visited map than we need to. + // For any possible reference cycle that might be encountered, + // hard(v1, v2) needs to return true for at least one of the types in the cycle, + // and it's safe and valid to get Value's internal pointer. + hard := func(v1, v2 Value) bool { + switch v1.Kind() { + case Map, Slice, Ptr, Interface: + // Nil pointers cannot be cyclic. Avoid putting them in the visited map. + return !v1.IsNil() && !v2.IsNil() + } + return false + } + + if hard(v1, v2) { + addr1 := v1.pointer() + addr2 := v2.pointer() + if uintptr(addr1) > uintptr(addr2) { + // Canonicalize order to reduce number of entries in visited. + // Assumes non-moving garbage collector. + addr1, addr2 = addr2, addr1 + } + + // Short circuit if references are already seen. + v := visit{addr1, addr2, v1.typecode} + if _, ok := visited[v]; ok { + return true + } + + // Remember for later. + visited[v] = struct{}{} + } + + switch v1.Kind() { + case Array: + for i := 0; i < v1.Len(); i++ { + if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Slice: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.Len() != v2.Len() { + return false + } + if v1.UnsafePointer() == v2.UnsafePointer() { + return true + } + for i := 0; i < v1.Len(); i++ { + if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Interface: + if v1.IsNil() || v2.IsNil() { + return v1.IsNil() == v2.IsNil() + } + return deepValueEqual(v1.Elem(), v2.Elem(), visited) + case Ptr: + if v1.UnsafePointer() == v2.UnsafePointer() { + return true + } + return deepValueEqual(v1.Elem(), v2.Elem(), visited) + case Struct: + for i, n := 0, v1.NumField(); i < n; i++ { + if !deepValueEqual(v1.Field(i), v2.Field(i), visited) { + return false + } + } + return true + case Map: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.Len() != v2.Len() { + return false + } + if v1.UnsafePointer() == v2.UnsafePointer() { + return true + } + for _, k := range v1.MapKeys() { + val1 := v1.MapIndex(k) + val2 := v2.MapIndex(k) + if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) { + return false + } + } + return true + case Func: + if v1.IsNil() && v2.IsNil() { + return true + } + // Can't do better than this: + return false + default: + // Normal equality suffices + return valueInterfaceUnsafe(v1) == valueInterfaceUnsafe(v2) + } +} + +// DeepEqual reports whether x and y are “deeply equal”, defined as follows. +// Two values of identical type are deeply equal if one of the following cases applies. +// Values of distinct types are never deeply equal. +// +// Array values are deeply equal when their corresponding elements are deeply equal. +// +// Struct values are deeply equal if their corresponding fields, +// both exported and unexported, are deeply equal. +// +// Func values are deeply equal if both are nil; otherwise they are not deeply equal. +// +// Interface values are deeply equal if they hold deeply equal concrete values. +// +// Map values are deeply equal when all of the following are true: +// they are both nil or both non-nil, they have the same length, +// and either they are the same map object or their corresponding keys +// (matched using Go equality) map to deeply equal values. +// +// Pointer values are deeply equal if they are equal using Go's == operator +// or if they point to deeply equal values. +// +// Slice values are deeply equal when all of the following are true: +// they are both nil or both non-nil, they have the same length, +// and either they point to the same initial entry of the same underlying array +// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. +// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) +// are not deeply equal. +// +// Other values - numbers, bools, strings, and channels - are deeply equal +// if they are equal using Go's == operator. +// +// In general DeepEqual is a recursive relaxation of Go's == operator. +// However, this idea is impossible to implement without some inconsistency. +// Specifically, it is possible for a value to be unequal to itself, +// either because it is of func type (uncomparable in general) +// or because it is a floating-point NaN value (not equal to itself in floating-point comparison), +// or because it is an array, struct, or interface containing +// such a value. +// On the other hand, pointer values are always equal to themselves, +// even if they point at or contain such problematic values, +// because they compare equal using Go's == operator, and that +// is a sufficient condition to be deeply equal, regardless of content. +// DeepEqual has been defined so that the same short-cut applies +// to slices and maps: if x and y are the same slice or the same map, +// they are deeply equal regardless of content. +// +// As DeepEqual traverses the data values it may find a cycle. The +// second and subsequent times that DeepEqual compares two pointer +// values that have been compared before, it treats the values as +// equal rather than examining the values to which they point. +// This ensures that DeepEqual terminates. +func DeepEqual(x, y interface{}) bool { + if x == nil || y == nil { + return x == y + } + v1 := ValueOf(x) + v2 := ValueOf(y) + if v1.typecode != v2.typecode { + return false + } + return deepValueEqual(v1, v2, make(map[visit]struct{})) +} diff --git a/src/reflect/endian-big.go b/src/internal/reflectlite/endian_big.go similarity index 98% rename from src/reflect/endian-big.go rename to src/internal/reflectlite/endian_big.go index 94951e200d..5ad792dcc3 100644 --- a/src/reflect/endian-big.go +++ b/src/internal/reflectlite/endian_big.go @@ -1,6 +1,6 @@ //go:build mips -package reflect +package reflectlite import "unsafe" diff --git a/src/reflect/endian-little.go b/src/internal/reflectlite/endian_little.go similarity index 98% rename from src/reflect/endian-little.go rename to src/internal/reflectlite/endian_little.go index 7d7e30059d..035ec01d8b 100644 --- a/src/reflect/endian-little.go +++ b/src/internal/reflectlite/endian_little.go @@ -1,6 +1,6 @@ //go:build !mips -package reflect +package reflectlite import "unsafe" diff --git a/src/internal/reflectlite/reflect.go b/src/internal/reflectlite/reflect.go deleted file mode 100644 index 938e56a556..0000000000 --- a/src/internal/reflectlite/reflect.go +++ /dev/null @@ -1,51 +0,0 @@ -package reflectlite - -import "reflect" - -func Swapper(slice interface{}) func(i, j int) { - return reflect.Swapper(slice) -} - -type Kind = reflect.Kind -type Type = reflect.Type -type Value = reflect.Value - -const ( - Invalid Kind = reflect.Invalid - Bool Kind = reflect.Bool - Int Kind = reflect.Int - Int8 Kind = reflect.Int8 - Int16 Kind = reflect.Int16 - Int32 Kind = reflect.Int32 - Int64 Kind = reflect.Int64 - Uint Kind = reflect.Uint - Uint8 Kind = reflect.Uint8 - Uint16 Kind = reflect.Uint16 - Uint32 Kind = reflect.Uint32 - Uint64 Kind = reflect.Uint64 - Uintptr Kind = reflect.Uintptr - Float32 Kind = reflect.Float32 - Float64 Kind = reflect.Float64 - Complex64 Kind = reflect.Complex64 - Complex128 Kind = reflect.Complex128 - Array Kind = reflect.Array - Chan Kind = reflect.Chan - Func Kind = reflect.Func - Interface Kind = reflect.Interface - Map Kind = reflect.Map - Ptr Kind = reflect.Ptr - Slice Kind = reflect.Slice - String Kind = reflect.String - Struct Kind = reflect.Struct - UnsafePointer Kind = reflect.UnsafePointer -) - -func ValueOf(i interface{}) reflect.Value { - return reflect.ValueOf(i) -} - -func TypeOf(i interface{}) reflect.Type { - return reflect.TypeOf(i) -} - -type ValueError = reflect.ValueError diff --git a/src/reflect/strconv.go b/src/internal/reflectlite/strconv.go similarity index 99% rename from src/reflect/strconv.go rename to src/internal/reflectlite/strconv.go index 2d8131c9df..deabe4a5c7 100644 --- a/src/reflect/strconv.go +++ b/src/internal/reflectlite/strconv.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package reflect +package reflectlite import ( "unicode/utf8" diff --git a/src/internal/reflectlite/swapper.go b/src/internal/reflectlite/swapper.go new file mode 100644 index 0000000000..b8d85a9442 --- /dev/null +++ b/src/internal/reflectlite/swapper.go @@ -0,0 +1,40 @@ +package reflectlite + +import "unsafe" + +// Some of code here has been copied from the Go sources: +// https://github.com/golang/go/blob/go1.15.2/src/reflect/swapper.go +// It has the following copyright note: +// +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +func Swapper(slice interface{}) func(i, j int) { + v := ValueOf(slice) + if v.Kind() != Slice { + panic(&ValueError{Method: "Swapper"}) + } + + // Just return Nop func if nothing to swap. + if v.Len() < 2 { + return func(i, j int) {} + } + + typ := v.typecode.Elem() + size := typ.Size() + + header := (*sliceHeader)(v.value) + tmp := unsafe.Pointer(&make([]byte, size)[0]) + + return func(i, j int) { + if uint(i) >= uint(header.len) || uint(j) >= uint(header.len) { + panic("reflect: slice index out of range") + } + val1 := unsafe.Add(header.data, uintptr(i)*size) + val2 := unsafe.Add(header.data, uintptr(j)*size) + memcpy(tmp, val1, size) + memcpy(val1, val2, size) + memcpy(val2, tmp, size) + } +} diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go new file mode 100644 index 0000000000..10350676c6 --- /dev/null +++ b/src/internal/reflectlite/type.go @@ -0,0 +1,1130 @@ +package reflectlite + +import ( + "internal/gclayout" + "internal/itoa" + "unsafe" +) + +// 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 + structFieldFlagIsEmbedded +) + +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 + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + String + UnsafePointer + Chan + Interface + Pointer + Slice + Array + Func + Map + Struct +) + +// Ptr is the old name for the Pointer kind. +const Ptr = Pointer + +func (k Kind) String() string { + switch k { + case Invalid: + return "invalid" + case Bool: + return "bool" + case Int: + return "int" + case Int8: + return "int8" + case Int16: + return "int16" + case Int32: + return "int32" + case Int64: + return "int64" + case Uint: + return "uint" + case Uint8: + return "uint8" + case Uint16: + return "uint16" + case Uint32: + return "uint32" + case Uint64: + return "uint64" + case Uintptr: + return "uintptr" + case Float32: + return "float32" + case Float64: + return "float64" + case Complex64: + return "complex64" + case Complex128: + return "complex128" + case String: + return "string" + case UnsafePointer: + return "unsafe.Pointer" + case Chan: + return "chan" + case Interface: + return "interface" + case Pointer: + return "ptr" + case Slice: + return "slice" + case Array: + return "array" + case Func: + return "func" + case Map: + return "map" + case Struct: + return "struct" + default: + return "kind" + itoa.Itoa(int(int8(k))) + } +} + +// Copied from reflect/type.go +// https://go.dev/src/reflect/type.go?#L348 + +// ChanDir represents a channel type's direction. +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan +) + +// Type represents the minimal interface for a Go type. +type Type interface { + // These should match the reflectlite.Type implementation in Go. + AssignableTo(u Type) bool + Comparable() bool + Elem() Type + Implements(u Type) bool + Kind() Kind + Name() string + PkgPath() string + Size() uintptr + String() string + + // Additional methods shared with reflect.Type. + Align() int + Field(i int) StructField + Key() Type + Len() int + NumField() int + NumMethod() int +} + +// 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 + flagComparable = 64 // flag that is set if this type is comparable + flagIsBinary = 128 // flag that is set if this type uses the hashmap binary algorithm +) + +// The base type struct. All type structs start with this. +type RawType struct { + meta uint8 // metadata byte, contains kind and flags (see constants 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 + numMethod uint16 + ptrTo *RawType + elem *RawType +} + +type ptrType struct { + RawType + numMethod uint16 + elem *RawType +} + +type interfaceType struct { + RawType + ptrTo *RawType + // TODO: methods +} + +type arrayType struct { + RawType + numMethod uint16 + ptrTo *RawType + elem *RawType + arrayLen uintptr + slicePtr *RawType +} + +type mapType struct { + RawType + numMethod uint16 + ptrTo *RawType + elem *RawType + key *RawType +} + +type namedType struct { + RawType + numMethod uint16 + ptrTo *RawType + elem *RawType + pkg *byte + name [1]byte +} + +// 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 + numMethod uint16 + ptrTo *RawType + pkgpath *byte + size uint32 + numField uint16 + 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.isNamed() { + return (*elemType)(unsafe.Pointer(t)).elem + } + return t +} + +func (t *RawType) ptrtag() uintptr { + return uintptr(unsafe.Pointer(t)) & 0b11 +} + +func (t *RawType) isNamed() bool { + if tag := t.ptrtag(); tag != 0 { + return false + } + return t.meta&flagNamed != 0 +} + +func TypeOf(i interface{}) Type { + if i == nil { + return nil + } + typecode, _ := decomposeInterface(i) + return (*RawType)(typecode) +} + +func PtrTo(t Type) Type { return PointerTo(t) } + +func PointerTo(t Type) Type { + return pointerTo(t.(*RawType)) +} + +func pointerTo(t *RawType) *RawType { + if t.isNamed() { + return (*elemType)(unsafe.Pointer(t)).ptrTo + } + + switch t.Kind() { + case Pointer: + if tag := t.ptrtag(); tag < 3 { + return (*RawType)(unsafe.Add(unsafe.Pointer(t), 1)) + } + + // TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131 + // We need to be able to create types that match existing types to prevent typecode equality. + panic("reflect: cannot make *****T type") + case Struct: + return (*structType)(unsafe.Pointer(t)).ptrTo + default: + return (*elemType)(unsafe.Pointer(t)).ptrTo + } +} + +func (t *RawType) String() string { + if t.isNamed() { + s := t.name() + if s[0] == '.' { + return s[1:] + } + return s + } + switch t.Kind() { + case Chan: + elem := t.elem().String() + switch t.ChanDir() { + case SendDir: + return "chan<- " + elem + case RecvDir: + return "<-chan " + elem + case BothDir: + if elem[0] == '<' { + // typ is recv chan, need parentheses as "<-" associates with leftmost + // chan possible, see: + // * https://golang.org/ref/spec#Channel_types + // * https://github.com/golang/go/issues/39897 + return "chan (" + elem + ")" + } + return "chan " + elem + } + + case Pointer: + return "*" + t.elem().String() + case Slice: + return "[]" + t.elem().String() + case Array: + return "[" + itoa.Itoa(t.Len()) + "]" + t.elem().String() + case Map: + return "map[" + t.key().String() + "]" + t.elem().String() + case Struct: + numField := t.NumField() + if numField == 0 { + return "struct {}" + } + s := "struct {" + for i := 0; i < numField; i++ { + f := t.rawField(i) + s += " " + f.Name + " " + f.Type.String() + if f.Tag != "" { + s += " " + quote(string(f.Tag)) + } + // every field except the last needs a semicolon + if i < numField-1 { + s += ";" + } + } + s += " }" + return s + case Interface: + // TODO(dgryski): Needs actual method set info + return "interface {}" + default: + return t.Kind().String() + } + + return t.Kind().String() +} + +func (t *RawType) Kind() Kind { + if t == nil { + return Invalid + } + + if tag := t.ptrtag(); tag != 0 { + return Pointer + } + + return Kind(t.meta & kindMask) +} + +var ( + errTypeElem = &TypeError{"Elem"} + errTypeKey = &TypeError{"Key"} + errTypeField = &TypeError{"Field"} + errTypeBits = &TypeError{"Bits"} + errTypeLen = &TypeError{"Len"} + errTypeNumField = &TypeError{"NumField"} + errTypeChanDir = &TypeError{"ChanDir"} + errTypeFieldByName = &TypeError{"FieldByName"} + errTypeFieldByIndex = &TypeError{"FieldByIndex"} +) + +// 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 { + return t.elem() +} + +func (t *RawType) elem() *RawType { + if tag := t.ptrtag(); tag != 0 { + return (*RawType)(unsafe.Add(unsafe.Pointer(t), -1)) + } + + underlying := t.underlying() + switch underlying.Kind() { + case Pointer: + return (*ptrType)(unsafe.Pointer(underlying)).elem + case Chan, Slice, Array, Map: + return (*elemType)(unsafe.Pointer(underlying)).elem + default: + panic(errTypeElem) + } +} + +func (t *RawType) key() *RawType { + underlying := t.underlying() + if underlying.Kind() != Map { + panic(errTypeKey) + } + return (*mapType)(unsafe.Pointer(underlying)).key +} + +// 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 { + field := t.rawField(i) + return StructField{ + Name: field.Name, + PkgPath: field.PkgPath, + Type: field.Type, // note: converts RawType to Type + Tag: field.Tag, + Anonymous: field.Anonymous, + Offset: field.Offset, + Index: []int{i}, + } +} + +func rawStructFieldFromPointer(descriptor *structType, fieldType *RawType, data unsafe.Pointer, flagsByte uint8, name string, offset uint32) rawStructField { + // 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. + pkgPath = readStringZ(unsafe.Pointer(descriptor.pkgpath)) + } + + return rawStructField{ + Name: name, + PkgPath: pkgPath, + Type: fieldType, + Tag: StructTag(tag), + Anonymous: flagsByte&structFieldFlagAnonymous != 0, + Offset: uintptr(offset), + } +} + +// rawField returns nearly the same value as Field but without converting the +// Type member to an interface. +// +// For internal use only. +func (t *RawType) rawField(n int) rawStructField { + if t.Kind() != Struct { + panic(errTypeField) + } + descriptor := (*structType)(unsafe.Pointer(t.underlying())) + if uint(n) >= uint(descriptor.numField) { + panic("reflect: field index out of range") + } + + // 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 := (*structField)(unsafe.Add(unsafe.Pointer(&descriptor.fields[0]), uintptr(n)*unsafe.Sizeof(structField{}))) + 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) + offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32)) + data = unsafe.Add(data, lenOffs) + + name := readStringZ(data) + data = unsafe.Add(data, len(name)) + + return rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset) +} + +// rawFieldByNameFunc returns nearly the same value as FieldByNameFunc but without converting the +// Type member to an interface. +// +// For internal use only. +func (t *RawType) rawFieldByNameFunc(match func(string) bool) (rawStructField, []int, bool) { + if t.Kind() != Struct { + panic(errTypeField) + } + + type fieldWalker struct { + t *RawType + index []int + } + + queue := make([]fieldWalker, 0, 4) + queue = append(queue, fieldWalker{t, nil}) + + for len(queue) > 0 { + type result struct { + r rawStructField + index []int + } + + var found []result + var nextlevel []fieldWalker + + // For all the structs at this level.. + for _, ll := range queue { + // Iterate over all the fields looking for the matching name + // Also calculate field offset. + + descriptor := (*structType)(unsafe.Pointer(ll.t.underlying())) + field := &descriptor.fields[0] + + for i := uint16(0); i < descriptor.numField; i++ { + 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) + + offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32)) + data = unsafe.Add(data, lenOffs) + + name := readStringZ(data) + data = unsafe.Add(data, len(name)) + if match(name) { + found = append(found, result{ + rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset), + append(ll.index[:len(ll.index):len(ll.index)], int(i)), + }) + } + + structOrPtrToStruct := field.fieldType.Kind() == Struct || (field.fieldType.Kind() == Pointer && field.fieldType.elem().Kind() == Struct) + if flagsByte&structFieldFlagIsEmbedded == structFieldFlagIsEmbedded && structOrPtrToStruct { + embedded := field.fieldType + if embedded.Kind() == Pointer { + embedded = embedded.elem() + } + + nextlevel = append(nextlevel, fieldWalker{ + t: embedded, + index: append(ll.index[:len(ll.index):len(ll.index)], int(i)), + }) + } + + // update offset/field pointer if there *is* a next field + if i < descriptor.numField-1 { + // Increment pointer to the next field. + field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{}))) + } + } + } + + // found multiple hits at this level + if len(found) > 1 { + return rawStructField{}, nil, false + } + + // found the field we were looking for + if len(found) == 1 { + r := found[0] + return r.r, r.index, true + } + + // else len(found) == 0, move on to the next level + queue = append(queue[:0], nextlevel...) + } + + // didn't find it + return rawStructField{}, nil, false +} + +// 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 { + kind := t.Kind() + if kind >= Int && kind <= Complex128 { + return int(t.Size()) * 8 + } + panic(errTypeBits) +} + +// Len returns the number of elements in this array. It panics of the type kind +// is not Array. +func (t *RawType) Len() int { + if t.Kind() != Array { + panic(errTypeLen) + } + + 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 { + if t.Kind() != Struct { + panic(errTypeNumField) + } + 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 { + switch t.Kind() { + case Bool, Int8, Uint8: + return 1 + case Int16, Uint16: + return 2 + case Int32, Uint32: + return 4 + case Int64, Uint64: + return 8 + case Int, Uint: + return unsafe.Sizeof(int(0)) + case Uintptr: + return unsafe.Sizeof(uintptr(0)) + case Float32: + return 4 + case Float64: + return 8 + case Complex64: + return 8 + case Complex128: + return 16 + case String: + return unsafe.Sizeof("") + case UnsafePointer, Chan, Map, Pointer: + return unsafe.Sizeof(uintptr(0)) + case Slice: + return unsafe.Sizeof([]int{}) + case Interface: + return unsafe.Sizeof(interface{}(nil)) + case Func: + var f func() + return unsafe.Sizeof(f) + case Array: + return t.elem().Size() * uintptr(t.Len()) + case Struct: + u := t.underlying() + return uintptr((*structType)(unsafe.Pointer(u)).size) + default: + panic("unimplemented: size of type") + } +} + +// Align returns the alignment of this type. It is similar to calling +// unsafe.Alignof. +func (t *RawType) Align() int { + switch t.Kind() { + case Bool, Int8, Uint8: + return int(unsafe.Alignof(int8(0))) + case Int16, Uint16: + return int(unsafe.Alignof(int16(0))) + case Int32, Uint32: + return int(unsafe.Alignof(int32(0))) + case Int64, Uint64: + return int(unsafe.Alignof(int64(0))) + case Int, Uint: + return int(unsafe.Alignof(int(0))) + case Uintptr: + return int(unsafe.Alignof(uintptr(0))) + case Float32: + return int(unsafe.Alignof(float32(0))) + case Float64: + return int(unsafe.Alignof(float64(0))) + case Complex64: + return int(unsafe.Alignof(complex64(0))) + case Complex128: + return int(unsafe.Alignof(complex128(0))) + case String: + return int(unsafe.Alignof("")) + case UnsafePointer, Chan, Map, Pointer: + return int(unsafe.Alignof(uintptr(0))) + case Slice: + return int(unsafe.Alignof([]int(nil))) + case Interface: + return int(unsafe.Alignof(interface{}(nil))) + case Func: + var f func() + return int(unsafe.Alignof(f)) + case Struct: + numField := t.NumField() + alignment := 1 + for i := 0; i < numField; i++ { + fieldAlignment := t.rawField(i).Type.Align() + if fieldAlignment > alignment { + alignment = fieldAlignment + } + } + return alignment + case Array: + return t.elem().Align() + default: + panic("unimplemented: alignment of type") + } +} + +func (r *RawType) gcLayout() unsafe.Pointer { + kind := r.Kind() + + if kind < String { + return gclayout.NoPtrs + } + + switch kind { + case Pointer, UnsafePointer, Chan, Map: + return gclayout.Pointer + case String: + return gclayout.String + case Slice: + return gclayout.Slice + } + + // Unknown (for now); let the conservative pointer scanning handle it + return nil +} + +// 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 { + 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) { + return true + } + + if t.underlying() == u.(*RawType).underlying() && (!t.isNamed() || !u.(*RawType).isNamed()) { + return true + } + + if u.Kind() == Interface && u.NumMethod() == 0 { + return true + } + + if u.Kind() == Interface { + panic("reflect: unimplemented: AssignableTo with interface") + } + return false +} + +func (t *RawType) Implements(u Type) bool { + if u.Kind() != Interface { + panic("reflect: non-interface type passed to Type.Implements") + } + return t.AssignableTo(u) +} + +// Comparable returns whether values of this type can be compared to each other. +func (t *RawType) Comparable() bool { + return (t.meta & flagComparable) == flagComparable +} + +// isBinary returns if the hashmapAlgorithmBinary functions can be used on this type +func (t *RawType) isBinary() bool { + return (t.meta & flagIsBinary) == flagIsBinary +} + +func (t *RawType) ChanDir() ChanDir { + if t.Kind() != Chan { + panic(errTypeChanDir) + } + + dir := int((*elemType)(unsafe.Pointer(t)).numMethod) + + // nummethod is overloaded for channel to store channel direction + return ChanDir(dir) +} + +func (t *RawType) NumMethod() int { + + if t.isNamed() { + return int((*namedType)(unsafe.Pointer(t)).numMethod) + } + + switch t.Kind() { + case Pointer: + return int((*ptrType)(unsafe.Pointer(t)).numMethod) + case Struct: + return int((*structType)(unsafe.Pointer(t)).numMethod) + case Interface: + //FIXME: Use len(methods) + return (*interfaceType)(unsafe.Pointer(t)).ptrTo.NumMethod() + } + + // Other types have no methods attached. Note we don't panic here. + return 0 +} + +// Read and return a null terminated string starting from data. +func readStringZ(data unsafe.Pointer) string { + start := data + var len uintptr + for *(*byte)(data) != 0 { + len++ + data = unsafe.Add(data, 1) // C: data++ + } + + return *(*string)(unsafe.Pointer(&stringHeader{ + data: start, + len: len, + })) +} + +func (t *RawType) name() string { + ntype := (*namedType)(unsafe.Pointer(t)) + return readStringZ(unsafe.Pointer(&ntype.name[0])) +} + +func (t *RawType) Name() string { + if t.isNamed() { + name := t.name() + for i := 0; i < len(name); i++ { + if name[i] == '.' { + return name[i+1:] + } + } + panic("corrupt name data") + } + + if kind := t.Kind(); kind < UnsafePointer { + return t.Kind().String() + } else if kind == UnsafePointer { + return "Pointer" + } + + return "" +} + +func (t *RawType) Key() Type { + return t.key() +} + +// OverflowComplex reports whether the complex128 x cannot be represented by type t. +// It panics if t's Kind is not Complex64 or Complex128. +func (t RawType) OverflowComplex(x complex128) bool { + k := t.Kind() + switch k { + case Complex64: + return overflowFloat32(real(x)) || overflowFloat32(imag(x)) + case Complex128: + return false + } + panic("reflect: OverflowComplex of non-complex type") +} + +// OverflowFloat reports whether the float64 x cannot be represented by type t. +// It panics if t's Kind is not Float32 or Float64. +func (t RawType) OverflowFloat(x float64) bool { + k := t.Kind() + switch k { + case Float32: + return overflowFloat32(x) + case Float64: + return false + } + panic("reflect: OverflowFloat of non-float type") +} + +// OverflowInt reports whether the int64 x cannot be represented by type t. +// It panics if t's Kind is not Int, Int8, Int16, Int32, or Int64. +func (t RawType) OverflowInt(x int64) bool { + k := t.Kind() + switch k { + case Int, Int8, Int16, Int32, Int64: + bitSize := t.Size() * 8 + trunc := (x << (64 - bitSize)) >> (64 - bitSize) + return x != trunc + } + panic("reflect: OverflowInt of non-int type") +} + +// OverflowUint reports whether the uint64 x cannot be represented by type t. +// It panics if t's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. +func (t RawType) OverflowUint(x uint64) bool { + k := t.Kind() + switch k { + case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: + bitSize := t.Size() * 8 + trunc := (x << (64 - bitSize)) >> (64 - bitSize) + return x != trunc + } + panic("reflect: OverflowUint of non-uint type") +} + +func (t *RawType) PkgPath() string { + if t.isNamed() { + ntype := (*namedType)(unsafe.Pointer(t)) + return readStringZ(unsafe.Pointer(ntype.pkg)) + } + + return "" +} + +func (t *RawType) FieldByName(name string) (StructField, bool) { + if t.Kind() != Struct { + panic(errTypeFieldByName) + } + + field, index, ok := t.rawFieldByNameFunc(func(n string) bool { return n == name }) + if !ok { + return StructField{}, false + } + + return StructField{ + Name: field.Name, + PkgPath: field.PkgPath, + Type: field.Type, // note: converts RawType to Type + Tag: field.Tag, + Anonymous: field.Anonymous, + Offset: field.Offset, + Index: index, + }, true +} + +func (t *RawType) FieldByNameFunc(match func(string) bool) (StructField, bool) { + if t.Kind() != Struct { + panic(TypeError{"FieldByNameFunc"}) + } + + field, index, ok := t.rawFieldByNameFunc(match) + if !ok { + return StructField{}, false + } + + return StructField{ + Name: field.Name, + PkgPath: field.PkgPath, + Type: field.Type, // note: converts RawType to Type + Tag: field.Tag, + Anonymous: field.Anonymous, + Offset: field.Offset, + Index: index, + }, true +} + +func (t *RawType) FieldByIndex(index []int) StructField { + ftype := t + var field rawStructField + + for _, n := range index { + structOrPtrToStruct := ftype.Kind() == Struct || (ftype.Kind() == Pointer && ftype.elem().Kind() == Struct) + if !structOrPtrToStruct { + panic(errTypeFieldByIndex) + } + + if ftype.Kind() == Pointer { + ftype = ftype.elem() + } + + field = ftype.rawField(n) + ftype = field.Type + } + + return StructField{ + Name: field.Name, + PkgPath: field.PkgPath, + Type: field.Type, // note: converts RawType to Type + Tag: field.Tag, + Anonymous: field.Anonymous, + Offset: field.Offset, + Index: index, + } +} + +// A StructField describes a single field in a struct. +// This must be kept in sync with [reflect.StructField]. +type StructField struct { + // Name indicates the field name. + Name string + + // PkgPath is the package path where the struct containing this field is + // declared for unexported fields, or the empty string for exported fields. + PkgPath string + + Type Type + Tag StructTag // field tag string + Offset uintptr + Index []int // index sequence for Type.FieldByIndex + Anonymous bool +} + +// IsExported reports whether the field is exported. +func (f StructField) IsExported() bool { + return f.PkgPath == "" +} + +// rawStructField is the same as StructField but with the Type member replaced +// with RawType. For internal use only. Avoiding this conversion to the Type +// interface improves code size in many cases. +type rawStructField struct { + Name string + PkgPath string + Type *RawType + Tag StructTag + Offset uintptr + Anonymous bool +} + +// A StructTag is the tag string in a struct field. +type StructTag string + +// TODO: it would be feasible to do the key/value splitting at compile time, +// avoiding the code size cost of doing it at runtime + +// Get returns the value associated with key in the tag string. +func (tag StructTag) Get(key string) string { + v, _ := tag.Lookup(key) + return v +} + +// Lookup returns the value associated with key in the tag string. +func (tag StructTag) Lookup(key string) (value string, ok bool) { + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if key == name { + value, err := unquote(qvalue) + if err != nil { + break + } + return value, true + } + } + return "", false +} + +// TypeError is the error that is used in a panic when invoking a method on a +// type that is not applicable to that type. +type TypeError struct { + Method string +} + +func (e *TypeError) Error() string { + return "reflect: call of reflect.Type." + e.Method + " on invalid type" +} + +func align(offset uintptr, alignment uintptr) uintptr { + return (offset + alignment - 1) &^ (alignment - 1) +} + +func SliceOf(t Type) Type { + panic("unimplemented: reflect.SliceOf()") +} + +func ArrayOf(n int, t Type) Type { + panic("unimplemented: reflect.ArrayOf()") +} + +func StructOf([]StructField) Type { + panic("unimplemented: reflect.StructOf()") +} + +func MapOf(key, value Type) Type { + panic("unimplemented: reflect.MapOf()") +} + +func FuncOf(in, out []Type, variadic bool) Type { + panic("unimplemented: reflect.FuncOf()") +} + +const maxVarintLen32 = 5 + +// encoding/binary.Uvarint, specialized for uint32 +func uvarint32(buf []byte) (uint32, int) { + var x uint32 + var s uint + for i, b := range buf { + if b < 0x80 { + return x | uint32(b)< unsafe.Sizeof(uintptr(0)) { + return int64(*(*int)(v.value)) + } else { + return int64(int(uintptr(v.value))) + } + case Int8: + if v.isIndirect() { + return int64(*(*int8)(v.value)) + } else { + return int64(int8(uintptr(v.value))) + } + case Int16: + if v.isIndirect() { + return int64(*(*int16)(v.value)) + } else { + return int64(int16(uintptr(v.value))) + } + case Int32: + if v.isIndirect() || unsafe.Sizeof(int32(0)) > unsafe.Sizeof(uintptr(0)) { + return int64(*(*int32)(v.value)) + } else { + return int64(int32(uintptr(v.value))) + } + case Int64: + if v.isIndirect() || unsafe.Sizeof(int64(0)) > unsafe.Sizeof(uintptr(0)) { + return int64(*(*int64)(v.value)) + } else { + return int64(int64(uintptr(v.value))) + } + default: + panic(&ValueError{Method: "Int", Kind: v.Kind()}) + } +} + +// CanUint reports whether Uint can be used without panicking. +func (v Value) CanUint() bool { + switch v.Kind() { + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return true + default: + return false + } +} + +func (v Value) Uint() uint64 { + switch v.Kind() { + case Uintptr: + if v.isIndirect() { + return uint64(*(*uintptr)(v.value)) + } else { + return uint64(uintptr(v.value)) + } + case Uint8: + if v.isIndirect() { + return uint64(*(*uint8)(v.value)) + } else { + return uint64(uintptr(v.value)) + } + case Uint16: + if v.isIndirect() { + return uint64(*(*uint16)(v.value)) + } else { + return uint64(uintptr(v.value)) + } + case Uint: + if v.isIndirect() || unsafe.Sizeof(uint(0)) > unsafe.Sizeof(uintptr(0)) { + return uint64(*(*uint)(v.value)) + } else { + return uint64(uintptr(v.value)) + } + case Uint32: + if v.isIndirect() || unsafe.Sizeof(uint32(0)) > unsafe.Sizeof(uintptr(0)) { + return uint64(*(*uint32)(v.value)) + } else { + return uint64(uintptr(v.value)) + } + case Uint64: + if v.isIndirect() || unsafe.Sizeof(uint64(0)) > unsafe.Sizeof(uintptr(0)) { + return uint64(*(*uint64)(v.value)) + } else { + return uint64(uintptr(v.value)) + } + default: + panic(&ValueError{Method: "Uint", Kind: v.Kind()}) + } +} + +// CanFloat reports whether Float can be used without panicking. +func (v Value) CanFloat() bool { + switch v.Kind() { + case Float32, Float64: + return true + default: + return false + } +} + +func (v Value) Float32() float32 { + switch v.Kind() { + case Float32: + if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) { + // The float is stored as an external value on systems with 16-bit + // pointers. + return *(*float32)(v.value) + } else { + // The float is directly stored in the interface value on systems + // with 32-bit and 64-bit pointers. + return *(*float32)(unsafe.Pointer(&v.value)) + } + + case Float64: + return float32(v.Float()) + + } + + panic(&ValueError{Method: "Float", Kind: v.Kind()}) +} + +func (v Value) Float() float64 { + switch v.Kind() { + case Float32: + if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) { + // The float is stored as an external value on systems with 16-bit + // pointers. + return float64(*(*float32)(v.value)) + } else { + // The float is directly stored in the interface value on systems + // with 32-bit and 64-bit pointers. + return float64(*(*float32)(unsafe.Pointer(&v.value))) + } + case Float64: + if v.isIndirect() || unsafe.Sizeof(float64(0)) > unsafe.Sizeof(uintptr(0)) { + // For systems with 16-bit and 32-bit pointers. + return *(*float64)(v.value) + } else { + // The float is directly stored in the interface value on systems + // with 64-bit pointers. + return *(*float64)(unsafe.Pointer(&v.value)) + } + default: + panic(&ValueError{Method: "Float", Kind: v.Kind()}) + } +} + +// CanComplex reports whether Complex can be used without panicking. +func (v Value) CanComplex() bool { + switch v.Kind() { + case Complex64, Complex128: + return true + default: + return false + } +} + +func (v Value) Complex() complex128 { + switch v.Kind() { + case Complex64: + if v.isIndirect() || unsafe.Sizeof(complex64(0)) > unsafe.Sizeof(uintptr(0)) { + // The complex number is stored as an external value on systems with + // 16-bit and 32-bit pointers. + return complex128(*(*complex64)(v.value)) + } else { + // The complex number is directly stored in the interface value on + // systems with 64-bit pointers. + return complex128(*(*complex64)(unsafe.Pointer(&v.value))) + } + case Complex128: + // This is a 128-bit value, which is always stored as an external value. + // It may be stored in the pointer directly on very uncommon + // architectures with 128-bit pointers, however. + return *(*complex128)(v.value) + default: + panic(&ValueError{Method: "Complex", Kind: v.Kind()}) + } +} + +func (v Value) String() string { + switch v.Kind() { + case String: + // A string value is always bigger than a pointer as it is made of a + // pointer and a length. + return *(*string)(v.value) + default: + // Special case because of the special treatment of .String() in Go. + return "<" + v.typecode.String() + " Value>" + } +} + +func (v Value) Bytes() []byte { + switch v.Kind() { + case Slice: + if v.typecode.elem().Kind() != Uint8 { + panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) + } + return *(*[]byte)(v.value) + + case Array: + v.checkAddressable() + + if v.typecode.elem().Kind() != Uint8 { + panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) + } + + // Small inline arrays are not addressable, so we only have to + // handle addressable arrays which will be stored as pointers + // in v.value + return unsafe.Slice((*byte)(v.value), v.Len()) + } + + panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) +} + +func (v Value) Slice(i, j int) Value { + switch v.Kind() { + case Slice: + hdr := *(*sliceHeader)(v.value) + i, j := uintptr(i), uintptr(j) + + if j < i || hdr.cap < j { + slicePanic() + } + + elemSize := v.typecode.underlying().elem().Size() + + hdr.len = j - i + hdr.cap = hdr.cap - i + hdr.data = unsafe.Add(hdr.data, i*elemSize) + + return Value{ + typecode: v.typecode, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + + case Array: + v.checkAddressable() + buf, length := buflen(v) + i, j := uintptr(i), uintptr(j) + if j < i || length < j { + slicePanic() + } + + elemSize := v.typecode.underlying().elem().Size() + + var hdr sliceHeader + hdr.len = j - i + hdr.cap = length - i + hdr.data = unsafe.Add(buf, i*elemSize) + + sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr + return Value{ + typecode: sliceType, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + + case String: + i, j := uintptr(i), uintptr(j) + str := *(*stringHeader)(v.value) + + if j < i || str.len < j { + slicePanic() + } + + hdr := stringHeader{ + data: unsafe.Add(str.data, i), + len: j - i, + } + + return Value{ + typecode: v.typecode, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + } + + panic(&ValueError{Method: "Slice", Kind: v.Kind()}) +} + +func (v Value) Slice3(i, j, k int) Value { + switch v.Kind() { + case Slice: + hdr := *(*sliceHeader)(v.value) + i, j, k := uintptr(i), uintptr(j), uintptr(k) + + if j < i || k < j || hdr.len < k { + slicePanic() + } + + elemSize := v.typecode.underlying().elem().Size() + + hdr.len = j - i + hdr.cap = k - i + hdr.data = unsafe.Add(hdr.data, i*elemSize) + + return Value{ + typecode: v.typecode, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + + case Array: + v.checkAddressable() + buf, length := buflen(v) + i, j, k := uintptr(i), uintptr(j), uintptr(k) + if j < i || k < j || length < k { + slicePanic() + } + + elemSize := v.typecode.underlying().elem().Size() + + var hdr sliceHeader + hdr.len = j - i + hdr.cap = k - i + hdr.data = unsafe.Add(buf, i*elemSize) + + sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr + return Value{ + typecode: sliceType, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + } + + panic("unimplemented: (reflect.Value).Slice3()") +} + +//go:linkname maplen runtime.hashmapLen +func maplen(p unsafe.Pointer) int + +//go:linkname chanlen runtime.chanLen +func chanlen(p unsafe.Pointer) int + +// Len returns the length of this value for slices, strings, arrays, channels, +// and maps. For other types, it panics. +func (v Value) Len() int { + switch v.typecode.Kind() { + case Array: + return v.typecode.Len() + case Chan: + return chanlen(v.pointer()) + case Map: + return maplen(v.pointer()) + case Slice: + return int((*sliceHeader)(v.value).len) + case String: + return int((*stringHeader)(v.value).len) + default: + panic(&ValueError{Method: "Len", Kind: v.Kind()}) + } +} + +//go:linkname chancap runtime.chanCap +func chancap(p unsafe.Pointer) int + +// Cap returns the capacity of this value for arrays, channels and slices. +// For other types, it panics. +func (v Value) Cap() int { + switch v.typecode.Kind() { + case Array: + return v.typecode.Len() + case Chan: + return chancap(v.pointer()) + case Slice: + return int((*sliceHeader)(v.value).cap) + default: + panic(&ValueError{Method: "Cap", Kind: v.Kind()}) + } +} + +//go:linkname mapclear runtime.hashmapClear +func mapclear(p unsafe.Pointer) + +// Clear clears the contents of a map or zeros the contents of a slice +// +// It panics if v's Kind is not Map or Slice. +func (v Value) Clear() { + switch v.typecode.Kind() { + case Map: + mapclear(v.pointer()) + case Slice: + hdr := (*sliceHeader)(v.value) + elemSize := v.typecode.underlying().elem().Size() + memzero(hdr.data, elemSize*hdr.len) + default: + panic(&ValueError{Method: "Clear", Kind: v.Kind()}) + } +} + +// NumField returns the number of fields of this struct. It panics for other +// value types. +func (v Value) NumField() int { + return v.typecode.NumField() +} + +func (v Value) Elem() Value { + switch v.Kind() { + case Ptr: + ptr := v.pointer() + if ptr == nil { + return Value{} + } + // Don't copy RO flags + flags := (v.flags & (valueFlagIndirect | valueFlagExported)) | valueFlagIndirect + return Value{ + typecode: v.typecode.elem(), + value: ptr, + flags: flags, + } + case Interface: + typecode, value := decomposeInterface(*(*interface{})(v.value)) + return Value{ + typecode: (*RawType)(typecode), + value: value, + flags: v.flags &^ valueFlagIndirect, + } + default: + panic(&ValueError{Method: "Elem", Kind: v.Kind()}) + } +} + +// Field returns the value of the i'th field of this struct. +func (v Value) Field(i int) Value { + if v.Kind() != Struct { + panic(&ValueError{Method: "Field", Kind: v.Kind()}) + } + structField := v.typecode.rawField(i) + + // Copy flags but clear EmbedRO; we're not an embedded field anymore + flags := v.flags & ^valueFlagEmbedRO + if structField.PkgPath != "" { + // No PkgPath => not exported. + // Clear exported flag even if the parent was exported. + flags &^= valueFlagExported + + // Update the RO flag + if structField.Anonymous { + // Embedded field + flags |= valueFlagEmbedRO + } else { + flags |= valueFlagStickyRO + } + } else { + // Parent field may not have been exported but we are + flags |= valueFlagExported + } + + size := v.typecode.Size() + fieldType := structField.Type + fieldSize := fieldType.Size() + if v.isIndirect() || fieldSize > unsafe.Sizeof(uintptr(0)) { + // v.value was already a pointer to the value and it should stay that + // way. + return Value{ + flags: flags, + typecode: fieldType, + value: unsafe.Add(v.value, structField.Offset), + } + } + + // The fieldSize is smaller than uintptr, which means that the value will + // have to be stored directly in the interface value. + + if fieldSize == 0 { + // The struct field is zero sized. + // This is a rare situation, but because it's undefined behavior + // to shift the size of the value (zeroing the value), handle this + // situation explicitly. + return Value{ + flags: flags, + typecode: fieldType, + value: unsafe.Pointer(nil), + } + } + + if size > unsafe.Sizeof(uintptr(0)) { + // The value was not stored in the interface before but will be + // afterwards, so load the value (from the correct offset) and return + // it. + ptr := unsafe.Add(v.value, structField.Offset) + value := unsafe.Pointer(loadValue(ptr, fieldSize)) + return Value{ + flags: flags &^ valueFlagIndirect, + typecode: fieldType, + 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. + value := maskAndShift(uintptr(v.value), structField.Offset, fieldSize) + return Value{ + flags: flags, + typecode: fieldType, + value: unsafe.Pointer(value), + } +} + +var uint8Type = TypeOf(uint8(0)).(*RawType) + +func (v Value) Index(i int) Value { + switch v.Kind() { + case Slice: + // Extract an element from the slice. + slice := *(*sliceHeader)(v.value) + if uint(i) >= uint(slice.len) { + panic("reflect: slice index out of range") + } + flags := (v.flags & (valueFlagExported | valueFlagIndirect)) | valueFlagIndirect | v.flags.ro() + elem := Value{ + typecode: v.typecode.elem(), + flags: flags, + } + elem.value = unsafe.Add(slice.data, elem.typecode.Size()*uintptr(i)) // pointer to new value + return elem + case String: + // Extract a character from a string. + // A string is never stored directly in the interface, but always as a + // pointer to the string value. + // Keeping valueFlagExported if set, but don't set valueFlagIndirect + // otherwise CanSet will return true for string elements (which is bad, + // strings are read-only). + s := *(*stringHeader)(v.value) + if uint(i) >= uint(s.len) { + panic("reflect: string index out of range") + } + return Value{ + typecode: uint8Type, + value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))), + flags: v.flags & valueFlagExported, + } + case Array: + // Extract an element from the array. + elemType := v.typecode.elem() + elemSize := elemType.Size() + size := v.typecode.Size() + if size == 0 { + // The element size is 0 and/or the length of the array is 0. + return Value{ + typecode: v.typecode.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 := unsafe.Add(v.value, elemSize*uintptr(i)) // pointer to new value + return Value{ + typecode: v.typecode.elem(), + flags: v.flags, + value: addr, + } + } + + if size > unsafe.Sizeof(uintptr(0)) || v.isIndirect() { + // The element fits in a pointer, but the array is not stored in the pointer directly. + // Load the value from the pointer. + addr := unsafe.Add(v.value, elemSize*uintptr(i)) // pointer to new value + value := addr + if !v.isIndirect() { + // Use a pointer to the value (don't load the value) if the + // 'indirect' flag is set. + value = unsafe.Pointer(loadValue(addr, elemSize)) + } + return Value{ + typecode: v.typecode.elem(), + flags: v.flags, + value: value, + } + } + + // 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.typecode.elem(), + flags: v.flags, + value: unsafe.Pointer(value), + } + default: + panic(&ValueError{Method: "Index", Kind: v.Kind()}) + } +} + +func (v Value) NumMethod() int { + if v.typecode == nil { + panic(&ValueError{Method: "reflect.Value.NumMethod", Kind: Invalid}) + } + return v.typecode.NumMethod() +} + +// OverflowFloat reports whether the float64 x cannot be represented by v's type. +// It panics if v's Kind is not Float32 or Float64. +func (v Value) OverflowFloat(x float64) bool { + k := v.Kind() + switch k { + case Float32: + return overflowFloat32(x) + case Float64: + return false + } + panic(&ValueError{Method: "reflect.Value.OverflowFloat", Kind: v.Kind()}) +} + +func overflowFloat32(x float64) bool { + if x < 0 { + x = -x + } + return math.MaxFloat32 < x && x <= math.MaxFloat64 +} + +func (v Value) MapKeys() []Value { + if v.Kind() != Map { + panic(&ValueError{Method: "MapKeys", Kind: v.Kind()}) + } + + // empty map + if v.Len() == 0 { + return nil + } + + keys := make([]Value, 0, v.Len()) + + it := hashmapNewIterator() + k := New(v.typecode.Key()) + e := New(v.typecode.Elem()) + + keyType := v.typecode.key() + keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0 + shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary() + + for hashmapNext(v.pointer(), it, k.value, e.value) { + if shouldUnpackInterface { + intf := *(*interface{})(k.value) + v := ValueOf(intf) + keys = append(keys, v) + } else { + keys = append(keys, k.Elem()) + } + k = New(v.typecode.Key()) + } + + return keys +} + +//go:linkname hashmapStringGet runtime.hashmapStringGet +func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool + +//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet +func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool + +//go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet +func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool + +func (v Value) MapIndex(key Value) Value { + if v.Kind() != Map { + panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) + } + + vkey := v.typecode.key() + + // compare key type with actual key type of map + if !key.typecode.AssignableTo(vkey) { + // type error? + panic("reflect.Value.MapIndex: incompatible types for key") + } + + elemType := v.typecode.Elem() + elem := New(elemType) + + if vkey.Kind() == String { + if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok { + return Value{} + } + return elem.Elem() + } else if vkey.isBinary() { + var keyptr unsafe.Pointer + if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + keyptr = key.value + } else { + keyptr = unsafe.Pointer(&key.value) + } + //TODO(dgryski): zero out padding bytes in key, if any + if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok { + return Value{} + } + return elem.Elem() + } else { + if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok { + return Value{} + } + return elem.Elem() + } +} + +//go:linkname hashmapNewIterator runtime.hashmapNewIterator +func hashmapNewIterator() unsafe.Pointer + +//go:linkname hashmapNext runtime.hashmapNext +func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool + +func (v Value) MapRange() *MapIter { + if v.Kind() != Map { + panic(&ValueError{Method: "MapRange", Kind: v.Kind()}) + } + + keyType := v.typecode.key() + + keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0 + shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary() + + return &MapIter{ + m: v, + it: hashmapNewIterator(), + unpackKeyInterface: shouldUnpackInterface, + } +} + +type MapIter struct { + m Value + it unsafe.Pointer + key Value + val Value + + valid bool + unpackKeyInterface bool +} + +func (it *MapIter) Key() Value { + if !it.valid { + panic("reflect.MapIter.Key called on invalid iterator") + } + + if it.unpackKeyInterface { + intf := *(*interface{})(it.key.value) + v := ValueOf(intf) + return v + } + + return it.key.Elem() +} + +func (it *MapIter) Value() Value { + if !it.valid { + panic("reflect.MapIter.Value called on invalid iterator") + } + + return it.val.Elem() +} + +func (it *MapIter) Next() bool { + it.key = New(it.m.typecode.Key()) + it.val = New(it.m.typecode.Elem()) + + it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value) + return it.valid +} + +func (v Value) Set(x Value) { + v.checkAddressable() + v.checkRO() + if !x.typecode.AssignableTo(v.typecode) { + panic("reflect.Value.Set: value of type " + x.typecode.String() + " cannot be assigned to type " + v.typecode.String()) + } + + if v.typecode.Kind() == Interface && x.typecode.Kind() != Interface { + // move the value of x back into the interface, if possible + if x.isIndirect() && x.typecode.Size() <= unsafe.Sizeof(uintptr(0)) { + x.value = unsafe.Pointer(loadValue(x.value, x.typecode.Size())) + } + + intf := composeInterface(unsafe.Pointer(x.typecode), x.value) + x = Value{ + typecode: v.typecode, + value: unsafe.Pointer(&intf), + } + } + + size := v.typecode.Size() + if size <= unsafe.Sizeof(uintptr(0)) && !x.isIndirect() { + storeValue(v.value, size, uintptr(x.value)) + } else { + memcpy(v.value, x.value, size) + } +} + +func (v Value) SetZero() { + v.checkAddressable() + v.checkRO() + size := v.typecode.Size() + memzero(v.value, size) +} + +func (v Value) SetBool(x bool) { + v.checkAddressable() + v.checkRO() + switch v.Kind() { + case Bool: + *(*bool)(v.value) = x + default: + panic(&ValueError{Method: "SetBool", Kind: v.Kind()}) + } +} + +func (v Value) SetInt(x int64) { + v.checkAddressable() + v.checkRO() + switch v.Kind() { + case Int: + *(*int)(v.value) = int(x) + case Int8: + *(*int8)(v.value) = int8(x) + case Int16: + *(*int16)(v.value) = int16(x) + case Int32: + *(*int32)(v.value) = int32(x) + case Int64: + *(*int64)(v.value) = x + default: + panic(&ValueError{Method: "SetInt", Kind: v.Kind()}) + } +} + +func (v Value) SetUint(x uint64) { + v.checkAddressable() + v.checkRO() + switch v.Kind() { + case Uint: + *(*uint)(v.value) = uint(x) + case Uint8: + *(*uint8)(v.value) = uint8(x) + case Uint16: + *(*uint16)(v.value) = uint16(x) + case Uint32: + *(*uint32)(v.value) = uint32(x) + case Uint64: + *(*uint64)(v.value) = x + case Uintptr: + *(*uintptr)(v.value) = uintptr(x) + default: + panic(&ValueError{Method: "SetUint", Kind: v.Kind()}) + } +} + +func (v Value) SetFloat(x float64) { + v.checkAddressable() + v.checkRO() + switch v.Kind() { + case Float32: + *(*float32)(v.value) = float32(x) + case Float64: + *(*float64)(v.value) = x + default: + panic(&ValueError{Method: "SetFloat", Kind: v.Kind()}) + } +} + +func (v Value) SetComplex(x complex128) { + v.checkAddressable() + v.checkRO() + switch v.Kind() { + case Complex64: + *(*complex64)(v.value) = complex64(x) + case Complex128: + *(*complex128)(v.value) = x + default: + panic(&ValueError{Method: "SetComplex", Kind: v.Kind()}) + } +} + +func (v Value) SetString(x string) { + v.checkAddressable() + v.checkRO() + switch v.Kind() { + case String: + *(*string)(v.value) = x + default: + panic(&ValueError{Method: "SetString", Kind: v.Kind()}) + } +} + +func (v Value) SetBytes(x []byte) { + v.checkAddressable() + v.checkRO() + if v.typecode.Kind() != Slice || v.typecode.elem().Kind() != Uint8 { + panic("reflect.Value.SetBytes called on not []byte") + } + + // copy the header contents over + *(*[]byte)(v.value) = x +} + +func (v Value) SetCap(n int) { + panic("unimplemented: (reflect.Value).SetCap()") +} + +func (v Value) SetLen(n int) { + if v.typecode.Kind() != Slice { + panic(&ValueError{Method: "reflect.Value.SetLen", Kind: v.Kind()}) + } + v.checkAddressable() + hdr := (*sliceHeader)(v.value) + if int(uintptr(n)) != n || uintptr(n) > hdr.cap { + panic("reflect.Value.SetLen: slice length out of range") + } + hdr.len = uintptr(n) +} + +func (v Value) checkAddressable() { + if !v.isIndirect() { + panic("reflect: value is not addressable") + } +} + +// OverflowInt reports whether the int64 x cannot be represented by v's type. +// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. +func (v Value) OverflowInt(x int64) bool { + switch v.Kind() { + case Int, Int8, Int16, Int32, Int64: + bitSize := v.typecode.Size() * 8 + trunc := (x << (64 - bitSize)) >> (64 - bitSize) + return x != trunc + } + panic(&ValueError{Method: "reflect.Value.OverflowInt", Kind: v.Kind()}) +} + +// OverflowUint reports whether the uint64 x cannot be represented by v's type. +// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. +func (v Value) OverflowUint(x uint64) bool { + k := v.Kind() + switch k { + case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: + bitSize := v.typecode.Size() * 8 + trunc := (x << (64 - bitSize)) >> (64 - bitSize) + return x != trunc + } + panic(&ValueError{Method: "reflect.Value.OverflowUint", Kind: v.Kind()}) +} + +func (v Value) CanConvert(t Type) bool { + // TODO: Optimize this to not actually perform a conversion + _, ok := convertOp(v, t) + return ok +} + +func (v Value) Convert(t Type) Value { + if v, ok := convertOp(v, t); ok { + return v + } + + panic("reflect.Value.Convert: value of type " + v.typecode.String() + " cannot be converted to type " + t.String()) +} + +func convertOp(src Value, typ Type) (Value, bool) { + + // Easy check first. Do we even need to do anything? + if src.typecode.underlying() == typ.(*RawType).underlying() { + return Value{ + typecode: typ.(*RawType), + value: src.value, + flags: src.flags, + }, true + } + + if rtype := typ.(*RawType); rtype.Kind() == Interface && rtype.NumMethod() == 0 { + iface := composeInterface(unsafe.Pointer(src.typecode), src.value) + return Value{ + typecode: rtype, + value: unsafe.Pointer(&iface), + flags: valueFlagExported, + }, true + } + + switch src.Kind() { + case Int, Int8, Int16, Int32, Int64: + switch rtype := typ.(*RawType); rtype.Kind() { + case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return cvtInt(src, rtype), true + case Float32, Float64: + return cvtIntFloat(src, rtype), true + case String: + return cvtIntString(src, rtype), true + } + + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + switch rtype := typ.(*RawType); rtype.Kind() { + case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return cvtUint(src, rtype), true + case Float32, Float64: + return cvtUintFloat(src, rtype), true + case String: + return cvtUintString(src, rtype), true + } + + case Float32, Float64: + switch rtype := typ.(*RawType); rtype.Kind() { + case Int, Int8, Int16, Int32, Int64: + return cvtFloatInt(src, rtype), true + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return cvtFloatUint(src, rtype), true + case Float32, Float64: + return cvtFloat(src, rtype), true + } + + /* + case Complex64, Complex128: + switch src.Kind() { + case Complex64, Complex128: + return cvtComplex + } + */ + + case Slice: + switch rtype := typ.(*RawType); rtype.Kind() { + case Array: + if src.typecode.elem() == rtype.elem() && rtype.Len() <= src.Len() { + return Value{ + typecode: rtype, + value: (*sliceHeader)(src.value).data, + flags: src.flags | valueFlagIndirect, + }, true + } + case Pointer: + if rtype.Elem().Kind() == Array { + if src.typecode.elem() == rtype.elem().elem() && rtype.elem().Len() <= src.Len() { + return Value{ + typecode: rtype, + value: (*sliceHeader)(src.value).data, + flags: src.flags & (valueFlagExported | valueFlagRO), + }, true + } + } + case String: + if !src.typecode.elem().isNamed() { + switch src.Type().Elem().Kind() { + case Uint8: + return cvtBytesString(src, rtype), true + case Int32: + return cvtRunesString(src, rtype), true + } + } + } + + case String: + rtype := typ.(*RawType) + if typ.Kind() == Slice && !rtype.elem().isNamed() { + switch typ.Elem().Kind() { + case Uint8: + return cvtStringBytes(src, rtype), true + case Int32: + return cvtStringRunes(src, rtype), true + } + } + } + + // TODO(dgryski): Unimplemented: + // Chan + // Non-defined pointers types with same underlying base type + // Interface <-> Type conversions + + return Value{}, false +} + +func cvtInt(v Value, t *RawType) Value { + return makeInt(v.flags, uint64(v.Int()), t) +} + +func cvtUint(v Value, t *RawType) Value { + return makeInt(v.flags, v.Uint(), t) +} + +func cvtIntFloat(v Value, t *RawType) Value { + return makeFloat(v.flags, float64(v.Int()), t) +} + +func cvtUintFloat(v Value, t *RawType) Value { + return makeFloat(v.flags, float64(v.Uint()), t) +} + +func cvtFloatInt(v Value, t *RawType) Value { + return makeInt(v.flags, uint64(int64(v.Float())), t) +} + +func cvtFloatUint(v Value, t *RawType) Value { + return makeInt(v.flags, uint64(v.Float()), t) +} + +func cvtFloat(v Value, t *RawType) Value { + if v.Type().Kind() == Float32 && t.Kind() == Float32 { + // Don't do any conversion if both types have underlying type float32. + // This avoids converting to float64 and back, which will + // convert a signaling NaN to a quiet NaN. See issue 36400. + return makeFloat32(v.flags, v.Float32(), t) + } + return makeFloat(v.flags, v.Float(), t) +} + +//go:linkname stringToBytes runtime.stringToBytes +func stringToBytes(x string) []byte + +func cvtStringBytes(v Value, t *RawType) Value { + b := stringToBytes(*(*string)(v.value)) + return Value{ + typecode: t, + value: unsafe.Pointer(&b), + flags: v.flags, + } +} + +//go:linkname stringFromBytes runtime.stringFromBytes +func stringFromBytes(x []byte) string + +func cvtBytesString(v Value, t *RawType) Value { + s := stringFromBytes(*(*[]byte)(v.value)) + return Value{ + typecode: t, + value: unsafe.Pointer(&s), + flags: v.flags, + } +} + +func makeInt(flags valueFlags, bits uint64, t *RawType) Value { + size := t.Size() + + v := Value{ + typecode: t, + flags: flags, + } + + ptr := unsafe.Pointer(&v.value) + if size > unsafe.Sizeof(uintptr(0)) { + ptr = alloc(size, nil) + v.value = ptr + } + + switch size { + case 1: + *(*uint8)(ptr) = uint8(bits) + case 2: + *(*uint16)(ptr) = uint16(bits) + case 4: + *(*uint32)(ptr) = uint32(bits) + case 8: + *(*uint64)(ptr) = bits + } + return v +} + +func makeFloat(flags valueFlags, f float64, t *RawType) Value { + size := t.Size() + + v := Value{ + typecode: t, + flags: flags, + } + + ptr := unsafe.Pointer(&v.value) + if size > unsafe.Sizeof(uintptr(0)) { + ptr = alloc(size, nil) + v.value = ptr + } + + switch size { + case 4: + *(*float32)(ptr) = float32(f) + case 8: + *(*float64)(ptr) = f + } + return v +} + +func makeFloat32(flags valueFlags, f float32, t *RawType) Value { + v := Value{ + typecode: t, + flags: flags, + } + *(*float32)(unsafe.Pointer(&v.value)) = float32(f) + return v +} + +func cvtIntString(src Value, t *RawType) Value { + panic("cvtUintString: unimplemented") +} + +func cvtUintString(src Value, t *RawType) Value { + panic("cvtUintString: unimplemented") +} + +func cvtStringRunes(src Value, t *RawType) Value { + panic("cvsStringRunes: unimplemented") +} + +func cvtRunesString(src Value, t *RawType) Value { + panic("cvsRunesString: unimplemented") +} + +//go:linkname slicePanic runtime.slicePanic +func slicePanic() + +func MakeSlice(typ Type, len, cap int) Value { + if typ.Kind() != Slice { + panic("reflect.MakeSlice of non-slice type") + } + + rtype := typ.(*RawType) + + ulen := uint(len) + ucap := uint(cap) + maxSize := (^uintptr(0)) / 2 + elem := rtype.elem() + elementSize := elem.Size() + if elementSize > 1 { + maxSize /= uintptr(elementSize) + } + if ulen > ucap || ucap > uint(maxSize) { + slicePanic() + } + + // This can't overflow because of the above checks. + size := uintptr(ucap) * elementSize + + var slice sliceHeader + slice.cap = uintptr(ucap) + slice.len = uintptr(ulen) + layout := elem.gcLayout() + + slice.data = alloc(size, layout) + + return Value{ + typecode: rtype, + value: unsafe.Pointer(&slice), + flags: valueFlagExported, + } +} + +var zerobuffer unsafe.Pointer + +const zerobufferLen = 32 + +func init() { + // 32 characters of zero bytes + zerobufferStr := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + s := (*stringHeader)(unsafe.Pointer(&zerobufferStr)) + zerobuffer = s.data +} + +func Zero(typ Type) Value { + size := typ.Size() + if size <= unsafe.Sizeof(uintptr(0)) { + return Value{ + typecode: typ.(*RawType), + value: nil, + flags: valueFlagExported | valueFlagRO, + } + } + + if size <= zerobufferLen { + return Value{ + typecode: typ.(*RawType), + value: unsafe.Pointer(zerobuffer), + flags: valueFlagExported | valueFlagRO, + } + } + + return Value{ + typecode: typ.(*RawType), + value: alloc(size, nil), + flags: valueFlagExported | valueFlagRO, + } +} + +// New is the reflect equivalent of the new(T) keyword, returning a pointer to a +// new value of the given type. +func New(typ Type) Value { + return Value{ + typecode: pointerTo(typ.(*RawType)), + value: alloc(typ.Size(), nil), + flags: valueFlagExported, + } +} + +type funcHeader struct { + Context unsafe.Pointer + Code unsafe.Pointer +} + +// Slice header that matches the underlying structure. Used for when we switch +// to a precise GC, which needs to know exactly where pointers live. +type sliceHeader struct { + data unsafe.Pointer + len uintptr + cap uintptr +} + +// Like sliceHeader, this type is used internally to make sure pointer and +// non-pointer fields match those of actual strings. +type stringHeader struct { + data unsafe.Pointer + len uintptr +} + +// Verify SliceHeader and StringHeader sizes. +// See https://github.com/tinygo-org/tinygo/pull/4156 +// and https://github.com/tinygo-org/tinygo/issues/1284. +var ( + _ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{} + _ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(stringHeader{})]byte{} +) + +type ValueError struct { + Method string + Kind Kind +} + +func (e *ValueError) Error() string { + if e.Kind == 0 { + return "reflect: call of " + e.Method + " on zero Value" + } + return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value" +} + +//go:linkname memcpy runtime.memcpy +func memcpy(dst, src unsafe.Pointer, size uintptr) + +//go:linkname memzero runtime.memzero +func memzero(ptr unsafe.Pointer, size uintptr) + +//go:linkname alloc runtime.alloc +func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer + +//go:linkname sliceAppend runtime.sliceAppend +func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) + +//go:linkname sliceCopy runtime.sliceCopy +func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int + +// Copy copies the contents of src into dst until either +// dst has been filled or src has been exhausted. +func Copy(dst, src Value) int { + compatibleTypes := false || + // dst and src are both slices or arrays with equal types + ((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) && + (src.typecode.Kind() == Slice || src.typecode.Kind() == Array) && + (dst.typecode.elem() == src.typecode.elem())) || + // dst is array or slice of uint8 and src is string + ((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) && + dst.typecode.elem().Kind() == Uint8 && + src.typecode.Kind() == String) + + if !compatibleTypes { + panic("Copy: type mismatch: " + dst.typecode.String() + "/" + src.typecode.String()) + } + + // Can read from an unaddressable array but not write to one. + if dst.typecode.Kind() == Array && !dst.isIndirect() { + panic("reflect.Copy: unaddressable array value") + } + + dstbuf, dstlen := buflen(dst) + srcbuf, srclen := buflen(src) + + if srclen > 0 { + dst.checkRO() + } + + return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size()) +} + +func buflen(v Value) (unsafe.Pointer, uintptr) { + var buf unsafe.Pointer + var len uintptr + switch v.typecode.Kind() { + case Slice: + hdr := (*sliceHeader)(v.value) + buf = hdr.data + len = hdr.len + case Array: + if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + buf = v.value + } else { + buf = unsafe.Pointer(&v.value) + } + len = uintptr(v.Len()) + case String: + hdr := (*stringHeader)(v.value) + buf = hdr.data + len = hdr.len + default: + // This shouldn't happen + panic("reflect.Copy: not slice or array or string") + } + + return buf, len +} + +//go:linkname sliceGrow runtime.sliceGrow +func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) + +// extend slice to hold n new elements +func extendSlice(v Value, n int) sliceHeader { + if v.Kind() != Slice { + panic(&ValueError{Method: "extendSlice", Kind: v.Kind()}) + } + + var old sliceHeader + if v.value != nil { + old = *(*sliceHeader)(v.value) + } + + nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size()) + + return sliceHeader{ + data: nbuf, + len: nlen + uintptr(n), + cap: ncap, + } +} + +// Append appends the values x to a slice s and returns the resulting slice. +// As in Go, each x's value must be assignable to the slice's element type. +func Append(v Value, x ...Value) Value { + if v.Kind() != Slice { + panic(&ValueError{Method: "Append", Kind: v.Kind()}) + } + oldLen := v.Len() + newslice := extendSlice(v, len(x)) + v.flags = valueFlagExported + v.value = (unsafe.Pointer)(&newslice) + for i, xx := range x { + v.Index(oldLen + i).Set(xx) + } + return v +} + +// AppendSlice appends a slice t to a slice s and returns the resulting slice. +// The slices s and t must have the same element type. +func AppendSlice(s, t Value) Value { + if s.typecode.Kind() != Slice || t.typecode.Kind() != Slice || s.typecode != t.typecode { + // Not a very helpful error message, but shortened to just one error to + // keep code size down. + panic("reflect.AppendSlice: invalid types") + } + if !s.isExported() || !t.isExported() { + // One of the sides was not exported, so can't access the data. + panic("reflect.AppendSlice: unexported") + } + sSlice := (*sliceHeader)(s.value) + tSlice := (*sliceHeader)(t.value) + elemSize := s.typecode.elem().Size() + ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize) + result := &sliceHeader{ + data: ptr, + len: len, + cap: cap, + } + return Value{ + typecode: s.typecode, + value: unsafe.Pointer(result), + flags: valueFlagExported, + } +} + +// Grow increases the slice's capacity, if necessary, to guarantee space for +// another n elements. After Grow(n), at least n elements can be appended +// to the slice without another allocation. +// +// It panics if v's Kind is not a Slice or if n is negative or too large to +// allocate the memory. +func (v Value) Grow(n int) { + v.checkAddressable() + if n < 0 { + panic("reflect.Grow: negative length") + } + if v.Kind() != Slice { + panic(&ValueError{Method: "Grow", Kind: v.Kind()}) + } + slice := (*sliceHeader)(v.value) + newslice := extendSlice(v, n) + // Only copy the new data and cap: the len remains unchanged. + slice.data = newslice.data + slice.cap = newslice.cap +} + +//go:linkname hashmapStringSet runtime.hashmapStringSet +func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) + +//go:linkname hashmapBinarySet runtime.hashmapBinarySet +func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer) + +//go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet +func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, value unsafe.Pointer) + +//go:linkname hashmapStringDelete runtime.hashmapStringDelete +func hashmapStringDelete(m unsafe.Pointer, key string) + +//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete +func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer) + +//go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete +func hashmapInterfaceDelete(m unsafe.Pointer, key interface{}) + +func (v Value) SetMapIndex(key, elem Value) { + v.checkRO() + if v.Kind() != Map { + panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()}) + } + + vkey := v.typecode.key() + + // compare key type with actual key type of map + if !key.typecode.AssignableTo(vkey) { + panic("reflect.Value.SetMapIndex: incompatible types for key") + } + + // if elem is the zero Value, it means delete + del := elem == Value{} + + if !del && !elem.typecode.AssignableTo(v.typecode.elem()) { + panic("reflect.Value.SetMapIndex: incompatible types for value") + } + + // make elem an interface if it needs to be converted + if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface { + intf := composeInterface(unsafe.Pointer(elem.typecode), elem.value) + elem = Value{ + typecode: v.typecode.elem(), + value: unsafe.Pointer(&intf), + } + } + + if key.Kind() == String { + if del { + hashmapStringDelete(v.pointer(), *(*string)(key.value)) + } else { + var elemptr unsafe.Pointer + if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + elemptr = elem.value + } else { + elemptr = unsafe.Pointer(&elem.value) + } + hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr) + } + + } else if key.typecode.isBinary() { + var keyptr unsafe.Pointer + if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + keyptr = key.value + } else { + keyptr = unsafe.Pointer(&key.value) + } + + if del { + hashmapBinaryDelete(v.pointer(), keyptr) + } else { + var elemptr unsafe.Pointer + if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + elemptr = elem.value + } else { + elemptr = unsafe.Pointer(&elem.value) + } + hashmapBinarySet(v.pointer(), keyptr, elemptr) + } + } else { + if del { + hashmapInterfaceDelete(v.pointer(), key.Interface()) + } else { + var elemptr unsafe.Pointer + if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + elemptr = elem.value + } else { + elemptr = unsafe.Pointer(&elem.value) + } + + hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr) + } + } +} + +// FieldByIndex returns the nested field corresponding to index. +func (v Value) FieldByIndex(index []int) Value { + if len(index) == 1 { + return v.Field(index[0]) + } + if v.Kind() != Struct { + panic(&ValueError{"FieldByIndex", v.Kind()}) + } + for i, x := range index { + if i > 0 { + if v.Kind() == Pointer && v.typecode.elem().Kind() == Struct { + if v.IsNil() { + panic("reflect: indirection through nil pointer to embedded struct") + } + v = v.Elem() + } + } + v = v.Field(x) + } + return v +} + +// FieldByIndexErr returns the nested field corresponding to index. +func (v Value) FieldByIndexErr(index []int) (Value, error) { + return Value{}, &ValueError{Method: "FieldByIndexErr"} +} + +func (v Value) FieldByName(name string) Value { + if v.Kind() != Struct { + panic(&ValueError{"FieldByName", v.Kind()}) + } + + if field, ok := v.typecode.FieldByName(name); ok { + return v.FieldByIndex(field.Index) + } + return Value{} +} + +func (v Value) FieldByNameFunc(match func(string) bool) Value { + if v.Kind() != Struct { + panic(&ValueError{"FieldByName", v.Kind()}) + } + + if field, ok := v.typecode.FieldByNameFunc(match); ok { + return v.FieldByIndex(field.Index) + } + return Value{} +} + +//go:linkname hashmapMake runtime.hashmapMake +func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer + +// MakeMapWithSize creates a new map with the specified type and initial space +// for approximately n elements. +func MakeMapWithSize(typ Type, n int) Value { + + // TODO(dgryski): deduplicate these? runtime and reflect both need them. + const ( + hashmapAlgorithmBinary uint8 = iota + hashmapAlgorithmString + hashmapAlgorithmInterface + ) + + if typ.Kind() != Map { + panic(&ValueError{Method: "MakeMap", Kind: typ.Kind()}) + } + + if n < 0 { + panic("reflect.MakeMapWithSize: negative size hint") + } + + key := typ.Key().(*RawType) + val := typ.Elem().(*RawType) + + var alg uint8 + + if key.Kind() == String { + alg = hashmapAlgorithmString + } else if key.isBinary() { + alg = hashmapAlgorithmBinary + } else { + alg = hashmapAlgorithmInterface + } + + m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg) + + return Value{ + typecode: typ.(*RawType), + value: m, + flags: valueFlagExported, + } +} + +// MakeMap creates a new map with the specified type. +func MakeMap(typ Type) Value { + return MakeMapWithSize(typ, 8) +} + +func (v Value) Call(in []Value) []Value { + panic("unimplemented: (reflect.Value).Call()") +} + +func (v Value) CallSlice(in []Value) []Value { + panic("unimplemented: (reflect.Value).CallSlice()") +} + +func (v Value) Method(i int) Value { + panic("unimplemented: (reflect.Value).Method()") +} + +func (v Value) MethodByName(name string) Value { + panic("unimplemented: (reflect.Value).MethodByName()") +} + +func (v Value) Recv() (x Value, ok bool) { + panic("unimplemented: (reflect.Value).Recv()") +} + +func NewAt(typ Type, p unsafe.Pointer) Value { + panic("unimplemented: reflect.New()") +} diff --git a/src/internal/reflectlite/visiblefields.go b/src/internal/reflectlite/visiblefields.go new file mode 100644 index 0000000000..b21af6178f --- /dev/null +++ b/src/internal/reflectlite/visiblefields.go @@ -0,0 +1,105 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflectlite + +// VisibleFields returns all the visible fields in t, which must be a +// struct type. A field is defined as visible if it's accessible +// directly with a FieldByName call. The returned fields include fields +// inside anonymous struct members and unexported fields. They follow +// the same order found in the struct, with anonymous fields followed +// immediately by their promoted fields. +// +// For each element e of the returned slice, the corresponding field +// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index). +func VisibleFields(t Type) []StructField { + if t == nil { + panic("reflect: VisibleFields(nil)") + } + if t.Kind() != Struct { + panic("reflect.VisibleFields of non-struct type") + } + w := &visibleFieldsWalker{ + byName: make(map[string]int), + visiting: make(map[Type]bool), + fields: make([]StructField, 0, t.NumField()), + index: make([]int, 0, 2), + } + w.walk(t) + // Remove all the fields that have been hidden. + // Use an in-place removal that avoids copying in + // the common case that there are no hidden fields. + j := 0 + for i := range w.fields { + f := &w.fields[i] + if f.Name == "" { + continue + } + if i != j { + // A field has been removed. We need to shuffle + // all the subsequent elements up. + w.fields[j] = *f + } + j++ + } + return w.fields[:j] +} + +type visibleFieldsWalker struct { + byName map[string]int + visiting map[Type]bool + fields []StructField + index []int +} + +// walk walks all the fields in the struct type t, visiting +// fields in index preorder and appending them to w.fields +// (this maintains the required ordering). +// Fields that have been overridden have their +// Name field cleared. +func (w *visibleFieldsWalker) walk(t Type) { + if w.visiting[t] { + return + } + w.visiting[t] = true + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + w.index = append(w.index, i) + add := true + if oldIndex, ok := w.byName[f.Name]; ok { + old := &w.fields[oldIndex] + if len(w.index) == len(old.Index) { + // Fields with the same name at the same depth + // cancel one another out. Set the field name + // to empty to signify that has happened, and + // there's no need to add this field. + old.Name = "" + add = false + } else if len(w.index) < len(old.Index) { + // The old field loses because it's deeper than the new one. + old.Name = "" + } else { + // The old field wins because it's shallower than the new one. + add = false + } + } + if add { + // Copy the index so that it's not overwritten + // by the other appends. + f.Index = append([]int(nil), w.index...) + w.byName[f.Name] = len(w.fields) + w.fields = append(w.fields, f) + } + if f.Anonymous { + if f.Type.Kind() == Pointer { + f.Type = f.Type.Elem() + } + if f.Type.Kind() == Struct { + w.walk(f.Type) + } + } + w.index = w.index[:len(w.index)-1] + } + delete(w.visiting, t) +} diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go index 18a728458c..362385aed8 100644 --- a/src/reflect/deepequal.go +++ b/src/reflect/deepequal.go @@ -1,196 +1,7 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Deep equality test via reflection - package reflect -import "unsafe" - -// During deepValueEqual, must keep track of checks that are -// in progress. The comparison algorithm assumes that all -// checks in progress are true when it reencounters them. -// Visited comparisons are stored in a map indexed by visit. -type visit struct { - a1 unsafe.Pointer - a2 unsafe.Pointer - typ *rawType -} - -// Tests for deep equality using reflected types. The map argument tracks -// comparisons that have already been seen, which allows short circuiting on -// recursive types. -func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool { - if !v1.IsValid() || !v2.IsValid() { - return v1.IsValid() == v2.IsValid() - } - if v1.typecode != v2.typecode { - return false - } - - // We want to avoid putting more in the visited map than we need to. - // For any possible reference cycle that might be encountered, - // hard(v1, v2) needs to return true for at least one of the types in the cycle, - // and it's safe and valid to get Value's internal pointer. - hard := func(v1, v2 Value) bool { - switch v1.Kind() { - case Map, Slice, Ptr, Interface: - // Nil pointers cannot be cyclic. Avoid putting them in the visited map. - return !v1.IsNil() && !v2.IsNil() - } - return false - } - - if hard(v1, v2) { - addr1 := v1.pointer() - addr2 := v2.pointer() - if uintptr(addr1) > uintptr(addr2) { - // Canonicalize order to reduce number of entries in visited. - // Assumes non-moving garbage collector. - addr1, addr2 = addr2, addr1 - } - - // Short circuit if references are already seen. - v := visit{addr1, addr2, v1.typecode} - if _, ok := visited[v]; ok { - return true - } - - // Remember for later. - visited[v] = struct{}{} - } - - switch v1.Kind() { - case Array: - for i := 0; i < v1.Len(); i++ { - if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { - return false - } - } - return true - case Slice: - if v1.IsNil() != v2.IsNil() { - return false - } - if v1.Len() != v2.Len() { - return false - } - if v1.UnsafePointer() == v2.UnsafePointer() { - return true - } - for i := 0; i < v1.Len(); i++ { - if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { - return false - } - } - return true - case Interface: - if v1.IsNil() || v2.IsNil() { - return v1.IsNil() == v2.IsNil() - } - return deepValueEqual(v1.Elem(), v2.Elem(), visited) - case Ptr: - if v1.UnsafePointer() == v2.UnsafePointer() { - return true - } - return deepValueEqual(v1.Elem(), v2.Elem(), visited) - case Struct: - for i, n := 0, v1.NumField(); i < n; i++ { - if !deepValueEqual(v1.Field(i), v2.Field(i), visited) { - return false - } - } - return true - case Map: - if v1.IsNil() != v2.IsNil() { - return false - } - if v1.Len() != v2.Len() { - return false - } - if v1.UnsafePointer() == v2.UnsafePointer() { - return true - } - for _, k := range v1.MapKeys() { - val1 := v1.MapIndex(k) - val2 := v2.MapIndex(k) - if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) { - return false - } - } - return true - case Func: - if v1.IsNil() && v2.IsNil() { - return true - } - // Can't do better than this: - return false - default: - // Normal equality suffices - return valueInterfaceUnsafe(v1) == valueInterfaceUnsafe(v2) - } -} +import "internal/reflectlite" -// DeepEqual reports whether x and y are “deeply equal”, defined as follows. -// Two values of identical type are deeply equal if one of the following cases applies. -// Values of distinct types are never deeply equal. -// -// Array values are deeply equal when their corresponding elements are deeply equal. -// -// Struct values are deeply equal if their corresponding fields, -// both exported and unexported, are deeply equal. -// -// Func values are deeply equal if both are nil; otherwise they are not deeply equal. -// -// Interface values are deeply equal if they hold deeply equal concrete values. -// -// Map values are deeply equal when all of the following are true: -// they are both nil or both non-nil, they have the same length, -// and either they are the same map object or their corresponding keys -// (matched using Go equality) map to deeply equal values. -// -// Pointer values are deeply equal if they are equal using Go's == operator -// or if they point to deeply equal values. -// -// Slice values are deeply equal when all of the following are true: -// they are both nil or both non-nil, they have the same length, -// and either they point to the same initial entry of the same underlying array -// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. -// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) -// are not deeply equal. -// -// Other values - numbers, bools, strings, and channels - are deeply equal -// if they are equal using Go's == operator. -// -// In general DeepEqual is a recursive relaxation of Go's == operator. -// However, this idea is impossible to implement without some inconsistency. -// Specifically, it is possible for a value to be unequal to itself, -// either because it is of func type (uncomparable in general) -// or because it is a floating-point NaN value (not equal to itself in floating-point comparison), -// or because it is an array, struct, or interface containing -// such a value. -// On the other hand, pointer values are always equal to themselves, -// even if they point at or contain such problematic values, -// because they compare equal using Go's == operator, and that -// is a sufficient condition to be deeply equal, regardless of content. -// DeepEqual has been defined so that the same short-cut applies -// to slices and maps: if x and y are the same slice or the same map, -// they are deeply equal regardless of content. -// -// As DeepEqual traverses the data values it may find a cycle. The -// second and subsequent times that DeepEqual compares two pointer -// values that have been compared before, it treats the values as -// equal rather than examining the values to which they point. -// This ensures that DeepEqual terminates. func DeepEqual(x, y interface{}) bool { - if x == nil || y == nil { - return x == y - } - v1 := ValueOf(x) - v2 := ValueOf(y) - if v1.typecode != v2.typecode { - return false - } - return deepValueEqual(v1, v2, make(map[visit]struct{})) + return reflectlite.DeepEqual(x, y) } diff --git a/src/reflect/iter.go b/src/reflect/iter.go new file mode 100644 index 0000000000..4cc2df8fd9 --- /dev/null +++ b/src/reflect/iter.go @@ -0,0 +1,177 @@ +//go:build go1.23 + +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect + +import ( + "iter" +) + +func rangeNum[T int8 | int16 | int32 | int64 | int | + uint8 | uint16 | uint32 | uint64 | uint | + uintptr, N int64 | uint64](num N, t Type) iter.Seq[Value] { + return func(yield func(v Value) bool) { + convert := t.PkgPath() != "" + // cannot use range T(v) because no core type. + for i := T(0); i < T(num); i++ { + tmp := ValueOf(i) + // if the iteration value type is define by + // type T built-in type. + if convert { + tmp = tmp.Convert(t) + } + if !yield(tmp) { + return + } + } + } +} + +// Seq returns an iter.Seq[Value] that loops over the elements of v. +// If v's kind is Func, it must be a function that has no results and +// that takes a single argument of type func(T) bool for some type T. +// If v's kind is Pointer, the pointer element type must have kind Array. +// Otherwise v's kind must be Int, Int8, Int16, Int32, Int64, +// Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, +// Array, Chan, Map, Slice, or String. +func (v Value) Seq() iter.Seq[Value] { + // TODO: canRangeFunc + // if canRangeFunc(v.typ()) { + // return func(yield func(Value) bool) { + // rf := MakeFunc(v.Type().In(0), func(in []Value) []Value { + // return []Value{ValueOf(yield(in[0]))} + // }) + // v.Call([]Value{rf}) + // } + // } + switch v.Kind() { + case Int: + return rangeNum[int](v.Int(), v.Type()) + case Int8: + return rangeNum[int8](v.Int(), v.Type()) + case Int16: + return rangeNum[int16](v.Int(), v.Type()) + case Int32: + return rangeNum[int32](v.Int(), v.Type()) + case Int64: + return rangeNum[int64](v.Int(), v.Type()) + case Uint: + return rangeNum[uint](v.Uint(), v.Type()) + case Uint8: + return rangeNum[uint8](v.Uint(), v.Type()) + case Uint16: + return rangeNum[uint16](v.Uint(), v.Type()) + case Uint32: + return rangeNum[uint32](v.Uint(), v.Type()) + case Uint64: + return rangeNum[uint64](v.Uint(), v.Type()) + case Uintptr: + return rangeNum[uintptr](v.Uint(), v.Type()) + case Pointer: + if v.Elem().Kind() != Array { + break + } + return func(yield func(Value) bool) { + v = v.Elem() + for i := 0; i < v.Len(); i++ { + if !yield(ValueOf(i)) { + return + } + } + } + case Array, Slice: + return func(yield func(Value) bool) { + for i := 0; i < v.Len(); i++ { + if !yield(ValueOf(i)) { + return + } + } + } + case String: + return func(yield func(Value) bool) { + for i := range v.String() { + if !yield(ValueOf(i)) { + return + } + } + } + case Map: + return func(yield func(Value) bool) { + i := v.MapRange() + for i.Next() { + if !yield(i.Key()) { + return + } + } + } + case Chan: + return func(yield func(Value) bool) { + for value, ok := v.Recv(); ok; value, ok = v.Recv() { + if !yield(value) { + return + } + } + } + } + panic("reflect: " + v.Type().String() + " cannot produce iter.Seq[Value]") +} + +// Seq2 returns an iter.Seq2[Value, Value] that loops over the elements of v. +// If v's kind is Func, it must be a function that has no results and +// that takes a single argument of type func(K, V) bool for some type K, V. +// If v's kind is Pointer, the pointer element type must have kind Array. +// Otherwise v's kind must be Array, Map, Slice, or String. +func (v Value) Seq2() iter.Seq2[Value, Value] { + // TODO: canRangeFunc2 + // if canRangeFunc2(v.typ()) { + // return func(yield func(Value, Value) bool) { + // rf := MakeFunc(v.Type().In(0), func(in []Value) []Value { + // return []Value{ValueOf(yield(in[0], in[1]))} + // }) + // v.Call([]Value{rf}) + // } + // } + switch v.Kind() { + case Pointer: + if v.Elem().Kind() != Array { + break + } + return func(yield func(Value, Value) bool) { + v = v.Elem() + for i := 0; i < v.Len(); i++ { + if !yield(ValueOf(i), v.Index(i)) { + return + } + } + } + case Array, Slice: + return func(yield func(Value, Value) bool) { + for i := 0; i < v.Len(); i++ { + if !yield(ValueOf(i), v.Index(i)) { + return + } + } + } + case String: + return func(yield func(Value, Value) bool) { + for i, v := range v.String() { + if !yield(ValueOf(i), ValueOf(v)) { + return + } + } + } + case Map: + return func(yield func(Value, Value) bool) { + i := v.MapRange() + for i.Next() { + if !yield(i.Key(), i.Value()) { + return + } + } + } + } + panic("reflect: " + v.Type().String() + " cannot produce iter.Seq2[Value, Value]") +} diff --git a/src/reflect/iter_test.go b/src/reflect/iter_test.go new file mode 100644 index 0000000000..48c62c477a --- /dev/null +++ b/src/reflect/iter_test.go @@ -0,0 +1,416 @@ +//go:build go1.23 + +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect_test + +import ( + "iter" + "maps" + "reflect" + . "reflect" + "testing" +) + +type N int8 + +func TestValueSeq(t *testing.T) { + m := map[string]int{ + "1": 1, + "2": 2, + "3": 3, + "4": 4, + } + c := make(chan int, 3) + for i := range 3 { + c <- i + } + close(c) + tests := []struct { + name string + val Value + check func(*testing.T, iter.Seq[Value]) + }{ + {"int", ValueOf(4), func(t *testing.T, s iter.Seq[Value]) { + i := int64(0) + for v := range s { + if v.Int() != i { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"int8", ValueOf(int8(4)), func(t *testing.T, s iter.Seq[Value]) { + i := int8(0) + for v := range s { + if v.Interface().(int8) != i { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"uint", ValueOf(uint64(4)), func(t *testing.T, s iter.Seq[Value]) { + i := uint64(0) + for v := range s { + if v.Uint() != i { + t.Fatalf("got %d, want %d", v.Uint(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"uint8", ValueOf(uint8(4)), func(t *testing.T, s iter.Seq[Value]) { + i := uint8(0) + for v := range s { + if v.Interface().(uint8) != i { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"*[4]int", ValueOf(&[4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq[Value]) { + i := int64(0) + for v := range s { + if v.Int() != i { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"[4]int", ValueOf([4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq[Value]) { + i := int64(0) + for v := range s { + if v.Int() != i { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"[]int", ValueOf([]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq[Value]) { + i := int64(0) + for v := range s { + if v.Int() != i { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"string", ValueOf("12语言"), func(t *testing.T, s iter.Seq[Value]) { + i := int64(0) + indexes := []int64{0, 1, 2, 5} + for v := range s { + if v.Int() != indexes[i] { + t.Fatalf("got %d, want %d", v.Int(), indexes[i]) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"map[string]int", ValueOf(m), func(t *testing.T, s iter.Seq[Value]) { + copy := maps.Clone(m) + for v := range s { + if _, ok := copy[v.String()]; !ok { + t.Fatalf("unexpected %v", v.Interface()) + } + delete(copy, v.String()) + } + if len(copy) != 0 { + t.Fatalf("should loop four times") + } + }}, + // {"chan int", ValueOf(c), func(t *testing.T, s iter.Seq[Value]) { + // i := 0 + // m := map[int64]bool{ + // 0: false, + // 1: false, + // 2: false, + // } + // for v := range s { + // if b, ok := m[v.Int()]; !ok || b { + // t.Fatalf("unexpected %v", v.Interface()) + // } + // m[v.Int()] = true + // i++ + // } + // if i != 3 { + // t.Fatalf("should loop three times") + // } + // }}, + // {"func", ValueOf(func(yield func(int) bool) { + // for i := range 4 { + // if !yield(i) { + // return + // } + // } + // }), func(t *testing.T, s iter.Seq[Value]) { + // i := int64(0) + // for v := range s { + // if v.Int() != i { + // t.Fatalf("got %d, want %d", v.Int(), i) + // } + // i++ + // } + // if i != 4 { + // t.Fatalf("should loop four times") + // } + // }}, + // {"method", ValueOf(methodIter{}).MethodByName("Seq"), func(t *testing.T, s iter.Seq[Value]) { + // i := int64(0) + // for v := range s { + // if v.Int() != i { + // t.Fatalf("got %d, want %d", v.Int(), i) + // } + // i++ + // } + // if i != 4 { + // t.Fatalf("should loop four times") + // } + // }}, + {"type N int8", ValueOf(N(4)), func(t *testing.T, s iter.Seq[Value]) { + i := N(0) + for v := range s { + if v.Int() != int64(i) { + t.Fatalf("got %d, want %d", v.Int(), i) + } + i++ + if v.Type() != reflect.TypeOf(i) { + j := ValueOf(i) + t.Logf("ValueOf(j): %s", j.Type()) + t.Fatalf("got %s, want %s", v.Type(), reflect.TypeOf(i)) + } + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + } + for _, tc := range tests { + seq := tc.val.Seq() + tc.check(t, seq) + } +} + +func TestValueSeq2(t *testing.T) { + m := map[string]int{ + "1": 1, + "2": 2, + "3": 3, + "4": 4, + } + tests := []struct { + name string + val Value + check func(*testing.T, iter.Seq2[Value, Value]) + }{ + {"*[4]int", ValueOf(&[4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) { + i := int64(0) + for v1, v2 := range s { + if v1.Int() != i { + t.Fatalf("got %d, want %d", v1.Int(), i) + } + i++ + if v2.Int() != i { + t.Fatalf("got %d, want %d", v2.Int(), i) + } + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"[4]int", ValueOf([4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) { + i := int64(0) + for v1, v2 := range s { + if v1.Int() != i { + t.Fatalf("got %d, want %d", v1.Int(), i) + } + i++ + if v2.Int() != i { + t.Fatalf("got %d, want %d", v2.Int(), i) + } + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"[]int", ValueOf([]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) { + i := int64(0) + for v1, v2 := range s { + if v1.Int() != i { + t.Fatalf("got %d, want %d", v1.Int(), i) + } + i++ + if v2.Int() != i { + t.Fatalf("got %d, want %d", v2.Int(), i) + } + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"string", ValueOf("12语言"), func(t *testing.T, s iter.Seq2[Value, Value]) { + next, stop := iter.Pull2(s) + defer stop() + i := int64(0) + for j, s := range "12语言" { + v1, v2, ok := next() + if !ok { + t.Fatalf("should loop four times") + } + if v1.Int() != int64(j) { + t.Fatalf("got %d, want %d", v1.Int(), j) + } + if v2.Interface() != s { + t.Fatalf("got %v, want %v", v2.Interface(), s) + } + i++ + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"map[string]int", ValueOf(m), func(t *testing.T, s iter.Seq2[Value, Value]) { + copy := maps.Clone(m) + for v1, v2 := range s { + v, ok := copy[v1.String()] + if !ok { + t.Fatalf("unexpected %v", v1.String()) + } + if v != v2.Interface() { + t.Fatalf("got %v, want %d", v2.Interface(), v) + } + delete(copy, v1.String()) + } + if len(copy) != 0 { + t.Fatalf("should loop four times") + } + }}, + // {"func", ValueOf(func(f func(int, int) bool) { + // for i := range 4 { + // f(i, i+1) + // } + // }), func(t *testing.T, s iter.Seq2[Value, Value]) { + // i := int64(0) + // for v1, v2 := range s { + // if v1.Int() != i { + // t.Fatalf("got %d, want %d", v1.Int(), i) + // } + // i++ + // if v2.Int() != i { + // t.Fatalf("got %d, want %d", v2.Int(), i) + // } + // } + // if i != 4 { + // t.Fatalf("should loop four times") + // } + // }}, + // {"method", ValueOf(methodIter2{}).MethodByName("Seq2"), func(t *testing.T, s iter.Seq2[Value, Value]) { + // i := int64(0) + // for v1, v2 := range s { + // if v1.Int() != i { + // t.Fatalf("got %d, want %d", v1.Int(), i) + // } + // i++ + // if v2.Int() != i { + // t.Fatalf("got %d, want %d", v2.Int(), i) + // } + // } + // if i != 4 { + // t.Fatalf("should loop four times") + // } + // }}, + {"[4]N", ValueOf([4]N{0, 1, 2, 3}), func(t *testing.T, s iter.Seq2[Value, Value]) { + i := N(0) + for v1, v2 := range s { + if v1.Int() != int64(i) { + t.Fatalf("got %d, want %d", v1.Int(), i) + } + if v2.Int() != int64(i) { + t.Fatalf("got %d, want %d", v2.Int(), i) + } + i++ + if v2.Type() != reflect.TypeOf(i) { + t.Fatalf("got %s, want %s", v2.Type(), reflect.TypeOf(i)) + } + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + {"[]N", ValueOf([]N{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) { + i := N(0) + for v1, v2 := range s { + if v1.Int() != int64(i) { + t.Fatalf("got %d, want %d", v1.Int(), i) + } + i++ + if v2.Int() != int64(i) { + t.Fatalf("got %d, want %d", v2.Int(), i) + } + if v2.Type() != reflect.TypeOf(i) { + t.Fatalf("got %s, want %s", v2.Type(), reflect.TypeOf(i)) + } + } + if i != 4 { + t.Fatalf("should loop four times") + } + }}, + } + for _, tc := range tests { + seq := tc.val.Seq2() + tc.check(t, seq) + } +} + +// methodIter is a type from which we can derive a method +// value that is an iter.Seq. +type methodIter struct{} + +func (methodIter) Seq(yield func(int) bool) { + for i := range 4 { + if !yield(i) { + return + } + } +} + +// For Type.CanSeq test. +func (methodIter) NonSeq(yield func(int)) {} + +// methodIter2 is a type from which we can derive a method +// value that is an iter.Seq2. +type methodIter2 struct{} + +func (methodIter2) Seq2(yield func(int, int) bool) { + for i := range 4 { + if !yield(i, i+1) { + return + } + } +} + +// For Type.CanSeq2 test. +func (methodIter2) NonSeq2(yield func(int, int)) {} diff --git a/src/reflect/swapper.go b/src/reflect/swapper.go index a2fa44cef0..d49e33e04c 100644 --- a/src/reflect/swapper.go +++ b/src/reflect/swapper.go @@ -1,40 +1,7 @@ package reflect -import "unsafe" - -// Some of code here has been copied from the Go sources: -// https://github.com/golang/go/blob/go1.15.2/src/reflect/swapper.go -// It has the following copyright note: -// -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +import "internal/reflectlite" func Swapper(slice interface{}) func(i, j int) { - v := ValueOf(slice) - if v.Kind() != Slice { - panic(&ValueError{Method: "Swapper"}) - } - - // Just return Nop func if nothing to swap. - if v.Len() < 2 { - return func(i, j int) {} - } - - typ := v.typecode.Elem() - size := typ.Size() - - header := (*sliceHeader)(v.value) - tmp := unsafe.Pointer(&make([]byte, size)[0]) - - return func(i, j int) { - if uint(i) >= uint(header.len) || uint(j) >= uint(header.len) { - panic("reflect: slice index out of range") - } - val1 := unsafe.Add(header.data, uintptr(i)*size) - val2 := unsafe.Add(header.data, uintptr(j)*size) - memcpy(tmp, val1, size) - memcpy(val1, val2, size) - memcpy(val2, tmp, size) - } + return reflectlite.Swapper(slice) } diff --git a/src/reflect/type.go b/src/reflect/type.go index c81d6ba554..a162aa7973 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -64,130 +64,50 @@ package reflect import ( - "internal/gclayout" - "internal/itoa" + "internal/reflectlite" "unsafe" ) -// 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 - structFieldFlagIsEmbedded -) +type Kind = reflectlite.Kind -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 - Int - Int8 - Int16 - Int32 - Int64 - Uint - Uint8 - Uint16 - Uint32 - Uint64 - Uintptr - Float32 - Float64 - Complex64 - Complex128 - String - UnsafePointer - Chan - Interface - Pointer - Slice - Array - Func - Map - Struct + Invalid Kind = reflectlite.Invalid + Bool Kind = reflectlite.Bool + Int Kind = reflectlite.Int + Int8 Kind = reflectlite.Int8 + Int16 Kind = reflectlite.Int16 + Int32 Kind = reflectlite.Int32 + Int64 Kind = reflectlite.Int64 + Uint Kind = reflectlite.Uint + Uint8 Kind = reflectlite.Uint8 + Uint16 Kind = reflectlite.Uint16 + Uint32 Kind = reflectlite.Uint32 + Uint64 Kind = reflectlite.Uint64 + Uintptr Kind = reflectlite.Uintptr + Float32 Kind = reflectlite.Float32 + Float64 Kind = reflectlite.Float64 + Complex64 Kind = reflectlite.Complex64 + Complex128 Kind = reflectlite.Complex128 + Array Kind = reflectlite.Array + Chan Kind = reflectlite.Chan + Func Kind = reflectlite.Func + Interface Kind = reflectlite.Interface + Map Kind = reflectlite.Map + Pointer Kind = reflectlite.Pointer + Slice Kind = reflectlite.Slice + String Kind = reflectlite.String + Struct Kind = reflectlite.Struct + UnsafePointer Kind = reflectlite.UnsafePointer ) -// Ptr is the old name for the Pointer kind. -const Ptr = Pointer - -func (k Kind) String() string { - switch k { - case Invalid: - return "invalid" - case Bool: - return "bool" - case Int: - return "int" - case Int8: - return "int8" - case Int16: - return "int16" - case Int32: - return "int32" - case Int64: - return "int64" - case Uint: - return "uint" - case Uint8: - return "uint8" - case Uint16: - return "uint16" - case Uint32: - return "uint32" - case Uint64: - return "uint64" - case Uintptr: - return "uintptr" - case Float32: - return "float32" - case Float64: - return "float64" - case Complex64: - return "complex64" - case Complex128: - return "complex128" - case String: - return "string" - case UnsafePointer: - return "unsafe.Pointer" - case Chan: - return "chan" - case Interface: - return "interface" - case Pointer: - return "ptr" - case Slice: - return "slice" - case Array: - return "array" - case Func: - return "func" - case Map: - return "map" - case Struct: - return "struct" - default: - return "kind" + itoa.Itoa(int(int8(k))) - } -} - -// Copied from reflect/type.go -// https://go.dev/src/reflect/type.go?#L348 +const Ptr = reflectlite.Ptr -// ChanDir represents a channel type's direction. -type ChanDir int +type ChanDir = reflectlite.ChanDir const ( - RecvDir ChanDir = 1 << iota // <-chan - SendDir // chan<- - BothDir = RecvDir | SendDir // chan + RecvDir = reflectlite.RecvDir + SendDir = reflectlite.SendDir + BothDir = reflectlite.BothDir ) // Method represents a single method. @@ -409,869 +329,139 @@ type Type interface { // OverflowUint reports whether the uint64 x cannot be represented by type t. // It panics if t's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. OverflowUint(x uint64) bool -} - -// 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 - flagComparable = 64 // flag that is set if this type is comparable - flagIsBinary = 128 // flag that is set if this type uses the hashmap binary algorithm -) - -// The base type struct. All type structs start with this. -type rawType struct { - meta uint8 // metadata byte, contains kind and flags (see constants 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 - numMethod uint16 - ptrTo *rawType - elem *rawType -} - -type ptrType struct { - rawType - numMethod uint16 - elem *rawType -} - -type interfaceType struct { - rawType - ptrTo *rawType - // TODO: methods -} - -type arrayType struct { - rawType - numMethod uint16 - ptrTo *rawType - elem *rawType - arrayLen uintptr - slicePtr *rawType -} - -type mapType struct { - rawType - numMethod uint16 - ptrTo *rawType - elem *rawType - key *rawType -} -type namedType struct { - rawType - numMethod uint16 - ptrTo *rawType - elem *rawType - pkg *byte - name [1]byte -} + // CanSeq reports whether a [Value] with this type can be iterated over using [Value.Seq]. + CanSeq() bool -// 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 - numMethod uint16 - ptrTo *rawType - pkgpath *byte - size uint32 - numField uint16 - fields [1]structField // the remaining fields are all of type structField + // CanSeq2 reports whether a [Value] with this type can be iterated over using [Value.Seq2]. + CanSeq2() bool } -type structField struct { - fieldType *rawType - data unsafe.Pointer // various bits of information, packed in a byte array +type rawType struct { + reflectlite.RawType } -// 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.isNamed() { - return (*elemType)(unsafe.Pointer(t)).elem +func toType(t reflectlite.Type) Type { + if t == nil { + return nil } - return t -} - -func (t *rawType) ptrtag() uintptr { - return uintptr(unsafe.Pointer(t)) & 0b11 + return (*rawType)(unsafe.Pointer(t.(*reflectlite.RawType))) } -func (t *rawType) isNamed() bool { - if tag := t.ptrtag(); tag != 0 { - return false - } - - return t.meta&flagNamed != 0 +func toRawType(t Type) *reflectlite.RawType { + return (*reflectlite.RawType)(unsafe.Pointer(t.(*rawType))) } func TypeOf(i interface{}) Type { - if i == nil { - return nil - } - typecode, _ := decomposeInterface(i) - return (*rawType)(typecode) + return toType(reflectlite.TypeOf(i)) } -func PtrTo(t Type) Type { return PointerTo(t) } +func PtrTo(t Type) Type { + return PointerTo(t) +} func PointerTo(t Type) Type { - return pointerTo(t.(*rawType)) + return toType(reflectlite.PointerTo(toRawType(t))) } -func pointerTo(t *rawType) *rawType { - if t.isNamed() { - return (*elemType)(unsafe.Pointer(t)).ptrTo - } +func (t *rawType) AssignableTo(u Type) bool { + return t.RawType.AssignableTo(&(u.(*rawType).RawType)) +} +func (t *rawType) CanSeq() bool { switch t.Kind() { + case Int8, Int16, Int32, Int64, Int, Uint8, Uint16, Uint32, Uint64, Uint, Uintptr, Array, Slice, Chan, String, Map: + return true + case Func: + // TODO: implement canRangeFunc + // return canRangeFunc(t) + panic("unimplemented: (reflect.Type).CanSeq() for functions") case Pointer: - if tag := t.ptrtag(); tag < 3 { - return (*rawType)(unsafe.Add(unsafe.Pointer(t), 1)) - } - - // TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131 - // We need to be able to create types that match existing types to prevent typecode equality. - panic("reflect: cannot make *****T type") - case Struct: - return (*structType)(unsafe.Pointer(t)).ptrTo - default: - return (*elemType)(unsafe.Pointer(t)).ptrTo + return t.Elem().Kind() == Array } + return false } -func (t *rawType) String() string { - if t.isNamed() { - s := t.name() - if s[0] == '.' { - return s[1:] - } - return s - } - +func (t *rawType) CanSeq2() bool { switch t.Kind() { - case Chan: - elem := t.elem().String() - switch t.ChanDir() { - case SendDir: - return "chan<- " + elem - case RecvDir: - return "<-chan " + elem - case BothDir: - if elem[0] == '<' { - // typ is recv chan, need parentheses as "<-" associates with leftmost - // chan possible, see: - // * https://golang.org/ref/spec#Channel_types - // * https://github.com/golang/go/issues/39897 - return "chan (" + elem + ")" - } - return "chan " + elem - } - + case Array, Slice, String, Map: + return true + case Func: + // TODO: implement canRangeFunc2 + // return canRangeFunc2(t) + panic("unimplemented: (reflect.Type).CanSeq2() for functions") case Pointer: - return "*" + t.elem().String() - case Slice: - return "[]" + t.elem().String() - case Array: - return "[" + itoa.Itoa(t.Len()) + "]" + t.elem().String() - case Map: - return "map[" + t.key().String() + "]" + t.elem().String() - case Struct: - numField := t.NumField() - if numField == 0 { - return "struct {}" - } - s := "struct {" - for i := 0; i < numField; i++ { - f := t.rawField(i) - s += " " + f.Name + " " + f.Type.String() - if f.Tag != "" { - s += " " + quote(string(f.Tag)) - } - // every field except the last needs a semicolon - if i < numField-1 { - s += ";" - } - } - s += " }" - return s - case Interface: - // TODO(dgryski): Needs actual method set info - return "interface {}" - default: - return t.Kind().String() + return t.Elem().Kind() == Array } - - return t.Kind().String() + return false } -func (t *rawType) Kind() Kind { - if t == nil { - return Invalid - } - - if tag := t.ptrtag(); tag != 0 { - return Pointer - } - - return Kind(t.meta & kindMask) +func (t *rawType) ConvertibleTo(u Type) bool { + panic("unimplemented: (reflect.Type).ConvertibleTo()") } -var ( - errTypeElem = &TypeError{"Elem"} - errTypeKey = &TypeError{"Key"} - errTypeField = &TypeError{"Field"} - errTypeBits = &TypeError{"Bits"} - errTypeLen = &TypeError{"Len"} - errTypeNumField = &TypeError{"NumField"} - errTypeChanDir = &TypeError{"ChanDir"} - errTypeFieldByName = &TypeError{"FieldByName"} - errTypeFieldByIndex = &TypeError{"FieldByIndex"} -) - -// 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 { - return t.elem() -} - -func (t *rawType) elem() *rawType { - if tag := t.ptrtag(); tag != 0 { - return (*rawType)(unsafe.Add(unsafe.Pointer(t), -1)) - } - - underlying := t.underlying() - switch underlying.Kind() { - case Pointer: - return (*ptrType)(unsafe.Pointer(underlying)).elem - case Chan, Slice, Array, Map: - return (*elemType)(unsafe.Pointer(underlying)).elem - default: - panic(errTypeElem) - } + return toType(t.RawType.Elem()) } -func (t *rawType) key() *rawType { - underlying := t.underlying() - if underlying.Kind() != Map { - panic(errTypeKey) - } - return (*mapType)(unsafe.Pointer(underlying)).key -} - -// 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 { - field := t.rawField(i) - return StructField{ - Name: field.Name, - PkgPath: field.PkgPath, - Type: field.Type, // note: converts rawType to Type - Tag: field.Tag, - Anonymous: field.Anonymous, - Offset: field.Offset, - Index: []int{i}, - } -} - -func rawStructFieldFromPointer(descriptor *structType, fieldType *rawType, data unsafe.Pointer, flagsByte uint8, name string, offset uint32) rawStructField { - // 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. - pkgPath = readStringZ(unsafe.Pointer(descriptor.pkgpath)) - } - - return rawStructField{ - Name: name, - PkgPath: pkgPath, - Type: fieldType, - Tag: StructTag(tag), - Anonymous: flagsByte&structFieldFlagAnonymous != 0, - Offset: uintptr(offset), - } -} - -// rawField returns nearly the same value as Field but without converting the -// Type member to an interface. -// -// For internal use only. -func (t *rawType) rawField(n int) rawStructField { - if t.Kind() != Struct { - panic(errTypeField) - } - descriptor := (*structType)(unsafe.Pointer(t.underlying())) - if uint(n) >= uint(descriptor.numField) { - panic("reflect: field index out of range") - } - - // 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 := (*structField)(unsafe.Add(unsafe.Pointer(&descriptor.fields[0]), uintptr(n)*unsafe.Sizeof(structField{}))) - 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) - offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32)) - data = unsafe.Add(data, lenOffs) - - name := readStringZ(data) - data = unsafe.Add(data, len(name)) - - return rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset) -} - -// rawFieldByNameFunc returns nearly the same value as FieldByNameFunc but without converting the -// Type member to an interface. -// -// For internal use only. -func (t *rawType) rawFieldByNameFunc(match func(string) bool) (rawStructField, []int, bool) { - if t.Kind() != Struct { - panic(errTypeField) - } - - type fieldWalker struct { - t *rawType - index []int - } - - queue := make([]fieldWalker, 0, 4) - queue = append(queue, fieldWalker{t, nil}) - - for len(queue) > 0 { - type result struct { - r rawStructField - index []int - } - - var found []result - var nextlevel []fieldWalker - - // For all the structs at this level.. - for _, ll := range queue { - // Iterate over all the fields looking for the matching name - // Also calculate field offset. - - descriptor := (*structType)(unsafe.Pointer(ll.t.underlying())) - field := &descriptor.fields[0] - - for i := uint16(0); i < descriptor.numField; i++ { - 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) - - offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32)) - data = unsafe.Add(data, lenOffs) - - name := readStringZ(data) - data = unsafe.Add(data, len(name)) - if match(name) { - found = append(found, result{ - rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset), - append(ll.index[:len(ll.index):len(ll.index)], int(i)), - }) - } - - structOrPtrToStruct := field.fieldType.Kind() == Struct || (field.fieldType.Kind() == Pointer && field.fieldType.elem().Kind() == Struct) - if flagsByte&structFieldFlagIsEmbedded == structFieldFlagIsEmbedded && structOrPtrToStruct { - embedded := field.fieldType - if embedded.Kind() == Pointer { - embedded = embedded.elem() - } - - nextlevel = append(nextlevel, fieldWalker{ - t: embedded, - index: append(ll.index[:len(ll.index):len(ll.index)], int(i)), - }) - } - - // update offset/field pointer if there *is* a next field - if i < descriptor.numField-1 { - // Increment pointer to the next field. - field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{}))) - } - } - } - - // found multiple hits at this level - if len(found) > 1 { - return rawStructField{}, nil, false - } - - // found the field we were looking for - if len(found) == 1 { - r := found[0] - return r.r, r.index, true - } - - // else len(found) == 0, move on to the next level - queue = append(queue[:0], nextlevel...) - } - - // didn't find it - return rawStructField{}, nil, false -} - -// 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 { - kind := t.Kind() - if kind >= Int && kind <= Complex128 { - return int(t.Size()) * 8 - } - panic(errTypeBits) -} - -// Len returns the number of elements in this array. It panics of the type kind -// is not Array. -func (t *rawType) Len() int { - if t.Kind() != Array { - panic(errTypeLen) - } - - 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 { - if t.Kind() != Struct { - panic(errTypeNumField) - } - 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 { - switch t.Kind() { - case Bool, Int8, Uint8: - return 1 - case Int16, Uint16: - return 2 - case Int32, Uint32: - return 4 - case Int64, Uint64: - return 8 - case Int, Uint: - return unsafe.Sizeof(int(0)) - case Uintptr: - return unsafe.Sizeof(uintptr(0)) - case Float32: - return 4 - case Float64: - return 8 - case Complex64: - return 8 - case Complex128: - return 16 - case String: - return unsafe.Sizeof("") - case UnsafePointer, Chan, Map, Pointer: - return unsafe.Sizeof(uintptr(0)) - case Slice: - return unsafe.Sizeof([]int{}) - case Interface: - return unsafe.Sizeof(interface{}(nil)) - case Func: - var f func() - return unsafe.Sizeof(f) - case Array: - return t.elem().Size() * uintptr(t.Len()) - case Struct: - u := t.underlying() - return uintptr((*structType)(unsafe.Pointer(u)).size) - default: - panic("unimplemented: size of type") - } -} - -// Align returns the alignment of this type. It is similar to calling -// unsafe.Alignof. -func (t *rawType) Align() int { - switch t.Kind() { - case Bool, Int8, Uint8: - return int(unsafe.Alignof(int8(0))) - case Int16, Uint16: - return int(unsafe.Alignof(int16(0))) - case Int32, Uint32: - return int(unsafe.Alignof(int32(0))) - case Int64, Uint64: - return int(unsafe.Alignof(int64(0))) - case Int, Uint: - return int(unsafe.Alignof(int(0))) - case Uintptr: - return int(unsafe.Alignof(uintptr(0))) - case Float32: - return int(unsafe.Alignof(float32(0))) - case Float64: - return int(unsafe.Alignof(float64(0))) - case Complex64: - return int(unsafe.Alignof(complex64(0))) - case Complex128: - return int(unsafe.Alignof(complex128(0))) - case String: - return int(unsafe.Alignof("")) - case UnsafePointer, Chan, Map, Pointer: - return int(unsafe.Alignof(uintptr(0))) - case Slice: - return int(unsafe.Alignof([]int(nil))) - case Interface: - return int(unsafe.Alignof(interface{}(nil))) - case Func: - var f func() - return int(unsafe.Alignof(f)) - case Struct: - numField := t.NumField() - alignment := 1 - for i := 0; i < numField; i++ { - fieldAlignment := t.rawField(i).Type.Align() - if fieldAlignment > alignment { - alignment = fieldAlignment - } - } - return alignment - case Array: - return t.elem().Align() - default: - panic("unimplemented: alignment of type") - } + f := t.RawType.Field(i) + return toStructField(f) } -func (r *rawType) gcLayout() unsafe.Pointer { - kind := r.Kind() - - if kind < String { - return gclayout.NoPtrs - } - - switch kind { - case Pointer, UnsafePointer, Chan, Map: - return gclayout.Pointer - case String: - return gclayout.String - case Slice: - return gclayout.Slice - } - - // Unknown (for now); let the conservative pointer scanning handle it - return nil +func (t *rawType) FieldByIndex(index []int) StructField { + f := t.RawType.FieldByIndex(index) + return toStructField(f) } -// 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 { - return t.Align() +func (t *rawType) FieldByName(name string) (StructField, bool) { + f, ok := t.RawType.FieldByName(name) + return toStructField(f), ok } -// 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) { - return true - } - - if t.underlying() == u.(*rawType).underlying() && (!t.isNamed() || !u.(*rawType).isNamed()) { - return true - } - - if u.Kind() == Interface && u.NumMethod() == 0 { - return true - } - - if u.Kind() == Interface { - panic("reflect: unimplemented: AssignableTo with interface") - } - return false +func (t *rawType) FieldByNameFunc(match func(string) bool) (StructField, bool) { + f, ok := t.RawType.FieldByNameFunc(match) + return toStructField(f), ok } func (t *rawType) Implements(u Type) bool { - if u.Kind() != Interface { - panic("reflect: non-interface type passed to Type.Implements") - } - return t.AssignableTo(u) -} - -// Comparable returns whether values of this type can be compared to each other. -func (t *rawType) Comparable() bool { - return (t.meta & flagComparable) == flagComparable -} - -// isBinary returns if the hashmapAlgorithmBinary functions can be used on this type -func (t *rawType) isBinary() bool { - return (t.meta & flagIsBinary) == flagIsBinary -} - -func (t *rawType) ChanDir() ChanDir { - if t.Kind() != Chan { - panic(errTypeChanDir) - } - - dir := int((*elemType)(unsafe.Pointer(t)).numMethod) - - // nummethod is overloaded for channel to store channel direction - return ChanDir(dir) + return t.RawType.Implements(&(u.(*rawType).RawType)) } -func (t *rawType) ConvertibleTo(u Type) bool { - panic("unimplemented: (reflect.Type).ConvertibleTo()") +func (t *rawType) In(i int) Type { + panic("unimplemented: (reflect.Type).In()") } func (t *rawType) IsVariadic() bool { panic("unimplemented: (reflect.Type).IsVariadic()") } -func (t *rawType) NumIn() int { - panic("unimplemented: (reflect.Type).NumIn()") -} - -func (t *rawType) NumOut() int { - panic("unimplemented: (reflect.Type).NumOut()") -} - -func (t *rawType) NumMethod() int { - - if t.isNamed() { - return int((*namedType)(unsafe.Pointer(t)).numMethod) - } - - switch t.Kind() { - case Pointer: - return int((*ptrType)(unsafe.Pointer(t)).numMethod) - case Struct: - return int((*structType)(unsafe.Pointer(t)).numMethod) - case Interface: - //FIXME: Use len(methods) - return (*interfaceType)(unsafe.Pointer(t)).ptrTo.NumMethod() - } - - // Other types have no methods attached. Note we don't panic here. - return 0 -} - -// Read and return a null terminated string starting from data. -func readStringZ(data unsafe.Pointer) string { - start := data - var len uintptr - for *(*byte)(data) != 0 { - len++ - data = unsafe.Add(data, 1) // C: data++ - } - - return *(*string)(unsafe.Pointer(&stringHeader{ - data: start, - len: len, - })) -} - -func (t *rawType) name() string { - ntype := (*namedType)(unsafe.Pointer(t)) - return readStringZ(unsafe.Pointer(&ntype.name[0])) -} - -func (t *rawType) Name() string { - if t.isNamed() { - name := t.name() - for i := 0; i < len(name); i++ { - if name[i] == '.' { - return name[i+1:] - } - } - panic("corrupt name data") - } - - if kind := t.Kind(); kind < UnsafePointer { - return t.Kind().String() - } else if kind == UnsafePointer { - return "Pointer" - } - - return "" -} - func (t *rawType) Key() Type { - return t.key() -} - -func (t rawType) In(i int) Type { - panic("unimplemented: (reflect.Type).In()") -} - -func (t rawType) Out(i int) Type { - panic("unimplemented: (reflect.Type).Out()") -} - -// OverflowComplex reports whether the complex128 x cannot be represented by type t. -// It panics if t's Kind is not Complex64 or Complex128. -func (t rawType) OverflowComplex(x complex128) bool { - k := t.Kind() - switch k { - case Complex64: - return overflowFloat32(real(x)) || overflowFloat32(imag(x)) - case Complex128: - return false - } - panic("reflect: OverflowComplex of non-complex type") -} - -// OverflowFloat reports whether the float64 x cannot be represented by type t. -// It panics if t's Kind is not Float32 or Float64. -func (t rawType) OverflowFloat(x float64) bool { - k := t.Kind() - switch k { - case Float32: - return overflowFloat32(x) - case Float64: - return false - } - panic("reflect: OverflowFloat of non-float type") -} - -// OverflowInt reports whether the int64 x cannot be represented by type t. -// It panics if t's Kind is not Int, Int8, Int16, Int32, or Int64. -func (t rawType) OverflowInt(x int64) bool { - k := t.Kind() - switch k { - case Int, Int8, Int16, Int32, Int64: - bitSize := t.Size() * 8 - trunc := (x << (64 - bitSize)) >> (64 - bitSize) - return x != trunc - } - panic("reflect: OverflowInt of non-int type") -} - -// OverflowUint reports whether the uint64 x cannot be represented by type t. -// It panics if t's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. -func (t rawType) OverflowUint(x uint64) bool { - k := t.Kind() - switch k { - case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: - bitSize := t.Size() * 8 - trunc := (x << (64 - bitSize)) >> (64 - bitSize) - return x != trunc - } - panic("reflect: OverflowUint of non-uint type") + return toType(t.RawType.Key()) } -func (t rawType) Method(i int) Method { +func (t *rawType) Method(i int) Method { panic("unimplemented: (reflect.Type).Method()") } -func (t rawType) MethodByName(name string) (Method, bool) { +func (t *rawType) MethodByName(name string) (Method, bool) { panic("unimplemented: (reflect.Type).MethodByName()") } -func (t *rawType) PkgPath() string { - if t.isNamed() { - ntype := (*namedType)(unsafe.Pointer(t)) - return readStringZ(unsafe.Pointer(ntype.pkg)) - } - - return "" -} - -func (t *rawType) FieldByName(name string) (StructField, bool) { - if t.Kind() != Struct { - panic(errTypeFieldByName) - } - - field, index, ok := t.rawFieldByNameFunc(func(n string) bool { return n == name }) - if !ok { - return StructField{}, false - } - - return StructField{ - Name: field.Name, - PkgPath: field.PkgPath, - Type: field.Type, // note: converts rawType to Type - Tag: field.Tag, - Anonymous: field.Anonymous, - Offset: field.Offset, - Index: index, - }, true +func (t *rawType) NumIn() int { + panic("unimplemented: (reflect.Type).NumIn()") } -func (t *rawType) FieldByNameFunc(match func(string) bool) (StructField, bool) { - if t.Kind() != Struct { - panic(TypeError{"FieldByNameFunc"}) - } - - field, index, ok := t.rawFieldByNameFunc(match) - if !ok { - return StructField{}, false - } - - return StructField{ - Name: field.Name, - PkgPath: field.PkgPath, - Type: field.Type, // note: converts rawType to Type - Tag: field.Tag, - Anonymous: field.Anonymous, - Offset: field.Offset, - Index: index, - }, true +func (t *rawType) NumOut() int { + panic("unimplemented: (reflect.Type).NumOut()") } -func (t *rawType) FieldByIndex(index []int) StructField { - ftype := t - var field rawStructField - - for _, n := range index { - structOrPtrToStruct := ftype.Kind() == Struct || (ftype.Kind() == Pointer && ftype.elem().Kind() == Struct) - if !structOrPtrToStruct { - panic(errTypeFieldByIndex) - } - - if ftype.Kind() == Pointer { - ftype = ftype.elem() - } - - field = ftype.rawField(n) - ftype = field.Type - } - - return StructField{ - Name: field.Name, - PkgPath: field.PkgPath, - Type: field.Type, // note: converts rawType to Type - Tag: field.Tag, - Anonymous: field.Anonymous, - Offset: field.Offset, - Index: index, - } +func (t *rawType) Out(i int) Type { + panic("unimplemented: (reflect.Type).Out()") } // A StructField describes a single field in a struct. +// This must be kept in sync with [reflectlite.StructField]. type StructField struct { // Name indicates the field name. Name string @@ -1287,139 +477,25 @@ type StructField struct { Anonymous bool } -// IsExported reports whether the field is exported. -func (f StructField) IsExported() bool { - return f.PkgPath == "" -} - -// rawStructField is the same as StructField but with the Type member replaced -// with rawType. For internal use only. Avoiding this conversion to the Type -// interface improves code size in many cases. -type rawStructField struct { - Name string - PkgPath string - Type *rawType - Tag StructTag - Offset uintptr - Anonymous bool -} - -// A StructTag is the tag string in a struct field. -type StructTag string - -// TODO: it would be feasible to do the key/value splitting at compile time, -// avoiding the code size cost of doing it at runtime - -// Get returns the value associated with key in the tag string. -func (tag StructTag) Get(key string) string { - v, _ := tag.Lookup(key) - return v -} - -// Lookup returns the value associated with key in the tag string. -func (tag StructTag) Lookup(key string) (value string, ok bool) { - for tag != "" { - // Skip leading space. - i := 0 - for i < len(tag) && tag[i] == ' ' { - i++ - } - tag = tag[i:] - if tag == "" { - break - } - - // Scan to colon. A space, a quote or a control character is a syntax error. - // Strictly speaking, control chars include the range [0x7f, 0x9f], not just - // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters - // as it is simpler to inspect the tag's bytes than the tag's runes. - i = 0 - for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { - i++ - } - if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { - break - } - name := string(tag[:i]) - tag = tag[i+1:] - - // Scan quoted string to find value. - i = 1 - for i < len(tag) && tag[i] != '"' { - if tag[i] == '\\' { - i++ - } - i++ - } - if i >= len(tag) { - break - } - qvalue := string(tag[:i+1]) - tag = tag[i+1:] - - if key == name { - value, err := unquote(qvalue) - if err != nil { - break - } - return value, true - } +func toStructField(f reflectlite.StructField) StructField { + return StructField{ + Name: f.Name, + PkgPath: f.PkgPath, + Type: toType(f.Type), + Tag: f.Tag, + Offset: f.Offset, + Index: f.Index, + Anonymous: f.Anonymous, } - return "", false -} - -// TypeError is the error that is used in a panic when invoking a method on a -// type that is not applicable to that type. -type TypeError struct { - Method string -} - -func (e *TypeError) Error() string { - return "reflect: call of reflect.Type." + e.Method + " on invalid type" -} - -func align(offset uintptr, alignment uintptr) uintptr { - return (offset + alignment - 1) &^ (alignment - 1) -} - -func SliceOf(t Type) Type { - panic("unimplemented: reflect.SliceOf()") -} - -func ArrayOf(n int, t Type) Type { - panic("unimplemented: reflect.ArrayOf()") -} - -func StructOf([]StructField) Type { - panic("unimplemented: reflect.StructOf()") -} - -func MapOf(key, value Type) Type { - panic("unimplemented: reflect.MapOf()") } -func FuncOf(in, out []Type, variadic bool) Type { - panic("unimplemented: reflect.FuncOf()") +// IsExported reports whether the field is exported. +func (f StructField) IsExported() bool { + return f.PkgPath == "" } -const maxVarintLen32 = 5 - -// encoding/binary.Uvarint, specialized for uint32 -func uvarint32(buf []byte) (uint32, int) { - var x uint32 - var s uint - for i, b := range buf { - if b < 0x80 { - return x | uint32(b)< unsafe.Sizeof(uintptr(0)) { - return int64(*(*int)(v.value)) - } else { - return int64(int(uintptr(v.value))) - } - case Int8: - if v.isIndirect() { - return int64(*(*int8)(v.value)) - } else { - return int64(int8(uintptr(v.value))) - } - case Int16: - if v.isIndirect() { - return int64(*(*int16)(v.value)) - } else { - return int64(int16(uintptr(v.value))) - } - case Int32: - if v.isIndirect() || unsafe.Sizeof(int32(0)) > unsafe.Sizeof(uintptr(0)) { - return int64(*(*int32)(v.value)) - } else { - return int64(int32(uintptr(v.value))) - } - case Int64: - if v.isIndirect() || unsafe.Sizeof(int64(0)) > unsafe.Sizeof(uintptr(0)) { - return int64(*(*int64)(v.value)) - } else { - return int64(int64(uintptr(v.value))) - } - default: - panic(&ValueError{Method: "Int", Kind: v.Kind()}) - } -} - -// CanUint reports whether Uint can be used without panicking. -func (v Value) CanUint() bool { - switch v.Kind() { - case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: - return true - default: - return false - } -} - -func (v Value) Uint() uint64 { - switch v.Kind() { - case Uintptr: - if v.isIndirect() { - return uint64(*(*uintptr)(v.value)) - } else { - return uint64(uintptr(v.value)) - } - case Uint8: - if v.isIndirect() { - return uint64(*(*uint8)(v.value)) - } else { - return uint64(uintptr(v.value)) - } - case Uint16: - if v.isIndirect() { - return uint64(*(*uint16)(v.value)) - } else { - return uint64(uintptr(v.value)) - } - case Uint: - if v.isIndirect() || unsafe.Sizeof(uint(0)) > unsafe.Sizeof(uintptr(0)) { - return uint64(*(*uint)(v.value)) - } else { - return uint64(uintptr(v.value)) - } - case Uint32: - if v.isIndirect() || unsafe.Sizeof(uint32(0)) > unsafe.Sizeof(uintptr(0)) { - return uint64(*(*uint32)(v.value)) - } else { - return uint64(uintptr(v.value)) - } - case Uint64: - if v.isIndirect() || unsafe.Sizeof(uint64(0)) > unsafe.Sizeof(uintptr(0)) { - return uint64(*(*uint64)(v.value)) - } else { - return uint64(uintptr(v.value)) - } - default: - panic(&ValueError{Method: "Uint", Kind: v.Kind()}) - } -} - -// CanFloat reports whether Float can be used without panicking. -func (v Value) CanFloat() bool { - switch v.Kind() { - case Float32, Float64: - return true - default: - return false - } -} - -func (v Value) Float32() float32 { - switch v.Kind() { - case Float32: - if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) { - // The float is stored as an external value on systems with 16-bit - // pointers. - return *(*float32)(v.value) - } else { - // The float is directly stored in the interface value on systems - // with 32-bit and 64-bit pointers. - return *(*float32)(unsafe.Pointer(&v.value)) - } - - case Float64: - return float32(v.Float()) - - } - - panic(&ValueError{Method: "Float", Kind: v.Kind()}) -} - -func (v Value) Float() float64 { - switch v.Kind() { - case Float32: - if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) { - // The float is stored as an external value on systems with 16-bit - // pointers. - return float64(*(*float32)(v.value)) - } else { - // The float is directly stored in the interface value on systems - // with 32-bit and 64-bit pointers. - return float64(*(*float32)(unsafe.Pointer(&v.value))) - } - case Float64: - if v.isIndirect() || unsafe.Sizeof(float64(0)) > unsafe.Sizeof(uintptr(0)) { - // For systems with 16-bit and 32-bit pointers. - return *(*float64)(v.value) - } else { - // The float is directly stored in the interface value on systems - // with 64-bit pointers. - return *(*float64)(unsafe.Pointer(&v.value)) - } - default: - panic(&ValueError{Method: "Float", Kind: v.Kind()}) - } -} - -// CanComplex reports whether Complex can be used without panicking. -func (v Value) CanComplex() bool { - switch v.Kind() { - case Complex64, Complex128: - return true - default: - return false - } -} - -func (v Value) Complex() complex128 { - switch v.Kind() { - case Complex64: - if v.isIndirect() || unsafe.Sizeof(complex64(0)) > unsafe.Sizeof(uintptr(0)) { - // The complex number is stored as an external value on systems with - // 16-bit and 32-bit pointers. - return complex128(*(*complex64)(v.value)) - } else { - // The complex number is directly stored in the interface value on - // systems with 64-bit pointers. - return complex128(*(*complex64)(unsafe.Pointer(&v.value))) - } - case Complex128: - // This is a 128-bit value, which is always stored as an external value. - // It may be stored in the pointer directly on very uncommon - // architectures with 128-bit pointers, however. - return *(*complex128)(v.value) - default: - panic(&ValueError{Method: "Complex", Kind: v.Kind()}) - } -} - -func (v Value) String() string { - switch v.Kind() { - case String: - // A string value is always bigger than a pointer as it is made of a - // pointer and a length. - return *(*string)(v.value) - default: - // Special case because of the special treatment of .String() in Go. - return "<" + v.typecode.String() + " Value>" - } -} - -func (v Value) Bytes() []byte { - switch v.Kind() { - case Slice: - if v.typecode.elem().Kind() != Uint8 { - panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) - } - return *(*[]byte)(v.value) - - case Array: - v.checkAddressable() - - if v.typecode.elem().Kind() != Uint8 { - panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) - } - - // Small inline arrays are not addressable, so we only have to - // handle addressable arrays which will be stored as pointers - // in v.value - return unsafe.Slice((*byte)(v.value), v.Len()) - } - - panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) + return Value{v.Value.Addr()} } func (v Value) Slice(i, j int) Value { - switch v.Kind() { - case Slice: - hdr := *(*sliceHeader)(v.value) - i, j := uintptr(i), uintptr(j) - - if j < i || hdr.cap < j { - slicePanic() - } - - elemSize := v.typecode.underlying().elem().Size() - - hdr.len = j - i - hdr.cap = hdr.cap - i - hdr.data = unsafe.Add(hdr.data, i*elemSize) - - return Value{ - typecode: v.typecode, - value: unsafe.Pointer(&hdr), - flags: v.flags, - } - - case Array: - v.checkAddressable() - buf, length := buflen(v) - i, j := uintptr(i), uintptr(j) - if j < i || length < j { - slicePanic() - } - - elemSize := v.typecode.underlying().elem().Size() - - var hdr sliceHeader - hdr.len = j - i - hdr.cap = length - i - hdr.data = unsafe.Add(buf, i*elemSize) - - sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr - return Value{ - typecode: sliceType, - value: unsafe.Pointer(&hdr), - flags: v.flags, - } - - case String: - i, j := uintptr(i), uintptr(j) - str := *(*stringHeader)(v.value) - - if j < i || str.len < j { - slicePanic() - } - - hdr := stringHeader{ - data: unsafe.Add(str.data, i), - len: j - i, - } - - return Value{ - typecode: v.typecode, - value: unsafe.Pointer(&hdr), - flags: v.flags, - } - } - - panic(&ValueError{Method: "Slice", Kind: v.Kind()}) + return Value{v.Value.Slice(i, j)} } func (v Value) Slice3(i, j, k int) Value { - switch v.Kind() { - case Slice: - hdr := *(*sliceHeader)(v.value) - i, j, k := uintptr(i), uintptr(j), uintptr(k) - - if j < i || k < j || hdr.len < k { - slicePanic() - } - - elemSize := v.typecode.underlying().elem().Size() - - hdr.len = j - i - hdr.cap = k - i - hdr.data = unsafe.Add(hdr.data, i*elemSize) - - return Value{ - typecode: v.typecode, - value: unsafe.Pointer(&hdr), - flags: v.flags, - } - - case Array: - v.checkAddressable() - buf, length := buflen(v) - i, j, k := uintptr(i), uintptr(j), uintptr(k) - if j < i || k < j || length < k { - slicePanic() - } - - elemSize := v.typecode.underlying().elem().Size() - - var hdr sliceHeader - hdr.len = j - i - hdr.cap = k - i - hdr.data = unsafe.Add(buf, i*elemSize) - - sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr - return Value{ - typecode: sliceType, - value: unsafe.Pointer(&hdr), - flags: v.flags, - } - } - - panic("unimplemented: (reflect.Value).Slice3()") -} - -//go:linkname maplen runtime.hashmapLen -func maplen(p unsafe.Pointer) int - -//go:linkname chanlen runtime.chanLen -func chanlen(p unsafe.Pointer) int - -// Len returns the length of this value for slices, strings, arrays, channels, -// and maps. For other types, it panics. -func (v Value) Len() int { - switch v.typecode.Kind() { - case Array: - return v.typecode.Len() - case Chan: - return chanlen(v.pointer()) - case Map: - return maplen(v.pointer()) - case Slice: - return int((*sliceHeader)(v.value).len) - case String: - return int((*stringHeader)(v.value).len) - default: - panic(&ValueError{Method: "Len", Kind: v.Kind()}) - } -} - -//go:linkname chancap runtime.chanCap -func chancap(p unsafe.Pointer) int - -// Cap returns the capacity of this value for arrays, channels and slices. -// For other types, it panics. -func (v Value) Cap() int { - switch v.typecode.Kind() { - case Array: - return v.typecode.Len() - case Chan: - return chancap(v.pointer()) - case Slice: - return int((*sliceHeader)(v.value).cap) - default: - panic(&ValueError{Method: "Cap", Kind: v.Kind()}) - } -} - -//go:linkname mapclear runtime.hashmapClear -func mapclear(p unsafe.Pointer) - -// Clear clears the contents of a map or zeros the contents of a slice -// -// It panics if v's Kind is not Map or Slice. -func (v Value) Clear() { - switch v.typecode.Kind() { - case Map: - mapclear(v.pointer()) - case Slice: - hdr := (*sliceHeader)(v.value) - elemSize := v.typecode.underlying().elem().Size() - memzero(hdr.data, elemSize*hdr.len) - default: - panic(&ValueError{Method: "Clear", Kind: v.Kind()}) - } -} - -// NumField returns the number of fields of this struct. It panics for other -// value types. -func (v Value) NumField() int { - return v.typecode.NumField() + return Value{v.Value.Slice3(i, j, k)} } func (v Value) Elem() Value { - switch v.Kind() { - case Ptr: - ptr := v.pointer() - if ptr == nil { - return Value{} - } - // Don't copy RO flags - flags := (v.flags & (valueFlagIndirect | valueFlagExported)) | valueFlagIndirect - return Value{ - typecode: v.typecode.elem(), - value: ptr, - flags: flags, - } - case Interface: - typecode, value := decomposeInterface(*(*interface{})(v.value)) - return Value{ - typecode: (*rawType)(typecode), - value: value, - flags: v.flags &^ valueFlagIndirect, - } - default: - panic(&ValueError{Method: "Elem", Kind: v.Kind()}) - } + return Value{v.Value.Elem()} } // Field returns the value of the i'th field of this struct. func (v Value) Field(i int) Value { - if v.Kind() != Struct { - panic(&ValueError{Method: "Field", Kind: v.Kind()}) - } - structField := v.typecode.rawField(i) - - // Copy flags but clear EmbedRO; we're not an embedded field anymore - flags := v.flags & ^valueFlagEmbedRO - if structField.PkgPath != "" { - // No PkgPath => not exported. - // Clear exported flag even if the parent was exported. - flags &^= valueFlagExported - - // Update the RO flag - if structField.Anonymous { - // Embedded field - flags |= valueFlagEmbedRO - } else { - flags |= valueFlagStickyRO - } - } else { - // Parent field may not have been exported but we are - flags |= valueFlagExported - } - - size := v.typecode.Size() - fieldType := structField.Type - fieldSize := fieldType.Size() - if v.isIndirect() || fieldSize > unsafe.Sizeof(uintptr(0)) { - // v.value was already a pointer to the value and it should stay that - // way. - return Value{ - flags: flags, - typecode: fieldType, - value: unsafe.Add(v.value, structField.Offset), - } - } - - // The fieldSize is smaller than uintptr, which means that the value will - // have to be stored directly in the interface value. - - if fieldSize == 0 { - // The struct field is zero sized. - // This is a rare situation, but because it's undefined behavior - // to shift the size of the value (zeroing the value), handle this - // situation explicitly. - return Value{ - flags: flags, - typecode: fieldType, - value: unsafe.Pointer(nil), - } - } - - if size > unsafe.Sizeof(uintptr(0)) { - // The value was not stored in the interface before but will be - // afterwards, so load the value (from the correct offset) and return - // it. - ptr := unsafe.Add(v.value, structField.Offset) - value := unsafe.Pointer(loadValue(ptr, fieldSize)) - return Value{ - flags: flags &^ valueFlagIndirect, - typecode: fieldType, - 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. - value := maskAndShift(uintptr(v.value), structField.Offset, fieldSize) - return Value{ - flags: flags, - typecode: fieldType, - value: unsafe.Pointer(value), - } + return Value{v.Value.Field(i)} } -var uint8Type = TypeOf(uint8(0)).(*rawType) - func (v Value) Index(i int) Value { - switch v.Kind() { - case Slice: - // Extract an element from the slice. - slice := *(*sliceHeader)(v.value) - if uint(i) >= uint(slice.len) { - panic("reflect: slice index out of range") - } - flags := (v.flags & (valueFlagExported | valueFlagIndirect)) | valueFlagIndirect | v.flags.ro() - elem := Value{ - typecode: v.typecode.elem(), - flags: flags, - } - elem.value = unsafe.Add(slice.data, elem.typecode.Size()*uintptr(i)) // pointer to new value - return elem - case String: - // Extract a character from a string. - // A string is never stored directly in the interface, but always as a - // pointer to the string value. - // Keeping valueFlagExported if set, but don't set valueFlagIndirect - // otherwise CanSet will return true for string elements (which is bad, - // strings are read-only). - s := *(*stringHeader)(v.value) - if uint(i) >= uint(s.len) { - panic("reflect: string index out of range") - } - return Value{ - typecode: uint8Type, - value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))), - flags: v.flags & valueFlagExported, - } - case Array: - // Extract an element from the array. - elemType := v.typecode.elem() - elemSize := elemType.Size() - size := v.typecode.Size() - if size == 0 { - // The element size is 0 and/or the length of the array is 0. - return Value{ - typecode: v.typecode.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 := unsafe.Add(v.value, elemSize*uintptr(i)) // pointer to new value - return Value{ - typecode: v.typecode.elem(), - flags: v.flags, - value: addr, - } - } - - if size > unsafe.Sizeof(uintptr(0)) || v.isIndirect() { - // The element fits in a pointer, but the array is not stored in the pointer directly. - // Load the value from the pointer. - addr := unsafe.Add(v.value, elemSize*uintptr(i)) // pointer to new value - value := addr - if !v.isIndirect() { - // Use a pointer to the value (don't load the value) if the - // 'indirect' flag is set. - value = unsafe.Pointer(loadValue(addr, elemSize)) - } - return Value{ - typecode: v.typecode.elem(), - flags: v.flags, - value: value, - } - } - - // 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.typecode.elem(), - flags: v.flags, - value: unsafe.Pointer(value), - } - default: - panic(&ValueError{Method: "Index", Kind: v.Kind()}) - } -} - -func (v Value) NumMethod() int { - if v.typecode == nil { - panic(&ValueError{Method: "reflect.Value.NumMethod", Kind: Invalid}) - } - return v.typecode.NumMethod() -} - -// OverflowFloat reports whether the float64 x cannot be represented by v's type. -// It panics if v's Kind is not Float32 or Float64. -func (v Value) OverflowFloat(x float64) bool { - k := v.Kind() - switch k { - case Float32: - return overflowFloat32(x) - case Float64: - return false - } - panic(&ValueError{Method: "reflect.Value.OverflowFloat", Kind: v.Kind()}) -} - -func overflowFloat32(x float64) bool { - if x < 0 { - x = -x - } - return math.MaxFloat32 < x && x <= math.MaxFloat64 + return Value{v.Value.Index(i)} } func (v Value) MapKeys() []Value { - if v.Kind() != Map { - panic(&ValueError{Method: "MapKeys", Kind: v.Kind()}) - } - - // empty map - if v.Len() == 0 { - return nil - } - - keys := make([]Value, 0, v.Len()) - - it := hashmapNewIterator() - k := New(v.typecode.Key()) - e := New(v.typecode.Elem()) - - keyType := v.typecode.key() - keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0 - shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary() - - for hashmapNext(v.pointer(), it, k.value, e.value) { - if shouldUnpackInterface { - intf := *(*interface{})(k.value) - v := ValueOf(intf) - keys = append(keys, v) - } else { - keys = append(keys, k.Elem()) - } - k = New(v.typecode.Key()) - } - - return keys + keys := v.Value.MapKeys() + return *(*[]Value)(unsafe.Pointer(&keys)) } -//go:linkname hashmapStringGet runtime.hashmapStringGet -func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool - -//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet -func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool - -//go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet -func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool - func (v Value) MapIndex(key Value) Value { - if v.Kind() != Map { - panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) - } - - vkey := v.typecode.key() - - // compare key type with actual key type of map - if !key.typecode.AssignableTo(vkey) { - // type error? - panic("reflect.Value.MapIndex: incompatible types for key") - } - - elemType := v.typecode.Elem() - elem := New(elemType) - - if vkey.Kind() == String { - if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok { - return Value{} - } - return elem.Elem() - } else if vkey.isBinary() { - var keyptr unsafe.Pointer - if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) { - keyptr = key.value - } else { - keyptr = unsafe.Pointer(&key.value) - } - //TODO(dgryski): zero out padding bytes in key, if any - if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok { - return Value{} - } - return elem.Elem() - } else { - if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok { - return Value{} - } - return elem.Elem() - } + return Value{v.Value.MapIndex(key.Value)} } -//go:linkname hashmapNewIterator runtime.hashmapNewIterator -func hashmapNewIterator() unsafe.Pointer - -//go:linkname hashmapNext runtime.hashmapNext -func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool - func (v Value) MapRange() *MapIter { - if v.Kind() != Map { - panic(&ValueError{Method: "MapRange", Kind: v.Kind()}) - } - - keyType := v.typecode.key() - - keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0 - shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary() - - return &MapIter{ - m: v, - it: hashmapNewIterator(), - unpackKeyInterface: shouldUnpackInterface, - } + return (*MapIter)(v.Value.MapRange()) } -type MapIter struct { - m Value - it unsafe.Pointer - key Value - val Value - - valid bool - unpackKeyInterface bool -} +type MapIter reflectlite.MapIter func (it *MapIter) Key() Value { - if !it.valid { - panic("reflect.MapIter.Key called on invalid iterator") - } - - if it.unpackKeyInterface { - intf := *(*interface{})(it.key.value) - v := ValueOf(intf) - return v - } - - return it.key.Elem() + return Value{((*reflectlite.MapIter)(it)).Key()} } func (it *MapIter) Value() Value { - if !it.valid { - panic("reflect.MapIter.Value called on invalid iterator") - } - - return it.val.Elem() + return Value{((*reflectlite.MapIter)(it)).Value()} } func (it *MapIter) Next() bool { - it.key = New(it.m.typecode.Key()) - it.val = New(it.m.typecode.Elem()) - - it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value) - return it.valid + return ((*reflectlite.MapIter)(it)).Next() } func (v Value) Set(x Value) { - v.checkAddressable() - v.checkRO() - if !x.typecode.AssignableTo(v.typecode) { - panic("reflect.Value.Set: value of type " + x.typecode.String() + " cannot be assigned to type " + v.typecode.String()) - } - - if v.typecode.Kind() == Interface && x.typecode.Kind() != Interface { - // move the value of x back into the interface, if possible - if x.isIndirect() && x.typecode.Size() <= unsafe.Sizeof(uintptr(0)) { - x.value = unsafe.Pointer(loadValue(x.value, x.typecode.Size())) - } - - intf := composeInterface(unsafe.Pointer(x.typecode), x.value) - x = Value{ - typecode: v.typecode, - value: unsafe.Pointer(&intf), - } - } - - size := v.typecode.Size() - if size <= unsafe.Sizeof(uintptr(0)) && !x.isIndirect() { - storeValue(v.value, size, uintptr(x.value)) - } else { - memcpy(v.value, x.value, size) - } -} - -func (v Value) SetZero() { - v.checkAddressable() - v.checkRO() - size := v.typecode.Size() - memzero(v.value, size) -} - -func (v Value) SetBool(x bool) { - v.checkAddressable() - v.checkRO() - switch v.Kind() { - case Bool: - *(*bool)(v.value) = x - default: - panic(&ValueError{Method: "SetBool", Kind: v.Kind()}) - } -} - -func (v Value) SetInt(x int64) { - v.checkAddressable() - v.checkRO() - switch v.Kind() { - case Int: - *(*int)(v.value) = int(x) - case Int8: - *(*int8)(v.value) = int8(x) - case Int16: - *(*int16)(v.value) = int16(x) - case Int32: - *(*int32)(v.value) = int32(x) - case Int64: - *(*int64)(v.value) = x - default: - panic(&ValueError{Method: "SetInt", Kind: v.Kind()}) - } -} - -func (v Value) SetUint(x uint64) { - v.checkAddressable() - v.checkRO() - switch v.Kind() { - case Uint: - *(*uint)(v.value) = uint(x) - case Uint8: - *(*uint8)(v.value) = uint8(x) - case Uint16: - *(*uint16)(v.value) = uint16(x) - case Uint32: - *(*uint32)(v.value) = uint32(x) - case Uint64: - *(*uint64)(v.value) = x - case Uintptr: - *(*uintptr)(v.value) = uintptr(x) - default: - panic(&ValueError{Method: "SetUint", Kind: v.Kind()}) - } -} - -func (v Value) SetFloat(x float64) { - v.checkAddressable() - v.checkRO() - switch v.Kind() { - case Float32: - *(*float32)(v.value) = float32(x) - case Float64: - *(*float64)(v.value) = x - default: - panic(&ValueError{Method: "SetFloat", Kind: v.Kind()}) - } -} - -func (v Value) SetComplex(x complex128) { - v.checkAddressable() - v.checkRO() - switch v.Kind() { - case Complex64: - *(*complex64)(v.value) = complex64(x) - case Complex128: - *(*complex128)(v.value) = x - default: - panic(&ValueError{Method: "SetComplex", Kind: v.Kind()}) - } -} - -func (v Value) SetString(x string) { - v.checkAddressable() - v.checkRO() - switch v.Kind() { - case String: - *(*string)(v.value) = x - default: - panic(&ValueError{Method: "SetString", Kind: v.Kind()}) - } -} - -func (v Value) SetBytes(x []byte) { - v.checkAddressable() - v.checkRO() - if v.typecode.Kind() != Slice || v.typecode.elem().Kind() != Uint8 { - panic("reflect.Value.SetBytes called on not []byte") - } - - // copy the header contents over - *(*[]byte)(v.value) = x -} - -func (v Value) SetCap(n int) { - panic("unimplemented: (reflect.Value).SetCap()") -} - -func (v Value) SetLen(n int) { - if v.typecode.Kind() != Slice { - panic(&ValueError{Method: "reflect.Value.SetLen", Kind: v.Kind()}) - } - v.checkAddressable() - hdr := (*sliceHeader)(v.value) - if int(uintptr(n)) != n || uintptr(n) > hdr.cap { - panic("reflect.Value.SetLen: slice length out of range") - } - hdr.len = uintptr(n) -} - -func (v Value) checkAddressable() { - if !v.isIndirect() { - panic("reflect: value is not addressable") - } -} - -// OverflowInt reports whether the int64 x cannot be represented by v's type. -// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. -func (v Value) OverflowInt(x int64) bool { - switch v.Kind() { - case Int, Int8, Int16, Int32, Int64: - bitSize := v.typecode.Size() * 8 - trunc := (x << (64 - bitSize)) >> (64 - bitSize) - return x != trunc - } - panic(&ValueError{Method: "reflect.Value.OverflowInt", Kind: v.Kind()}) -} - -// OverflowUint reports whether the uint64 x cannot be represented by v's type. -// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. -func (v Value) OverflowUint(x uint64) bool { - k := v.Kind() - switch k { - case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: - bitSize := v.typecode.Size() * 8 - trunc := (x << (64 - bitSize)) >> (64 - bitSize) - return x != trunc - } - panic(&ValueError{Method: "reflect.Value.OverflowUint", Kind: v.Kind()}) + v.Value.Set(x.Value) } func (v Value) CanConvert(t Type) bool { - // TODO: Optimize this to not actually perform a conversion - _, ok := convertOp(v, t) - return ok + return v.Value.CanConvert(toRawType(t)) } func (v Value) Convert(t Type) Value { - if v, ok := convertOp(v, t); ok { - return v - } - - panic("reflect.Value.Convert: value of type " + v.typecode.String() + " cannot be converted to type " + t.String()) -} - -func convertOp(src Value, typ Type) (Value, bool) { - - // Easy check first. Do we even need to do anything? - if src.typecode.underlying() == typ.(*rawType).underlying() { - return Value{ - typecode: typ.(*rawType), - value: src.value, - flags: src.flags, - }, true - } - - if rtype := typ.(*rawType); rtype.Kind() == Interface && rtype.NumMethod() == 0 { - iface := composeInterface(unsafe.Pointer(src.typecode), src.value) - return Value{ - typecode: rtype, - value: unsafe.Pointer(&iface), - flags: valueFlagExported, - }, true - } - - switch src.Kind() { - case Int, Int8, Int16, Int32, Int64: - switch rtype := typ.(*rawType); rtype.Kind() { - case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: - return cvtInt(src, rtype), true - case Float32, Float64: - return cvtIntFloat(src, rtype), true - case String: - return cvtIntString(src, rtype), true - } - - case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: - switch rtype := typ.(*rawType); rtype.Kind() { - case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: - return cvtUint(src, rtype), true - case Float32, Float64: - return cvtUintFloat(src, rtype), true - case String: - return cvtUintString(src, rtype), true - } - - case Float32, Float64: - switch rtype := typ.(*rawType); rtype.Kind() { - case Int, Int8, Int16, Int32, Int64: - return cvtFloatInt(src, rtype), true - case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: - return cvtFloatUint(src, rtype), true - case Float32, Float64: - return cvtFloat(src, rtype), true - } - - /* - case Complex64, Complex128: - switch src.Kind() { - case Complex64, Complex128: - return cvtComplex - } - */ - - case Slice: - switch rtype := typ.(*rawType); rtype.Kind() { - case Array: - if src.typecode.elem() == rtype.elem() && rtype.Len() <= src.Len() { - return Value{ - typecode: rtype, - value: (*sliceHeader)(src.value).data, - flags: src.flags | valueFlagIndirect, - }, true - } - case Pointer: - if rtype.Elem().Kind() == Array { - if src.typecode.elem() == rtype.elem().elem() && rtype.elem().Len() <= src.Len() { - return Value{ - typecode: rtype, - value: (*sliceHeader)(src.value).data, - flags: src.flags & (valueFlagExported | valueFlagRO), - }, true - } - } - case String: - if !src.typecode.elem().isNamed() { - switch src.Type().Elem().Kind() { - case Uint8: - return cvtBytesString(src, rtype), true - case Int32: - return cvtRunesString(src, rtype), true - } - } - } - - case String: - rtype := typ.(*rawType) - if typ.Kind() == Slice && !rtype.elem().isNamed() { - switch typ.Elem().Kind() { - case Uint8: - return cvtStringBytes(src, rtype), true - case Int32: - return cvtStringRunes(src, rtype), true - } - } - } - - // TODO(dgryski): Unimplemented: - // Chan - // Non-defined pointers types with same underlying base type - // Interface <-> Type conversions - - return Value{}, false -} - -func cvtInt(v Value, t *rawType) Value { - return makeInt(v.flags, uint64(v.Int()), t) -} - -func cvtUint(v Value, t *rawType) Value { - return makeInt(v.flags, v.Uint(), t) -} - -func cvtIntFloat(v Value, t *rawType) Value { - return makeFloat(v.flags, float64(v.Int()), t) -} - -func cvtUintFloat(v Value, t *rawType) Value { - return makeFloat(v.flags, float64(v.Uint()), t) -} - -func cvtFloatInt(v Value, t *rawType) Value { - return makeInt(v.flags, uint64(int64(v.Float())), t) + return Value{v.Value.Convert(toRawType(t))} } -func cvtFloatUint(v Value, t *rawType) Value { - return makeInt(v.flags, uint64(v.Float()), t) -} - -func cvtFloat(v Value, t *rawType) Value { - if v.Type().Kind() == Float32 && t.Kind() == Float32 { - // Don't do any conversion if both types have underlying type float32. - // This avoids converting to float64 and back, which will - // convert a signaling NaN to a quiet NaN. See issue 36400. - return makeFloat32(v.flags, v.Float32(), t) - } - return makeFloat(v.flags, v.Float(), t) -} - -//go:linkname stringToBytes runtime.stringToBytes -func stringToBytes(x string) []byte - -func cvtStringBytes(v Value, t *rawType) Value { - b := stringToBytes(*(*string)(v.value)) - return Value{ - typecode: t, - value: unsafe.Pointer(&b), - flags: v.flags, - } -} - -//go:linkname stringFromBytes runtime.stringFromBytes -func stringFromBytes(x []byte) string - -func cvtBytesString(v Value, t *rawType) Value { - s := stringFromBytes(*(*[]byte)(v.value)) - return Value{ - typecode: t, - value: unsafe.Pointer(&s), - flags: v.flags, - } -} - -func makeInt(flags valueFlags, bits uint64, t *rawType) Value { - size := t.Size() - - v := Value{ - typecode: t, - flags: flags, - } - - ptr := unsafe.Pointer(&v.value) - if size > unsafe.Sizeof(uintptr(0)) { - ptr = alloc(size, nil) - v.value = ptr - } - - switch size { - case 1: - *(*uint8)(ptr) = uint8(bits) - case 2: - *(*uint16)(ptr) = uint16(bits) - case 4: - *(*uint32)(ptr) = uint32(bits) - case 8: - *(*uint64)(ptr) = bits - } - return v -} - -func makeFloat(flags valueFlags, f float64, t *rawType) Value { - size := t.Size() - - v := Value{ - typecode: t, - flags: flags, - } - - ptr := unsafe.Pointer(&v.value) - if size > unsafe.Sizeof(uintptr(0)) { - ptr = alloc(size, nil) - v.value = ptr - } - - switch size { - case 4: - *(*float32)(ptr) = float32(f) - case 8: - *(*float64)(ptr) = f - } - return v -} - -func makeFloat32(flags valueFlags, f float32, t *rawType) Value { - v := Value{ - typecode: t, - flags: flags, - } - *(*float32)(unsafe.Pointer(&v.value)) = float32(f) - return v -} - -func cvtIntString(src Value, t *rawType) Value { - panic("cvtUintString: unimplemented") -} - -func cvtUintString(src Value, t *rawType) Value { - panic("cvtUintString: unimplemented") -} - -func cvtStringRunes(src Value, t *rawType) Value { - panic("cvsStringRunes: unimplemented") -} - -func cvtRunesString(src Value, t *rawType) Value { - panic("cvsRunesString: unimplemented") -} - -//go:linkname slicePanic runtime.slicePanic -func slicePanic() - func MakeSlice(typ Type, len, cap int) Value { - if typ.Kind() != Slice { - panic("reflect.MakeSlice of non-slice type") - } - - rtype := typ.(*rawType) - - ulen := uint(len) - ucap := uint(cap) - maxSize := (^uintptr(0)) / 2 - elem := rtype.elem() - elementSize := elem.Size() - if elementSize > 1 { - maxSize /= uintptr(elementSize) - } - if ulen > ucap || ucap > uint(maxSize) { - slicePanic() - } - - // This can't overflow because of the above checks. - size := uintptr(ucap) * elementSize - - var slice sliceHeader - slice.cap = uintptr(ucap) - slice.len = uintptr(ulen) - layout := elem.gcLayout() - - slice.data = alloc(size, layout) - - return Value{ - typecode: rtype, - value: unsafe.Pointer(&slice), - flags: valueFlagExported, - } -} - -var zerobuffer unsafe.Pointer - -const zerobufferLen = 32 - -func init() { - // 32 characters of zero bytes - zerobufferStr := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - s := (*stringHeader)(unsafe.Pointer(&zerobufferStr)) - zerobuffer = s.data + return Value{reflectlite.MakeSlice(toRawType(typ), len, cap)} } func Zero(typ Type) Value { - size := typ.Size() - if size <= unsafe.Sizeof(uintptr(0)) { - return Value{ - typecode: typ.(*rawType), - value: nil, - flags: valueFlagExported | valueFlagRO, - } - } - - if size <= zerobufferLen { - return Value{ - typecode: typ.(*rawType), - value: unsafe.Pointer(zerobuffer), - flags: valueFlagExported | valueFlagRO, - } - } - - return Value{ - typecode: typ.(*rawType), - value: alloc(size, nil), - flags: valueFlagExported | valueFlagRO, - } + return Value{reflectlite.Zero(toRawType(typ))} } // New is the reflect equivalent of the new(T) keyword, returning a pointer to a // new value of the given type. func New(typ Type) Value { - return Value{ - typecode: pointerTo(typ.(*rawType)), - value: alloc(typ.Size(), nil), - flags: valueFlagExported, - } -} - -type funcHeader struct { - Context unsafe.Pointer - Code unsafe.Pointer -} - -type SliceHeader struct { - Data uintptr - Len intw - Cap intw -} - -// Slice header that matches the underlying structure. Used for when we switch -// to a precise GC, which needs to know exactly where pointers live. -type sliceHeader struct { - data unsafe.Pointer - len uintptr - cap uintptr -} - -type StringHeader struct { - Data uintptr - Len intw -} - -// Like sliceHeader, this type is used internally to make sure pointer and -// non-pointer fields match those of actual strings. -type stringHeader struct { - data unsafe.Pointer - len uintptr -} - -// Verify SliceHeader and StringHeader sizes. -// See https://github.com/tinygo-org/tinygo/pull/4156 -// and https://github.com/tinygo-org/tinygo/issues/1284. -var ( - _ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(SliceHeader{})]byte{} - _ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{} - _ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(StringHeader{})]byte{} - _ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(stringHeader{})]byte{} -) - -type ValueError struct { - Method string - Kind Kind + return Value{reflectlite.New(toRawType(typ))} } -func (e *ValueError) Error() string { - if e.Kind == 0 { - return "reflect: call of " + e.Method + " on zero Value" - } - return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value" -} - -//go:linkname memcpy runtime.memcpy -func memcpy(dst, src unsafe.Pointer, size uintptr) - -//go:linkname memzero runtime.memzero -func memzero(ptr unsafe.Pointer, size uintptr) - -//go:linkname alloc runtime.alloc -func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer - -//go:linkname sliceAppend runtime.sliceAppend -func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) - -//go:linkname sliceCopy runtime.sliceCopy -func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int +type ValueError = reflectlite.ValueError // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { - compatibleTypes := false || - // dst and src are both slices or arrays with equal types - ((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) && - (src.typecode.Kind() == Slice || src.typecode.Kind() == Array) && - (dst.typecode.elem() == src.typecode.elem())) || - // dst is array or slice of uint8 and src is string - ((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) && - dst.typecode.elem().Kind() == Uint8 && - src.typecode.Kind() == String) - - if !compatibleTypes { - panic("Copy: type mismatch: " + dst.typecode.String() + "/" + src.typecode.String()) - } - - // Can read from an unaddressable array but not write to one. - if dst.typecode.Kind() == Array && !dst.isIndirect() { - panic("reflect.Copy: unaddressable array value") - } - - dstbuf, dstlen := buflen(dst) - srcbuf, srclen := buflen(src) - - if srclen > 0 { - dst.checkRO() - } - - return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size()) -} - -func buflen(v Value) (unsafe.Pointer, uintptr) { - var buf unsafe.Pointer - var len uintptr - switch v.typecode.Kind() { - case Slice: - hdr := (*sliceHeader)(v.value) - buf = hdr.data - len = hdr.len - case Array: - if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) { - buf = v.value - } else { - buf = unsafe.Pointer(&v.value) - } - len = uintptr(v.Len()) - case String: - hdr := (*stringHeader)(v.value) - buf = hdr.data - len = hdr.len - default: - // This shouldn't happen - panic("reflect.Copy: not slice or array or string") - } - - return buf, len -} - -//go:linkname sliceGrow runtime.sliceGrow -func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) - -// extend slice to hold n new elements -func extendSlice(v Value, n int) sliceHeader { - if v.Kind() != Slice { - panic(&ValueError{Method: "extendSlice", Kind: v.Kind()}) - } - - var old sliceHeader - if v.value != nil { - old = *(*sliceHeader)(v.value) - } - - nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size()) - - return sliceHeader{ - data: nbuf, - len: nlen + uintptr(n), - cap: ncap, - } + return reflectlite.Copy(dst.Value, src.Value) } // Append appends the values x to a slice s and returns the resulting slice. // As in Go, each x's value must be assignable to the slice's element type. func Append(v Value, x ...Value) Value { - if v.Kind() != Slice { - panic(&ValueError{Method: "Append", Kind: v.Kind()}) - } - oldLen := v.Len() - newslice := extendSlice(v, len(x)) - v.flags = valueFlagExported - v.value = (unsafe.Pointer)(&newslice) - for i, xx := range x { - v.Index(oldLen + i).Set(xx) - } - return v + y := *(*[]reflectlite.Value)(unsafe.Pointer(&x)) + return Value{reflectlite.Append(v.Value, y...)} } // AppendSlice appends a slice t to a slice s and returns the resulting slice. // The slices s and t must have the same element type. func AppendSlice(s, t Value) Value { - if s.typecode.Kind() != Slice || t.typecode.Kind() != Slice || s.typecode != t.typecode { - // Not a very helpful error message, but shortened to just one error to - // keep code size down. - panic("reflect.AppendSlice: invalid types") - } - if !s.isExported() || !t.isExported() { - // One of the sides was not exported, so can't access the data. - panic("reflect.AppendSlice: unexported") - } - sSlice := (*sliceHeader)(s.value) - tSlice := (*sliceHeader)(t.value) - elemSize := s.typecode.elem().Size() - ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize) - result := &sliceHeader{ - data: ptr, - len: len, - cap: cap, - } - return Value{ - typecode: s.typecode, - value: unsafe.Pointer(result), - flags: valueFlagExported, - } -} - -// Grow increases the slice's capacity, if necessary, to guarantee space for -// another n elements. After Grow(n), at least n elements can be appended -// to the slice without another allocation. -// -// It panics if v's Kind is not a Slice or if n is negative or too large to -// allocate the memory. -func (v Value) Grow(n int) { - v.checkAddressable() - if n < 0 { - panic("reflect.Grow: negative length") - } - if v.Kind() != Slice { - panic(&ValueError{Method: "Grow", Kind: v.Kind()}) - } - slice := (*sliceHeader)(v.value) - newslice := extendSlice(v, n) - // Only copy the new data and cap: the len remains unchanged. - slice.data = newslice.data - slice.cap = newslice.cap + return Value{reflectlite.AppendSlice(s.Value, t.Value)} } -//go:linkname hashmapStringSet runtime.hashmapStringSet -func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) - -//go:linkname hashmapBinarySet runtime.hashmapBinarySet -func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer) - -//go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet -func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, value unsafe.Pointer) - -//go:linkname hashmapStringDelete runtime.hashmapStringDelete -func hashmapStringDelete(m unsafe.Pointer, key string) - -//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete -func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer) - -//go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete -func hashmapInterfaceDelete(m unsafe.Pointer, key interface{}) - func (v Value) SetMapIndex(key, elem Value) { - v.checkRO() - if v.Kind() != Map { - panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()}) - } - - vkey := v.typecode.key() - - // compare key type with actual key type of map - if !key.typecode.AssignableTo(vkey) { - panic("reflect.Value.SetMapIndex: incompatible types for key") - } - - // if elem is the zero Value, it means delete - del := elem == Value{} - - if !del && !elem.typecode.AssignableTo(v.typecode.elem()) { - panic("reflect.Value.SetMapIndex: incompatible types for value") - } - - // make elem an interface if it needs to be converted - if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface { - intf := composeInterface(unsafe.Pointer(elem.typecode), elem.value) - elem = Value{ - typecode: v.typecode.elem(), - value: unsafe.Pointer(&intf), - } - } - - if key.Kind() == String { - if del { - hashmapStringDelete(v.pointer(), *(*string)(key.value)) - } else { - var elemptr unsafe.Pointer - if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) { - elemptr = elem.value - } else { - elemptr = unsafe.Pointer(&elem.value) - } - hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr) - } - - } else if key.typecode.isBinary() { - var keyptr unsafe.Pointer - if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) { - keyptr = key.value - } else { - keyptr = unsafe.Pointer(&key.value) - } - - if del { - hashmapBinaryDelete(v.pointer(), keyptr) - } else { - var elemptr unsafe.Pointer - if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) { - elemptr = elem.value - } else { - elemptr = unsafe.Pointer(&elem.value) - } - hashmapBinarySet(v.pointer(), keyptr, elemptr) - } - } else { - if del { - hashmapInterfaceDelete(v.pointer(), key.Interface()) - } else { - var elemptr unsafe.Pointer - if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) { - elemptr = elem.value - } else { - elemptr = unsafe.Pointer(&elem.value) - } - - hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr) - } - } + v.Value.SetMapIndex(key.Value, elem.Value) } // FieldByIndex returns the nested field corresponding to index. func (v Value) FieldByIndex(index []int) Value { - if len(index) == 1 { - return v.Field(index[0]) - } - if v.Kind() != Struct { - panic(&ValueError{"FieldByIndex", v.Kind()}) - } - for i, x := range index { - if i > 0 { - if v.Kind() == Pointer && v.typecode.elem().Kind() == Struct { - if v.IsNil() { - panic("reflect: indirection through nil pointer to embedded struct") - } - v = v.Elem() - } - } - v = v.Field(x) - } - return v + return Value{v.Value.FieldByIndex(index)} } // FieldByIndexErr returns the nested field corresponding to index. func (v Value) FieldByIndexErr(index []int) (Value, error) { - return Value{}, &ValueError{Method: "FieldByIndexErr"} + out, err := v.Value.FieldByIndexErr(index) + return Value{out}, err } func (v Value) FieldByName(name string) Value { - if v.Kind() != Struct { - panic(&ValueError{"FieldByName", v.Kind()}) - } - - if field, ok := v.typecode.FieldByName(name); ok { - return v.FieldByIndex(field.Index) - } - return Value{} + return Value{v.Value.FieldByName(name)} } func (v Value) FieldByNameFunc(match func(string) bool) Value { - if v.Kind() != Struct { - panic(&ValueError{"FieldByName", v.Kind()}) - } - - if field, ok := v.typecode.FieldByNameFunc(match); ok { - return v.FieldByIndex(field.Index) - } - return Value{} -} - -//go:linkname hashmapMake runtime.hashmapMake -func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer - -// MakeMapWithSize creates a new map with the specified type and initial space -// for approximately n elements. -func MakeMapWithSize(typ Type, n int) Value { - - // TODO(dgryski): deduplicate these? runtime and reflect both need them. - const ( - hashmapAlgorithmBinary uint8 = iota - hashmapAlgorithmString - hashmapAlgorithmInterface - ) - - if typ.Kind() != Map { - panic(&ValueError{Method: "MakeMap", Kind: typ.Kind()}) - } - - if n < 0 { - panic("reflect.MakeMapWithSize: negative size hint") - } - - key := typ.Key().(*rawType) - val := typ.Elem().(*rawType) - - var alg uint8 - - if key.Kind() == String { - alg = hashmapAlgorithmString - } else if key.isBinary() { - alg = hashmapAlgorithmBinary - } else { - alg = hashmapAlgorithmInterface - } - - m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg) - - return Value{ - typecode: typ.(*rawType), - value: m, - flags: valueFlagExported, - } + return Value{v.Value.FieldByNameFunc(match)} } type SelectDir int @@ -2113,7 +172,13 @@ func (v Value) Close() { // MakeMap creates a new map with the specified type. func MakeMap(typ Type) Value { - return MakeMapWithSize(typ, 8) + return Value{reflectlite.MakeMap(toRawType(typ))} +} + +// MakeMapWithSize creates a new map with the specified type and initial space +// for approximately n elements. +func MakeMapWithSize(typ Type, n int) Value { + return Value{reflectlite.MakeMapWithSize(toRawType(typ), n)} } func (v Value) Call(in []Value) []Value { @@ -2124,6 +189,10 @@ func (v Value) CallSlice(in []Value) []Value { panic("unimplemented: (reflect.Value).CallSlice()") } +func (v Value) Equal(u Value) bool { + return v.Value.Equal(u.Value) +} + func (v Value) Method(i int) Value { panic("unimplemented: (reflect.Value).Method()") } @@ -2139,3 +208,24 @@ func (v Value) Recv() (x Value, ok bool) { func NewAt(typ Type, p unsafe.Pointer) Value { panic("unimplemented: reflect.New()") } + +// Deprecated: Use unsafe.Slice or unsafe.SliceData instead. +type SliceHeader struct { + Data uintptr + Len intw + Cap intw +} + +// Deprecated: Use unsafe.String or unsafe.StringData instead. +type StringHeader struct { + Data uintptr + Len intw +} + +// Verify SliceHeader and StringHeader sizes. +// See https://github.com/tinygo-org/tinygo/pull/4156 +// and https://github.com/tinygo-org/tinygo/issues/1284. +var ( + _ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(SliceHeader{})]byte{} + _ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(StringHeader{})]byte{} +) diff --git a/src/reflect/visiblefields.go b/src/reflect/visiblefields.go index 9375faa110..ac722da070 100644 --- a/src/reflect/visiblefields.go +++ b/src/reflect/visiblefields.go @@ -1,105 +1,11 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package reflect -// VisibleFields returns all the visible fields in t, which must be a -// struct type. A field is defined as visible if it's accessible -// directly with a FieldByName call. The returned fields include fields -// inside anonymous struct members and unexported fields. They follow -// the same order found in the struct, with anonymous fields followed -// immediately by their promoted fields. -// -// For each element e of the returned slice, the corresponding field -// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index). -func VisibleFields(t Type) []StructField { - if t == nil { - panic("reflect: VisibleFields(nil)") - } - if t.Kind() != Struct { - panic("reflect.VisibleFields of non-struct type") - } - w := &visibleFieldsWalker{ - byName: make(map[string]int), - visiting: make(map[Type]bool), - fields: make([]StructField, 0, t.NumField()), - index: make([]int, 0, 2), - } - w.walk(t) - // Remove all the fields that have been hidden. - // Use an in-place removal that avoids copying in - // the common case that there are no hidden fields. - j := 0 - for i := range w.fields { - f := &w.fields[i] - if f.Name == "" { - continue - } - if i != j { - // A field has been removed. We need to shuffle - // all the subsequent elements up. - w.fields[j] = *f - } - j++ - } - return w.fields[:j] -} - -type visibleFieldsWalker struct { - byName map[string]int - visiting map[Type]bool - fields []StructField - index []int -} +import ( + "internal/reflectlite" + "unsafe" +) -// walk walks all the fields in the struct type t, visiting -// fields in index preorder and appending them to w.fields -// (this maintains the required ordering). -// Fields that have been overridden have their -// Name field cleared. -func (w *visibleFieldsWalker) walk(t Type) { - if w.visiting[t] { - return - } - w.visiting[t] = true - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - w.index = append(w.index, i) - add := true - if oldIndex, ok := w.byName[f.Name]; ok { - old := &w.fields[oldIndex] - if len(w.index) == len(old.Index) { - // Fields with the same name at the same depth - // cancel one another out. Set the field name - // to empty to signify that has happened, and - // there's no need to add this field. - old.Name = "" - add = false - } else if len(w.index) < len(old.Index) { - // The old field loses because it's deeper than the new one. - old.Name = "" - } else { - // The old field wins because it's shallower than the new one. - add = false - } - } - if add { - // Copy the index so that it's not overwritten - // by the other appends. - f.Index = append([]int(nil), w.index...) - w.byName[f.Name] = len(w.fields) - w.fields = append(w.fields, f) - } - if f.Anonymous { - if f.Type.Kind() == Pointer { - f.Type = f.Type.Elem() - } - if f.Type.Kind() == Struct { - w.walk(f.Type) - } - } - w.index = w.index[:len(w.index)-1] - } - delete(w.visiting, t) +func VisibleFields(t Type) []StructField { + fields := reflectlite.VisibleFields(toRawType(t)) + return *(*[]StructField)(unsafe.Pointer(&fields)) } diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index ad42fda753..894d92a1ba 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -6,7 +6,7 @@ package runtime // https://golang.org/src/runtime/map.go import ( - "reflect" + "internal/reflectlite" "tinygo" "unsafe" ) @@ -539,8 +539,8 @@ func hashmapStringDelete(m *hashmap, key string) { // a field is exported and thus allows circumventing the type system. // The hash function needs it as it also needs to hash unexported struct fields. // -//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe -func valueInterfaceUnsafe(v reflect.Value) interface{} +//go:linkname valueInterfaceUnsafe internal/reflectlite.valueInterfaceUnsafe +func valueInterfaceUnsafe(v reflectlite.Value) interface{} func hashmapFloat32Hash(ptr unsafe.Pointer, seed uintptr) uint32 { f := *(*uint32)(ptr) @@ -561,7 +561,7 @@ func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 { } func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 { - x := reflect.ValueOf(itf) + x := reflectlite.ValueOf(itf) if x.RawType() == nil { return 0 // nil interface } @@ -574,39 +574,39 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 { } switch x.RawType().Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + case reflectlite.Int, reflectlite.Int8, reflectlite.Int16, reflectlite.Int32, reflectlite.Int64: return hash32(ptr, x.RawType().Size(), seed) - case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + case reflectlite.Bool, reflectlite.Uint, reflectlite.Uint8, reflectlite.Uint16, reflectlite.Uint32, reflectlite.Uint64, reflectlite.Uintptr: return hash32(ptr, x.RawType().Size(), seed) - case reflect.Float32: + case reflectlite.Float32: // It should be possible to just has the contents. However, NaN != NaN // so if you're using lots of NaNs as map keys (you shouldn't) then hash // time may become exponential. To fix that, it would be better to // return a random number instead: // https://research.swtch.com/randhash return hashmapFloat32Hash(ptr, seed) - case reflect.Float64: + case reflectlite.Float64: return hashmapFloat64Hash(ptr, seed) - case reflect.Complex64: + case reflectlite.Complex64: rptr, iptr := ptr, unsafe.Add(ptr, 4) return hashmapFloat32Hash(rptr, seed) ^ hashmapFloat32Hash(iptr, seed) - case reflect.Complex128: + case reflectlite.Complex128: rptr, iptr := ptr, unsafe.Add(ptr, 8) return hashmapFloat64Hash(rptr, seed) ^ hashmapFloat64Hash(iptr, seed) - case reflect.String: + case reflectlite.String: return hashmapStringHash(x.String(), seed) - case reflect.Chan, reflect.Ptr, reflect.UnsafePointer: + case reflectlite.Chan, reflectlite.Ptr, reflectlite.UnsafePointer: // It might seem better to just return the pointer, but that won't // result in an evenly distributed hashmap. Instead, hash the pointer // like most other types. return hash32(ptr, x.RawType().Size(), seed) - case reflect.Array: + case reflectlite.Array: var hash uint32 for i := 0; i < x.Len(); i++ { hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed) } return hash - case reflect.Struct: + case reflectlite.Struct: var hash uint32 for i := 0; i < x.NumField(); i++ { hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed) diff --git a/src/runtime/interface.go b/src/runtime/interface.go index b9813225f2..e1d263a7e3 100644 --- a/src/runtime/interface.go +++ b/src/runtime/interface.go @@ -6,7 +6,7 @@ package runtime // anything (including non-pointers). import ( - "reflect" + "internal/reflectlite" "unsafe" ) @@ -27,12 +27,12 @@ func decomposeInterface(i _interface) (unsafe.Pointer, unsafe.Pointer) { // Return true iff both interfaces are equal. func interfaceEqual(x, y interface{}) bool { - return reflectValueEqual(reflect.ValueOf(x), reflect.ValueOf(y)) + return reflectValueEqual(reflectlite.ValueOf(x), reflectlite.ValueOf(y)) } -func reflectValueEqual(x, y reflect.Value) bool { +func reflectValueEqual(x, y reflectlite.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 + // would introduce an infinite recursion: comparing two reflectlite.Type values // is done with this reflectValueEqual runtime call. if x.RawType() == nil || y.RawType() == nil { // One of them is nil. @@ -46,35 +46,35 @@ func reflectValueEqual(x, y reflect.Value) bool { } switch x.RawType().Kind() { - case reflect.Bool: + case reflectlite.Bool: return x.Bool() == y.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + case reflectlite.Int, reflectlite.Int8, reflectlite.Int16, reflectlite.Int32, reflectlite.Int64: return x.Int() == y.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + case reflectlite.Uint, reflectlite.Uint8, reflectlite.Uint16, reflectlite.Uint32, reflectlite.Uint64, reflectlite.Uintptr: return x.Uint() == y.Uint() - case reflect.Float32, reflect.Float64: + case reflectlite.Float32, reflectlite.Float64: return x.Float() == y.Float() - case reflect.Complex64, reflect.Complex128: + case reflectlite.Complex64, reflectlite.Complex128: return x.Complex() == y.Complex() - case reflect.String: + case reflectlite.String: return x.String() == y.String() - case reflect.Chan, reflect.Ptr, reflect.UnsafePointer: + case reflectlite.Chan, reflectlite.Ptr, reflectlite.UnsafePointer: return x.UnsafePointer() == y.UnsafePointer() - case reflect.Array: + case reflectlite.Array: for i := 0; i < x.Len(); i++ { if !reflectValueEqual(x.Index(i), y.Index(i)) { return false } } return true - case reflect.Struct: + case reflectlite.Struct: for i := 0; i < x.NumField(); i++ { if !reflectValueEqual(x.Field(i), y.Field(i)) { return false } } return true - case reflect.Interface: + case reflectlite.Interface: return reflectValueEqual(x.Elem(), y.Elem()) default: runtimePanic("comparing un-comparable type") diff --git a/transform/rtcalls.go b/transform/rtcalls.go index 8310fc9f19..3abc1d3952 100644 --- a/transform/rtcalls.go +++ b/transform/rtcalls.go @@ -117,8 +117,9 @@ func OptimizeStringEqual(mod llvm.Module) { // As of this writing, the (reflect.Type).Interface method has not yet been // implemented so this optimization is critical for the encoding/json package. func OptimizeReflectImplements(mod llvm.Module) { - implementsSignature := mod.NamedGlobal("reflect/methods.Implements(reflect.Type) bool") - if implementsSignature.IsNil() { + implementsSignature1 := mod.NamedGlobal("reflect/methods.Implements(reflect.Type) bool") + implementsSignature2 := mod.NamedGlobal("reflect/methods.Implements(internal/reflectlite.Type) bool") + if implementsSignature1.IsNil() && implementsSignature2.IsNil() { return } @@ -132,7 +133,8 @@ func OptimizeReflectImplements(mod llvm.Module) { if attr.IsNil() { continue } - if attr.GetStringValue() == "reflect/methods.Implements(reflect.Type) bool" { + val := attr.GetStringValue() + if val == "reflect/methods.Implements(reflect.Type) bool" || val == "reflect/methods.Implements(internal/reflectlite.Type) bool" { implementsFunc = fn break }