diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:19:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:19:13 +0000 |
commit | ccd992355df7192993c666236047820244914598 (patch) | |
tree | f00fea65147227b7743083c6148396f74cd66935 /src/reflect/type.go | |
parent | Initial commit. (diff) | |
download | golang-1.21-ccd992355df7192993c666236047820244914598.tar.xz golang-1.21-ccd992355df7192993c666236047820244914598.zip |
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/reflect/type.go')
-rw-r--r-- | src/reflect/type.go | 2911 |
1 files changed, 2911 insertions, 0 deletions
diff --git a/src/reflect/type.go b/src/reflect/type.go new file mode 100644 index 0000000..9fd242e --- /dev/null +++ b/src/reflect/type.go @@ -0,0 +1,2911 @@ +// 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. + +// Package reflect implements run-time reflection, allowing a program to +// manipulate objects with arbitrary types. The typical use is to take a value +// with static type interface{} and extract its dynamic type information by +// calling TypeOf, which returns a Type. +// +// A call to ValueOf returns a Value representing the run-time data. +// Zero takes a Type and returns a Value representing a zero value +// for that type. +// +// See "The Laws of Reflection" for an introduction to reflection in Go: +// https://golang.org/doc/articles/laws_of_reflection.html +package reflect + +import ( + "internal/abi" + "internal/goarch" + "strconv" + "sync" + "unicode" + "unicode/utf8" + "unsafe" +) + +// Type is the representation of a Go type. +// +// Not all methods apply to all kinds of types. Restrictions, +// if any, are noted in the documentation for each method. +// Use the Kind method to find out the kind of type before +// calling kind-specific methods. Calling a method +// inappropriate to the kind of type causes a run-time panic. +// +// Type values are comparable, such as with the == operator, +// so they can be used as map keys. +// Two Type values are equal if they represent identical types. +type Type interface { + // Methods applicable to all types. + + // Align returns the alignment in bytes of a value of + // this type when allocated in memory. + Align() int + + // FieldAlign returns the alignment in bytes of a value of + // this type when used as a field in a struct. + FieldAlign() int + + // Method returns the i'th method in the type's method set. + // It panics if i is not in the range [0, NumMethod()). + // + // For a non-interface type T or *T, the returned Method's Type and Func + // fields describe a function whose first argument is the receiver, + // and only exported methods are accessible. + // + // For an interface type, the returned Method's Type field gives the + // method signature, without a receiver, and the Func field is nil. + // + // Methods are sorted in lexicographic order. + Method(int) Method + + // MethodByName returns the method with that name in the type's + // method set and a boolean indicating if the method was found. + // + // For a non-interface type T or *T, the returned Method's Type and Func + // fields describe a function whose first argument is the receiver. + // + // For an interface type, the returned Method's Type field gives the + // method signature, without a receiver, and the Func field is nil. + MethodByName(string) (Method, bool) + + // NumMethod returns the number of methods accessible using Method. + // + // For a non-interface type, it returns the number of exported methods. + // + // For an interface type, it returns the number of exported and unexported methods. + NumMethod() int + + // Name returns the type's name within its package for a defined type. + // For other (non-defined) types it returns the empty string. + Name() string + + // PkgPath returns a defined type's package path, that is, the import path + // that uniquely identifies the package, such as "encoding/base64". + // If the type was predeclared (string, error) or not defined (*T, struct{}, + // []int, or A where A is an alias for a non-defined type), the package path + // will be the empty string. + PkgPath() string + + // Size returns the number of bytes needed to store + // a value of the given type; it is analogous to unsafe.Sizeof. + Size() uintptr + + // String returns a string representation of the type. + // The string representation may use shortened package names + // (e.g., base64 instead of "encoding/base64") and is not + // guaranteed to be unique among types. To test for type identity, + // compare the Types directly. + String() string + + // Kind returns the specific kind of this type. + Kind() Kind + + // Implements reports whether the type implements the interface type u. + Implements(u Type) bool + + // AssignableTo reports whether a value of the type is assignable to type u. + AssignableTo(u Type) bool + + // ConvertibleTo reports whether a value of the type is convertible to type u. + // Even if ConvertibleTo returns true, the conversion may still panic. + // For example, a slice of type []T is convertible to *[N]T, + // but the conversion will panic if its length is less than N. + ConvertibleTo(u Type) bool + + // Comparable reports whether values of this type are comparable. + // Even if Comparable returns true, the comparison may still panic. + // For example, values of interface type are comparable, + // but the comparison will panic if their dynamic type is not comparable. + Comparable() bool + + // Methods applicable only to some types, depending on Kind. + // The methods allowed for each kind are: + // + // Int*, Uint*, Float*, Complex*: Bits + // Array: Elem, Len + // Chan: ChanDir, Elem + // Func: In, NumIn, Out, NumOut, IsVariadic. + // Map: Key, Elem + // Pointer: Elem + // Slice: Elem + // Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField + + // Bits returns the size of the type in bits. + // It panics if the type's Kind is not one of the + // sized or unsized Int, Uint, Float, or Complex kinds. + Bits() int + + // ChanDir returns a channel type's direction. + // It panics if the type's Kind is not Chan. + ChanDir() ChanDir + + // IsVariadic reports whether a function type's final input parameter + // is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's + // implicit actual type []T. + // + // For concreteness, if t represents func(x int, y ... float64), then + // + // t.NumIn() == 2 + // t.In(0) is the reflect.Type for "int" + // t.In(1) is the reflect.Type for "[]float64" + // t.IsVariadic() == true + // + // IsVariadic panics if the type's Kind is not Func. + IsVariadic() bool + + // Elem returns a type's element type. + // It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice. + Elem() Type + + // Field returns a struct type's i'th field. + // It panics if the type's Kind is not Struct. + // It panics if i is not in the range [0, NumField()). + Field(i int) StructField + + // FieldByIndex returns the nested field corresponding + // to the index sequence. It is equivalent to calling Field + // successively for each index i. + // It panics if the type's Kind is not Struct. + FieldByIndex(index []int) StructField + + // FieldByName returns the struct field with the given name + // and a boolean indicating if the field was found. + FieldByName(name string) (StructField, bool) + + // FieldByNameFunc returns the struct field with a name + // that satisfies the match function and a boolean indicating if + // the field was found. + // + // FieldByNameFunc considers the fields in the struct itself + // and then the fields in any embedded structs, in breadth first order, + // stopping at the shallowest nesting depth containing one or more + // fields satisfying the match function. If multiple fields at that depth + // satisfy the match function, they cancel each other + // and FieldByNameFunc returns no match. + // This behavior mirrors Go's handling of name lookup in + // structs containing embedded fields. + FieldByNameFunc(match func(string) bool) (StructField, bool) + + // In returns the type of a function type's i'th input parameter. + // It panics if the type's Kind is not Func. + // It panics if i is not in the range [0, NumIn()). + In(i int) Type + + // Key returns a map type's key type. + // It panics if the type's Kind is not Map. + Key() Type + + // Len returns an array type's length. + // It panics if the type's Kind is not Array. + Len() int + + // NumField returns a struct type's field count. + // It panics if the type's Kind is not Struct. + NumField() int + + // NumIn returns a function type's input parameter count. + // It panics if the type's Kind is not Func. + NumIn() int + + // NumOut returns a function type's output parameter count. + // It panics if the type's Kind is not Func. + NumOut() int + + // Out returns the type of a function type's i'th output parameter. + // It panics if the type's Kind is not Func. + // It panics if i is not in the range [0, NumOut()). + Out(i int) Type + + common() *abi.Type + uncommon() *uncommonType +} + +// BUG(rsc): FieldByName and related functions consider struct field names to be equal +// if the names are equal, even if they are unexported names originating +// in different packages. The practical effect of this is that the result of +// t.FieldByName("x") is not well defined if the struct type t contains +// multiple fields named x (embedded from different packages). +// FieldByName may return one of the fields named x or may report that there are none. +// See https://golang.org/issue/4876 for more details. + +/* + * These data structures are known to the compiler (../cmd/compile/internal/reflectdata/reflect.go). + * A few are known to ../runtime/type.go to convey to debuggers. + * They are also known to ../runtime/type.go. + */ + +// A Kind represents the specific kind of type that a Type represents. +// The zero Kind is not a valid kind. +type Kind uint + +const ( + Invalid Kind = iota + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + Array + Chan + Func + Interface + Map + Pointer + Slice + String + Struct + UnsafePointer +) + +// Ptr is the old name for the Pointer kind. +const Ptr = Pointer + +// uncommonType is present only for defined types or types with methods +// (if T is a defined type, the uncommonTypes for T and *T have methods). +// Using a pointer to this struct reduces the overall size required +// to describe a non-defined type with no methods. +type uncommonType = abi.UncommonType + +// Embed this type to get common/uncommon +type common struct { + abi.Type +} + +// rtype is the common implementation of most values. +// It is embedded in other struct types. +type rtype struct { + t abi.Type +} + +func (t *rtype) common() *abi.Type { + return &t.t +} + +func (t *rtype) uncommon() *abi.UncommonType { + return t.t.Uncommon() +} + +type aNameOff = abi.NameOff +type aTypeOff = abi.TypeOff +type aTextOff = abi.TextOff + +// ChanDir represents a channel type's direction. +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan +) + +// arrayType represents a fixed array type. +type arrayType = abi.ArrayType + +// chanType represents a channel type. +type chanType = abi.ChanType + +// funcType represents a function type. +// +// A *rtype for each in and out parameter is stored in an array that +// directly follows the funcType (and possibly its uncommonType). So +// a function type with one method, one input, and one output is: +// +// struct { +// funcType +// uncommonType +// [2]*rtype // [0] is in, [1] is out +// } +type funcType = abi.FuncType + +// interfaceType represents an interface type. +type interfaceType struct { + abi.InterfaceType // can embed directly because not a public type. +} + +func (t *interfaceType) nameOff(off aNameOff) abi.Name { + return toRType(&t.Type).nameOff(off) +} + +func nameOffFor(t *abi.Type, off aNameOff) abi.Name { + return toRType(t).nameOff(off) +} + +func typeOffFor(t *abi.Type, off aTypeOff) *abi.Type { + return toRType(t).typeOff(off) +} + +func (t *interfaceType) typeOff(off aTypeOff) *abi.Type { + return toRType(&t.Type).typeOff(off) +} + +func (t *interfaceType) common() *abi.Type { + return &t.Type +} + +func (t *interfaceType) uncommon() *abi.UncommonType { + return t.Uncommon() +} + +// mapType represents a map type. +type mapType struct { + abi.MapType +} + +// ptrType represents a pointer type. +type ptrType struct { + abi.PtrType +} + +// sliceType represents a slice type. +type sliceType struct { + abi.SliceType +} + +// Struct field +type structField = abi.StructField + +// structType represents a struct type. +type structType struct { + abi.StructType +} + +func pkgPath(n abi.Name) string { + if n.Bytes == nil || *n.DataChecked(0, "name flag field")&(1<<2) == 0 { + return "" + } + i, l := n.ReadVarint(1) + off := 1 + i + l + if n.HasTag() { + i2, l2 := n.ReadVarint(off) + off += i2 + l2 + } + var nameOff int32 + // Note that this field may not be aligned in memory, + // so we cannot use a direct int32 assignment here. + copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.DataChecked(off, "name offset field")))[:]) + pkgPathName := abi.Name{Bytes: (*byte)(resolveTypeOff(unsafe.Pointer(n.Bytes), nameOff))} + return pkgPathName.Name() +} + +func newName(n, tag string, exported, embedded bool) abi.Name { + return abi.NewName(n, tag, exported, embedded) +} + +/* + * The compiler knows the exact layout of all the data structures above. + * The compiler does not know about the data structures and methods below. + */ + +// Method represents a single method. +type Method struct { + // Name is the method name. + Name string + + // PkgPath is the package path that qualifies a lower case (unexported) + // method name. It is empty for upper case (exported) method names. + // The combination of PkgPath and Name uniquely identifies a method + // in a method set. + // See https://golang.org/ref/spec#Uniqueness_of_identifiers + PkgPath string + + Type Type // method type + Func Value // func with receiver as first argument + Index int // index for Type.Method +} + +// IsExported reports whether the method is exported. +func (m Method) IsExported() bool { + return m.PkgPath == "" +} + +const ( + kindDirectIface = 1 << 5 + kindGCProg = 1 << 6 // Type.gc points to GC program + kindMask = (1 << 5) - 1 +) + +// String returns the name of k. +func (k Kind) String() string { + if uint(k) < uint(len(kindNames)) { + return kindNames[uint(k)] + } + return "kind" + strconv.Itoa(int(k)) +} + +var kindNames = []string{ + Invalid: "invalid", + Bool: "bool", + Int: "int", + Int8: "int8", + Int16: "int16", + Int32: "int32", + Int64: "int64", + Uint: "uint", + Uint8: "uint8", + Uint16: "uint16", + Uint32: "uint32", + Uint64: "uint64", + Uintptr: "uintptr", + Float32: "float32", + Float64: "float64", + Complex64: "complex64", + Complex128: "complex128", + Array: "array", + Chan: "chan", + Func: "func", + Interface: "interface", + Map: "map", + Pointer: "ptr", + Slice: "slice", + String: "string", + Struct: "struct", + UnsafePointer: "unsafe.Pointer", +} + +// resolveNameOff resolves a name offset from a base pointer. +// The (*rtype).nameOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +// +//go:noescape +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + +// resolveTypeOff resolves an *rtype offset from a base type. +// The (*rtype).typeOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +// +//go:noescape +func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +// resolveTextOff resolves a function pointer offset from a base type. +// The (*rtype).textOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +// +//go:noescape +func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +// +//go:noescape +func addReflectOff(ptr unsafe.Pointer) int32 + +// resolveReflectName adds a name to the reflection lookup map in the runtime. +// It returns a new nameOff that can be used to refer to the pointer. +func resolveReflectName(n abi.Name) aNameOff { + return aNameOff(addReflectOff(unsafe.Pointer(n.Bytes))) +} + +// resolveReflectType adds a *rtype to the reflection lookup map in the runtime. +// It returns a new typeOff that can be used to refer to the pointer. +func resolveReflectType(t *abi.Type) aTypeOff { + return aTypeOff(addReflectOff(unsafe.Pointer(t))) +} + +// resolveReflectText adds a function pointer to the reflection lookup map in +// the runtime. It returns a new textOff that can be used to refer to the +// pointer. +func resolveReflectText(ptr unsafe.Pointer) aTextOff { + return aTextOff(addReflectOff(ptr)) +} + +func (t *rtype) nameOff(off aNameOff) abi.Name { + return abi.Name{Bytes: (*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} +} + +func (t *rtype) typeOff(off aTypeOff) *abi.Type { + return (*abi.Type)(resolveTypeOff(unsafe.Pointer(t), int32(off))) +} + +func (t *rtype) textOff(off aTextOff) unsafe.Pointer { + return resolveTextOff(unsafe.Pointer(t), int32(off)) +} + +func textOffFor(t *abi.Type, off aTextOff) unsafe.Pointer { + return toRType(t).textOff(off) +} + +func (t *rtype) String() string { + s := t.nameOff(t.t.Str).Name() + if t.t.TFlag&abi.TFlagExtraStar != 0 { + return s[1:] + } + return s +} + +func (t *rtype) Size() uintptr { return t.t.Size() } + +func (t *rtype) Bits() int { + if t == nil { + panic("reflect: Bits of nil Type") + } + k := t.Kind() + if k < Int || k > Complex128 { + panic("reflect: Bits of non-arithmetic Type " + t.String()) + } + return int(t.t.Size_) * 8 +} + +func (t *rtype) Align() int { return t.t.Align() } + +func (t *rtype) FieldAlign() int { return t.t.FieldAlign() } + +func (t *rtype) Kind() Kind { return Kind(t.t.Kind()) } + +func (t *rtype) exportedMethods() []abi.Method { + ut := t.uncommon() + if ut == nil { + return nil + } + return ut.ExportedMethods() +} + +func (t *rtype) NumMethod() int { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.NumMethod() + } + return len(t.exportedMethods()) +} + +func (t *rtype) Method(i int) (m Method) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.Method(i) + } + methods := t.exportedMethods() + if i < 0 || i >= len(methods) { + panic("reflect: Method index out of range") + } + p := methods[i] + pname := t.nameOff(p.Name) + m.Name = pname.Name() + fl := flag(Func) + mtyp := t.typeOff(p.Mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) + in := make([]Type, 0, 1+ft.NumIn()) + in = append(in, t) + for _, arg := range ft.InSlice() { + in = append(in, toRType(arg)) + } + out := make([]Type, 0, ft.NumOut()) + for _, ret := range ft.OutSlice() { + out = append(out, toRType(ret)) + } + mt := FuncOf(in, out, ft.IsVariadic()) + m.Type = mt + tfn := t.textOff(p.Tfn) + fn := unsafe.Pointer(&tfn) + m.Func = Value{&mt.(*rtype).t, fn, fl} + + m.Index = i + return m +} + +func (t *rtype) MethodByName(name string) (m Method, ok bool) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.MethodByName(name) + } + ut := t.uncommon() + if ut == nil { + return Method{}, false + } + + methods := ut.ExportedMethods() + + // We are looking for the first index i where the string becomes >= s. + // This is a copy of sort.Search, with f(h) replaced by (t.nameOff(methods[h].name).name() >= name). + i, j := 0, len(methods) + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + // i ≤ h < j + if !(t.nameOff(methods[h].Name).Name() >= name) { + i = h + 1 // preserves f(i-1) == false + } else { + j = h // preserves f(j) == true + } + } + // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. + if i < len(methods) && name == t.nameOff(methods[i].Name).Name() { + return t.Method(i), true + } + + return Method{}, false +} + +func (t *rtype) PkgPath() string { + if t.t.TFlag&abi.TFlagNamed == 0 { + return "" + } + ut := t.uncommon() + if ut == nil { + return "" + } + return t.nameOff(ut.PkgPath).Name() +} + +func pkgPathFor(t *abi.Type) string { + return toRType(t).PkgPath() +} + +func (t *rtype) Name() string { + if !t.t.HasName() { + return "" + } + s := t.String() + i := len(s) - 1 + sqBrackets := 0 + for i >= 0 && (s[i] != '.' || sqBrackets != 0) { + switch s[i] { + case ']': + sqBrackets++ + case '[': + sqBrackets-- + } + i-- + } + return s[i+1:] +} + +func nameFor(t *abi.Type) string { + return toRType(t).Name() +} + +func (t *rtype) ChanDir() ChanDir { + if t.Kind() != Chan { + panic("reflect: ChanDir of non-chan type " + t.String()) + } + tt := (*abi.ChanType)(unsafe.Pointer(t)) + return ChanDir(tt.Dir) +} + +func toRType(t *abi.Type) *rtype { + return (*rtype)(unsafe.Pointer(t)) +} + +func elem(t *abi.Type) *abi.Type { + et := t.Elem() + if et != nil { + return et + } + panic("reflect: Elem of invalid type " + stringFor(t)) +} + +func (t *rtype) Elem() Type { + return toType(elem(t.common())) +} + +func (t *rtype) Field(i int) StructField { + if t.Kind() != Struct { + panic("reflect: Field of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.Field(i) +} + +func (t *rtype) FieldByIndex(index []int) StructField { + if t.Kind() != Struct { + panic("reflect: FieldByIndex of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.FieldByIndex(index) +} + +func (t *rtype) FieldByName(name string) (StructField, bool) { + if t.Kind() != Struct { + panic("reflect: FieldByName of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.FieldByName(name) +} + +func (t *rtype) FieldByNameFunc(match func(string) bool) (StructField, bool) { + if t.Kind() != Struct { + panic("reflect: FieldByNameFunc of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.FieldByNameFunc(match) +} + +func (t *rtype) Key() Type { + if t.Kind() != Map { + panic("reflect: Key of non-map type " + t.String()) + } + tt := (*mapType)(unsafe.Pointer(t)) + return toType(tt.Key) +} + +func (t *rtype) Len() int { + if t.Kind() != Array { + panic("reflect: Len of non-array type " + t.String()) + } + tt := (*arrayType)(unsafe.Pointer(t)) + return int(tt.Len) +} + +func (t *rtype) NumField() int { + if t.Kind() != Struct { + panic("reflect: NumField of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return len(tt.Fields) +} + +func (t *rtype) In(i int) Type { + if t.Kind() != Func { + panic("reflect: In of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return toType(tt.InSlice()[i]) +} + +func (t *rtype) NumIn() int { + if t.Kind() != Func { + panic("reflect: NumIn of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return tt.NumIn() +} + +func (t *rtype) NumOut() int { + if t.Kind() != Func { + panic("reflect: NumOut of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return tt.NumOut() +} + +func (t *rtype) Out(i int) Type { + if t.Kind() != Func { + panic("reflect: Out of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return toType(tt.OutSlice()[i]) +} + +func (t *rtype) IsVariadic() bool { + if t.Kind() != Func { + panic("reflect: IsVariadic of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return tt.IsVariadic() +} + +// add returns p+x. +// +// The whySafe string is ignored, so that the function still inlines +// as efficiently as p+x, but all call sites should use the string to +// record why the addition is safe, which is to say why the addition +// does not cause x to advance to the very end of p's allocation +// and therefore point incorrectly at the next block in memory. +func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +func (d ChanDir) String() string { + switch d { + case SendDir: + return "chan<-" + case RecvDir: + return "<-chan" + case BothDir: + return "chan" + } + return "ChanDir" + strconv.Itoa(int(d)) +} + +// Method returns the i'th method in the type's method set. +func (t *interfaceType) Method(i int) (m Method) { + if i < 0 || i >= len(t.Methods) { + return + } + p := &t.Methods[i] + pname := t.nameOff(p.Name) + m.Name = pname.Name() + if !pname.IsExported() { + m.PkgPath = pkgPath(pname) + if m.PkgPath == "" { + m.PkgPath = t.PkgPath.Name() + } + } + m.Type = toType(t.typeOff(p.Typ)) + m.Index = i + return +} + +// NumMethod returns the number of interface methods in the type's method set. +func (t *interfaceType) NumMethod() int { return len(t.Methods) } + +// MethodByName method with the given name in the type's method set. +func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { + if t == nil { + return + } + var p *abi.Imethod + for i := range t.Methods { + p = &t.Methods[i] + if t.nameOff(p.Name).Name() == name { + return t.Method(i), true + } + } + return +} + +// A StructField describes a single field in a struct. +type StructField struct { + // Name is the field name. + Name string + + // PkgPath is the package path that qualifies a lower case (unexported) + // field name. It is empty for upper case (exported) field names. + // See https://golang.org/ref/spec#Uniqueness_of_identifiers + PkgPath string + + Type Type // field type + Tag StructTag // field tag string + Offset uintptr // offset within struct, in bytes + Index []int // index sequence for Type.FieldByIndex + Anonymous bool // is an embedded field +} + +// IsExported reports whether the field is exported. +func (f StructField) IsExported() bool { + return f.PkgPath == "" +} + +// A StructTag is the tag string in a struct field. +// +// By convention, tag strings are a concatenation of +// optionally space-separated key:"value" pairs. +// Each key is a non-empty string consisting of non-control +// characters other than space (U+0020 ' '), quote (U+0022 '"'), +// and colon (U+003A ':'). Each value is quoted using U+0022 '"' +// characters and Go string literal syntax. +type StructTag string + +// Get returns the value associated with key in the tag string. +// If there is no such key in the tag, Get returns the empty string. +// If the tag does not have the conventional format, the value +// returned by Get is unspecified. To determine whether a tag is +// explicitly set to the empty string, use Lookup. +func (tag StructTag) Get(key string) string { + v, _ := tag.Lookup(key) + return v +} + +// Lookup returns the value associated with key in the tag string. +// If the key is present in the tag the value (which may be empty) +// is returned. Otherwise the returned value will be the empty string. +// The ok return value reports whether the value was explicitly set in +// the tag string. If the tag does not have the conventional format, +// the value returned by Lookup is unspecified. +func (tag StructTag) Lookup(key string) (value string, ok bool) { + // When modifying this code, also update the validateStructTag code + // in cmd/vet/structtag.go. + + 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 := strconv.Unquote(qvalue) + if err != nil { + break + } + return value, true + } + } + return "", false +} + +// Field returns the i'th struct field. +func (t *structType) Field(i int) (f StructField) { + if i < 0 || i >= len(t.Fields) { + panic("reflect: Field index out of bounds") + } + p := &t.Fields[i] + f.Type = toType(p.Typ) + f.Name = p.Name.Name() + f.Anonymous = p.Embedded() + if !p.Name.IsExported() { + f.PkgPath = t.PkgPath.Name() + } + if tag := p.Name.Tag(); tag != "" { + f.Tag = StructTag(tag) + } + f.Offset = p.Offset + + // NOTE(rsc): This is the only allocation in the interface + // presented by a reflect.Type. It would be nice to avoid, + // at least in the common cases, but we need to make sure + // that misbehaving clients of reflect cannot affect other + // uses of reflect. One possibility is CL 5371098, but we + // postponed that ugliness until there is a demonstrated + // need for the performance. This is issue 2320. + f.Index = []int{i} + return +} + +// TODO(gri): Should there be an error/bool indicator if the index +// is wrong for FieldByIndex? + +// FieldByIndex returns the nested field corresponding to index. +func (t *structType) FieldByIndex(index []int) (f StructField) { + f.Type = toType(&t.Type) + for i, x := range index { + if i > 0 { + ft := f.Type + if ft.Kind() == Pointer && ft.Elem().Kind() == Struct { + ft = ft.Elem() + } + f.Type = ft + } + f = f.Type.Field(x) + } + return +} + +// A fieldScan represents an item on the fieldByNameFunc scan work list. +type fieldScan struct { + typ *structType + index []int +} + +// FieldByNameFunc returns the struct field with a name that satisfies the +// match function and a boolean to indicate if the field was found. +func (t *structType) FieldByNameFunc(match func(string) bool) (result StructField, ok bool) { + // This uses the same condition that the Go language does: there must be a unique instance + // of the match at a given depth level. If there are multiple instances of a match at the + // same depth, they annihilate each other and inhibit any possible match at a lower level. + // The algorithm is breadth first search, one depth level at a time. + + // The current and next slices are work queues: + // current lists the fields to visit on this depth level, + // and next lists the fields on the next lower level. + current := []fieldScan{} + next := []fieldScan{{typ: t}} + + // nextCount records the number of times an embedded type has been + // encountered and considered for queueing in the 'next' slice. + // We only queue the first one, but we increment the count on each. + // If a struct type T can be reached more than once at a given depth level, + // then it annihilates itself and need not be considered at all when we + // process that next depth level. + var nextCount map[*structType]int + + // visited records the structs that have been considered already. + // Embedded pointer fields can create cycles in the graph of + // reachable embedded types; visited avoids following those cycles. + // It also avoids duplicated effort: if we didn't find the field in an + // embedded type T at level 2, we won't find it in one at level 4 either. + visited := map[*structType]bool{} + + for len(next) > 0 { + current, next = next, current[:0] + count := nextCount + nextCount = nil + + // Process all the fields at this depth, now listed in 'current'. + // The loop queues embedded fields found in 'next', for processing during the next + // iteration. The multiplicity of the 'current' field counts is recorded + // in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'. + for _, scan := range current { + t := scan.typ + if visited[t] { + // We've looked through this type before, at a higher level. + // That higher level would shadow the lower level we're now at, + // so this one can't be useful to us. Ignore it. + continue + } + visited[t] = true + for i := range t.Fields { + f := &t.Fields[i] + // Find name and (for embedded field) type for field f. + fname := f.Name.Name() + var ntyp *abi.Type + if f.Embedded() { + // Embedded field of type T or *T. + ntyp = f.Typ + if ntyp.Kind() == abi.Pointer { + ntyp = ntyp.Elem() + } + } + + // Does it match? + if match(fname) { + // Potential match + if count[t] > 1 || ok { + // Name appeared multiple times at this level: annihilate. + return StructField{}, false + } + result = t.Field(i) + result.Index = nil + result.Index = append(result.Index, scan.index...) + result.Index = append(result.Index, i) + ok = true + continue + } + + // Queue embedded struct fields for processing with next level, + // but only if we haven't seen a match yet at this level and only + // if the embedded types haven't already been queued. + if ok || ntyp == nil || ntyp.Kind() != abi.Struct { + continue + } + styp := (*structType)(unsafe.Pointer(ntyp)) + if nextCount[styp] > 0 { + nextCount[styp] = 2 // exact multiple doesn't matter + continue + } + if nextCount == nil { + nextCount = map[*structType]int{} + } + nextCount[styp] = 1 + if count[t] > 1 { + nextCount[styp] = 2 // exact multiple doesn't matter + } + var index []int + index = append(index, scan.index...) + index = append(index, i) + next = append(next, fieldScan{styp, index}) + } + } + if ok { + break + } + } + return +} + +// FieldByName returns the struct field with the given name +// and a boolean to indicate if the field was found. +func (t *structType) FieldByName(name string) (f StructField, present bool) { + // Quick check for top-level name, or struct without embedded fields. + hasEmbeds := false + if name != "" { + for i := range t.Fields { + tf := &t.Fields[i] + if tf.Name.Name() == name { + return t.Field(i), true + } + if tf.Embedded() { + hasEmbeds = true + } + } + } + if !hasEmbeds { + return + } + return t.FieldByNameFunc(func(s string) bool { return s == name }) +} + +// TypeOf returns the reflection Type that represents the dynamic type of i. +// If i is a nil interface value, TypeOf returns nil. +func TypeOf(i any) Type { + eface := *(*emptyInterface)(unsafe.Pointer(&i)) + // Noescape so this doesn't make i to escape. See the comment + // at Value.typ for why this is safe. + return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ)))) +} + +// rtypeOf directly extracts the *rtype of the provided value. +func rtypeOf(i any) *abi.Type { + eface := *(*emptyInterface)(unsafe.Pointer(&i)) + return eface.typ +} + +// ptrMap is the cache for PointerTo. +var ptrMap sync.Map // map[*rtype]*ptrType + +// PtrTo returns the pointer type with element t. +// For example, if t represents type Foo, PtrTo(t) represents *Foo. +// +// PtrTo is the old spelling of PointerTo. +// The two functions behave identically. +func PtrTo(t Type) Type { return PointerTo(t) } + +// PointerTo returns the pointer type with element t. +// For example, if t represents type Foo, PointerTo(t) represents *Foo. +func PointerTo(t Type) Type { + return toRType(t.(*rtype).ptrTo()) +} + +func (t *rtype) ptrTo() *abi.Type { + at := &t.t + if at.PtrToThis != 0 { + return t.typeOff(at.PtrToThis) + } + + // Check the cache. + if pi, ok := ptrMap.Load(t); ok { + return &pi.(*ptrType).Type + } + + // Look in known types. + s := "*" + t.String() + for _, tt := range typesByString(s) { + p := (*ptrType)(unsafe.Pointer(tt)) + if p.Elem != &t.t { + continue + } + pi, _ := ptrMap.LoadOrStore(t, p) + return &pi.(*ptrType).Type + } + + // Create a new ptrType starting with the description + // of an *unsafe.Pointer. + var iptr any = (*unsafe.Pointer)(nil) + prototype := *(**ptrType)(unsafe.Pointer(&iptr)) + pp := *prototype + + pp.Str = resolveReflectName(newName(s, "", false, false)) + pp.PtrToThis = 0 + + // For the type structures linked into the binary, the + // compiler provides a good hash of the string. + // Create a good hash for the new string by using + // the FNV-1 hash's mixing function to combine the + // old hash and the new "*". + pp.Hash = fnv1(t.t.Hash, '*') + + pp.Elem = at + + pi, _ := ptrMap.LoadOrStore(t, &pp) + return &pi.(*ptrType).Type +} + +func ptrTo(t *abi.Type) *abi.Type { + return toRType(t).ptrTo() +} + +// fnv1 incorporates the list of bytes into the hash x using the FNV-1 hash function. +func fnv1(x uint32, list ...byte) uint32 { + for _, b := range list { + x = x*16777619 ^ uint32(b) + } + return x +} + +func (t *rtype) Implements(u Type) bool { + if u == nil { + panic("reflect: nil type passed to Type.Implements") + } + if u.Kind() != Interface { + panic("reflect: non-interface type passed to Type.Implements") + } + return implements(u.common(), t.common()) +} + +func (t *rtype) AssignableTo(u Type) bool { + if u == nil { + panic("reflect: nil type passed to Type.AssignableTo") + } + uu := u.common() + return directlyAssignable(uu, t.common()) || implements(uu, t.common()) +} + +func (t *rtype) ConvertibleTo(u Type) bool { + if u == nil { + panic("reflect: nil type passed to Type.ConvertibleTo") + } + return convertOp(u.common(), t.common()) != nil +} + +func (t *rtype) Comparable() bool { + return t.t.Equal != nil +} + +// implements reports whether the type V implements the interface type T. +func implements(T, V *abi.Type) bool { + if T.Kind() != abi.Interface { + return false + } + t := (*interfaceType)(unsafe.Pointer(T)) + if len(t.Methods) == 0 { + return true + } + + // The same algorithm applies in both cases, but the + // method tables for an interface type and a concrete type + // are different, so the code is duplicated. + // In both cases the algorithm is a linear scan over the two + // lists - T's methods and V's methods - simultaneously. + // Since method tables are stored in a unique sorted order + // (alphabetical, with no duplicate method names), the scan + // through V's methods must hit a match for each of T's + // methods along the way, or else V does not implement T. + // This lets us run the scan in overall linear time instead of + // the quadratic time a naive search would require. + // See also ../runtime/iface.go. + if V.Kind() == abi.Interface { + v := (*interfaceType)(unsafe.Pointer(V)) + i := 0 + for j := 0; j < len(v.Methods); j++ { + tm := &t.Methods[i] + tmName := t.nameOff(tm.Name) + vm := &v.Methods[j] + vmName := nameOffFor(V, vm.Name) + if vmName.Name() == tmName.Name() && typeOffFor(V, vm.Typ) == t.typeOff(tm.Typ) { + if !tmName.IsExported() { + tmPkgPath := pkgPath(tmName) + if tmPkgPath == "" { + tmPkgPath = t.PkgPath.Name() + } + vmPkgPath := pkgPath(vmName) + if vmPkgPath == "" { + vmPkgPath = v.PkgPath.Name() + } + if tmPkgPath != vmPkgPath { + continue + } + } + if i++; i >= len(t.Methods) { + return true + } + } + } + return false + } + + v := V.Uncommon() + if v == nil { + return false + } + i := 0 + vmethods := v.Methods() + for j := 0; j < int(v.Mcount); j++ { + tm := &t.Methods[i] + tmName := t.nameOff(tm.Name) + vm := vmethods[j] + vmName := nameOffFor(V, vm.Name) + if vmName.Name() == tmName.Name() && typeOffFor(V, vm.Mtyp) == t.typeOff(tm.Typ) { + if !tmName.IsExported() { + tmPkgPath := pkgPath(tmName) + if tmPkgPath == "" { + tmPkgPath = t.PkgPath.Name() + } + vmPkgPath := pkgPath(vmName) + if vmPkgPath == "" { + vmPkgPath = nameOffFor(V, v.PkgPath).Name() + } + if tmPkgPath != vmPkgPath { + continue + } + } + if i++; i >= len(t.Methods) { + return true + } + } + } + return false +} + +// specialChannelAssignability reports whether a value x of channel type V +// can be directly assigned (using memmove) to another channel type T. +// https://golang.org/doc/go_spec.html#Assignability +// T and V must be both of Chan kind. +func specialChannelAssignability(T, V *abi.Type) bool { + // Special case: + // x is a bidirectional channel value, T is a channel type, + // x's type V and T have identical element types, + // and at least one of V or T is not a defined type. + return V.ChanDir() == abi.BothDir && (nameFor(T) == "" || nameFor(V) == "") && haveIdenticalType(T.Elem(), V.Elem(), true) +} + +// directlyAssignable reports whether a value x of type V can be directly +// assigned (using memmove) to a value of type T. +// https://golang.org/doc/go_spec.html#Assignability +// Ignoring the interface rules (implemented elsewhere) +// and the ideal constant rules (no ideal constants at run time). +func directlyAssignable(T, V *abi.Type) bool { + // x's type V is identical to T? + if T == V { + return true + } + + // Otherwise at least one of T and V must not be defined + // and they must have the same kind. + if T.HasName() && V.HasName() || T.Kind() != V.Kind() { + return false + } + + if T.Kind() == abi.Chan && specialChannelAssignability(T, V) { + return true + } + + // x's type T and V must have identical underlying types. + return haveIdenticalUnderlyingType(T, V, true) +} + +func haveIdenticalType(T, V *abi.Type, cmpTags bool) bool { + if cmpTags { + return T == V + } + + if nameFor(T) != nameFor(V) || T.Kind() != V.Kind() || pkgPathFor(T) != pkgPathFor(V) { + return false + } + + return haveIdenticalUnderlyingType(T, V, false) +} + +func haveIdenticalUnderlyingType(T, V *abi.Type, cmpTags bool) bool { + if T == V { + return true + } + + kind := Kind(T.Kind()) + if kind != Kind(V.Kind()) { + return false + } + + // Non-composite types of equal kind have same underlying type + // (the predefined instance of the type). + if Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer { + return true + } + + // Composite types. + switch kind { + case Array: + return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case Chan: + return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case Func: + t := (*funcType)(unsafe.Pointer(T)) + v := (*funcType)(unsafe.Pointer(V)) + if t.OutCount != v.OutCount || t.InCount != v.InCount { + return false + } + for i := 0; i < t.NumIn(); i++ { + if !haveIdenticalType(t.In(i), v.In(i), cmpTags) { + return false + } + } + for i := 0; i < t.NumOut(); i++ { + if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) { + return false + } + } + return true + + case Interface: + t := (*interfaceType)(unsafe.Pointer(T)) + v := (*interfaceType)(unsafe.Pointer(V)) + if len(t.Methods) == 0 && len(v.Methods) == 0 { + return true + } + // Might have the same methods but still + // need a run time conversion. + return false + + case Map: + return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case Pointer, Slice: + return haveIdenticalType(T.Elem(), V.Elem(), cmpTags) + + case Struct: + t := (*structType)(unsafe.Pointer(T)) + v := (*structType)(unsafe.Pointer(V)) + if len(t.Fields) != len(v.Fields) { + return false + } + if t.PkgPath.Name() != v.PkgPath.Name() { + return false + } + for i := range t.Fields { + tf := &t.Fields[i] + vf := &v.Fields[i] + if tf.Name.Name() != vf.Name.Name() { + return false + } + if !haveIdenticalType(tf.Typ, vf.Typ, cmpTags) { + return false + } + if cmpTags && tf.Name.Tag() != vf.Name.Tag() { + return false + } + if tf.Offset != vf.Offset { + return false + } + if tf.Embedded() != vf.Embedded() { + return false + } + } + return true + } + + return false +} + +// typelinks is implemented in package runtime. +// It returns a slice of the sections in each module, +// and a slice of *rtype offsets in each module. +// +// The types in each module are sorted by string. That is, the first +// two linked types of the first module are: +// +// d0 := sections[0] +// t1 := (*rtype)(add(d0, offset[0][0])) +// t2 := (*rtype)(add(d0, offset[0][1])) +// +// and +// +// t1.String() < t2.String() +// +// Note that strings are not unique identifiers for types: +// there can be more than one with a given string. +// Only types we might want to look up are included: +// pointers, channels, maps, slices, and arrays. +func typelinks() (sections []unsafe.Pointer, offset [][]int32) + +func rtypeOff(section unsafe.Pointer, off int32) *abi.Type { + return (*abi.Type)(add(section, uintptr(off), "sizeof(rtype) > 0")) +} + +// typesByString returns the subslice of typelinks() whose elements have +// the given string representation. +// It may be empty (no known types with that string) or may have +// multiple elements (multiple types with that string). +func typesByString(s string) []*abi.Type { + sections, offset := typelinks() + var ret []*abi.Type + + for offsI, offs := range offset { + section := sections[offsI] + + // We are looking for the first index i where the string becomes >= s. + // This is a copy of sort.Search, with f(h) replaced by (*typ[h].String() >= s). + i, j := 0, len(offs) + for i < j { + h := i + (j-i)>>1 // avoid overflow when computing h + // i ≤ h < j + if !(stringFor(rtypeOff(section, offs[h])) >= s) { + i = h + 1 // preserves f(i-1) == false + } else { + j = h // preserves f(j) == true + } + } + // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. + + // Having found the first, linear scan forward to find the last. + // We could do a second binary search, but the caller is going + // to do a linear scan anyway. + for j := i; j < len(offs); j++ { + typ := rtypeOff(section, offs[j]) + if stringFor(typ) != s { + break + } + ret = append(ret, typ) + } + } + return ret +} + +// The lookupCache caches ArrayOf, ChanOf, MapOf and SliceOf lookups. +var lookupCache sync.Map // map[cacheKey]*rtype + +// A cacheKey is the key for use in the lookupCache. +// Four values describe any of the types we are looking for: +// type kind, one or two subtypes, and an extra integer. +type cacheKey struct { + kind Kind + t1 *abi.Type + t2 *abi.Type + extra uintptr +} + +// The funcLookupCache caches FuncOf lookups. +// FuncOf does not share the common lookupCache since cacheKey is not +// sufficient to represent functions unambiguously. +var funcLookupCache struct { + sync.Mutex // Guards stores (but not loads) on m. + + // m is a map[uint32][]*rtype keyed by the hash calculated in FuncOf. + // Elements of m are append-only and thus safe for concurrent reading. + m sync.Map +} + +// ChanOf returns the channel type with the given direction and element type. +// For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int. +// +// The gc runtime imposes a limit of 64 kB on channel element types. +// If t's size is equal to or exceeds this limit, ChanOf panics. +func ChanOf(dir ChanDir, t Type) Type { + typ := t.common() + + // Look in cache. + ckey := cacheKey{Chan, typ, nil, uintptr(dir)} + if ch, ok := lookupCache.Load(ckey); ok { + return ch.(*rtype) + } + + // This restriction is imposed by the gc compiler and the runtime. + if typ.Size_ >= 1<<16 { + panic("reflect.ChanOf: element size too large") + } + + // Look in known types. + var s string + switch dir { + default: + panic("reflect.ChanOf: invalid dir") + case SendDir: + s = "chan<- " + stringFor(typ) + case RecvDir: + s = "<-chan " + stringFor(typ) + case BothDir: + typeStr := stringFor(typ) + if typeStr[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 + s = "chan (" + typeStr + ")" + } else { + s = "chan " + typeStr + } + } + for _, tt := range typesByString(s) { + ch := (*chanType)(unsafe.Pointer(tt)) + if ch.Elem == typ && ch.Dir == abi.ChanDir(dir) { + ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + return ti.(Type) + } + } + + // Make a channel type. + var ichan any = (chan unsafe.Pointer)(nil) + prototype := *(**chanType)(unsafe.Pointer(&ichan)) + ch := *prototype + ch.TFlag = abi.TFlagRegularMemory + ch.Dir = abi.ChanDir(dir) + ch.Str = resolveReflectName(newName(s, "", false, false)) + ch.Hash = fnv1(typ.Hash, 'c', byte(dir)) + ch.Elem = typ + + ti, _ := lookupCache.LoadOrStore(ckey, toRType(&ch.Type)) + return ti.(Type) +} + +// MapOf returns the map type with the given key and element types. +// For example, if k represents int and e represents string, +// MapOf(k, e) represents map[int]string. +// +// If the key type is not a valid map key type (that is, if it does +// not implement Go's == operator), MapOf panics. +func MapOf(key, elem Type) Type { + ktyp := key.common() + etyp := elem.common() + + if ktyp.Equal == nil { + panic("reflect.MapOf: invalid key type " + stringFor(ktyp)) + } + + // Look in cache. + ckey := cacheKey{Map, ktyp, etyp, 0} + if mt, ok := lookupCache.Load(ckey); ok { + return mt.(Type) + } + + // Look in known types. + s := "map[" + stringFor(ktyp) + "]" + stringFor(etyp) + for _, tt := range typesByString(s) { + mt := (*mapType)(unsafe.Pointer(tt)) + if mt.Key == ktyp && mt.Elem == etyp { + ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + return ti.(Type) + } + } + + // Make a map type. + // Note: flag values must match those used in the TMAP case + // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. + var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil) + mt := **(**mapType)(unsafe.Pointer(&imap)) + mt.Str = resolveReflectName(newName(s, "", false, false)) + mt.TFlag = 0 + mt.Hash = fnv1(etyp.Hash, 'm', byte(ktyp.Hash>>24), byte(ktyp.Hash>>16), byte(ktyp.Hash>>8), byte(ktyp.Hash)) + mt.Key = ktyp + mt.Elem = etyp + mt.Bucket = bucketOf(ktyp, etyp) + mt.Hasher = func(p unsafe.Pointer, seed uintptr) uintptr { + return typehash(ktyp, p, seed) + } + mt.Flags = 0 + if ktyp.Size_ > maxKeySize { + mt.KeySize = uint8(goarch.PtrSize) + mt.Flags |= 1 // indirect key + } else { + mt.KeySize = uint8(ktyp.Size_) + } + if etyp.Size_ > maxValSize { + mt.ValueSize = uint8(goarch.PtrSize) + mt.Flags |= 2 // indirect value + } else { + mt.MapType.ValueSize = uint8(etyp.Size_) + } + mt.MapType.BucketSize = uint16(mt.Bucket.Size_) + if isReflexive(ktyp) { + mt.Flags |= 4 + } + if needKeyUpdate(ktyp) { + mt.Flags |= 8 + } + if hashMightPanic(ktyp) { + mt.Flags |= 16 + } + mt.PtrToThis = 0 + + ti, _ := lookupCache.LoadOrStore(ckey, toRType(&mt.Type)) + return ti.(Type) +} + +var funcTypes []Type +var funcTypesMutex sync.Mutex + +func initFuncTypes(n int) Type { + funcTypesMutex.Lock() + defer funcTypesMutex.Unlock() + if n >= len(funcTypes) { + newFuncTypes := make([]Type, n+1) + copy(newFuncTypes, funcTypes) + funcTypes = newFuncTypes + } + if funcTypes[n] != nil { + return funcTypes[n] + } + + funcTypes[n] = StructOf([]StructField{ + { + Name: "FuncType", + Type: TypeOf(funcType{}), + }, + { + Name: "Args", + Type: ArrayOf(n, TypeOf(&rtype{})), + }, + }) + return funcTypes[n] +} + +// FuncOf returns the function type with the given argument and result types. +// For example if k represents int and e represents string, +// FuncOf([]Type{k}, []Type{e}, false) represents func(int) string. +// +// The variadic argument controls whether the function is variadic. FuncOf +// panics if the in[len(in)-1] does not represent a slice and variadic is +// true. +func FuncOf(in, out []Type, variadic bool) Type { + if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { + panic("reflect.FuncOf: last arg of variadic func must be slice") + } + + // Make a func type. + var ifunc any = (func())(nil) + prototype := *(**funcType)(unsafe.Pointer(&ifunc)) + n := len(in) + len(out) + + if n > 128 { + panic("reflect.FuncOf: too many arguments") + } + + o := New(initFuncTypes(n)).Elem() + ft := (*funcType)(unsafe.Pointer(o.Field(0).Addr().Pointer())) + args := unsafe.Slice((**rtype)(unsafe.Pointer(o.Field(1).Addr().Pointer())), n)[0:0:n] + *ft = *prototype + + // Build a hash and minimally populate ft. + var hash uint32 + for _, in := range in { + t := in.(*rtype) + args = append(args, t) + hash = fnv1(hash, byte(t.t.Hash>>24), byte(t.t.Hash>>16), byte(t.t.Hash>>8), byte(t.t.Hash)) + } + if variadic { + hash = fnv1(hash, 'v') + } + hash = fnv1(hash, '.') + for _, out := range out { + t := out.(*rtype) + args = append(args, t) + hash = fnv1(hash, byte(t.t.Hash>>24), byte(t.t.Hash>>16), byte(t.t.Hash>>8), byte(t.t.Hash)) + } + + ft.TFlag = 0 + ft.Hash = hash + ft.InCount = uint16(len(in)) + ft.OutCount = uint16(len(out)) + if variadic { + ft.OutCount |= 1 << 15 + } + + // Look in cache. + if ts, ok := funcLookupCache.m.Load(hash); ok { + for _, t := range ts.([]*abi.Type) { + if haveIdenticalUnderlyingType(&ft.Type, t, true) { + return toRType(t) + } + } + } + + // Not in cache, lock and retry. + funcLookupCache.Lock() + defer funcLookupCache.Unlock() + if ts, ok := funcLookupCache.m.Load(hash); ok { + for _, t := range ts.([]*abi.Type) { + if haveIdenticalUnderlyingType(&ft.Type, t, true) { + return toRType(t) + } + } + } + + addToCache := func(tt *abi.Type) Type { + var rts []*abi.Type + if rti, ok := funcLookupCache.m.Load(hash); ok { + rts = rti.([]*abi.Type) + } + funcLookupCache.m.Store(hash, append(rts, tt)) + return toType(tt) + } + + // Look in known types for the same string representation. + str := funcStr(ft) + for _, tt := range typesByString(str) { + if haveIdenticalUnderlyingType(&ft.Type, tt, true) { + return addToCache(tt) + } + } + + // Populate the remaining fields of ft and store in cache. + ft.Str = resolveReflectName(newName(str, "", false, false)) + ft.PtrToThis = 0 + return addToCache(&ft.Type) +} +func stringFor(t *abi.Type) string { + return toRType(t).String() +} + +// funcStr builds a string representation of a funcType. +func funcStr(ft *funcType) string { + repr := make([]byte, 0, 64) + repr = append(repr, "func("...) + for i, t := range ft.InSlice() { + if i > 0 { + repr = append(repr, ", "...) + } + if ft.IsVariadic() && i == int(ft.InCount)-1 { + repr = append(repr, "..."...) + repr = append(repr, stringFor((*sliceType)(unsafe.Pointer(t)).Elem)...) + } else { + repr = append(repr, stringFor(t)...) + } + } + repr = append(repr, ')') + out := ft.OutSlice() + if len(out) == 1 { + repr = append(repr, ' ') + } else if len(out) > 1 { + repr = append(repr, " ("...) + } + for i, t := range out { + if i > 0 { + repr = append(repr, ", "...) + } + repr = append(repr, stringFor(t)...) + } + if len(out) > 1 { + repr = append(repr, ')') + } + return string(repr) +} + +// isReflexive reports whether the == operation on the type is reflexive. +// That is, x == x for all values x of type t. +func isReflexive(t *abi.Type) bool { + switch Kind(t.Kind()) { + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Pointer, String, UnsafePointer: + return true + case Float32, Float64, Complex64, Complex128, Interface: + return false + case Array: + tt := (*arrayType)(unsafe.Pointer(t)) + return isReflexive(tt.Elem) + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for _, f := range tt.Fields { + if !isReflexive(f.Typ) { + return false + } + } + return true + default: + // Func, Map, Slice, Invalid + panic("isReflexive called on non-key type " + stringFor(t)) + } +} + +// needKeyUpdate reports whether map overwrites require the key to be copied. +func needKeyUpdate(t *abi.Type) bool { + switch Kind(t.Kind()) { + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Pointer, UnsafePointer: + return false + case Float32, Float64, Complex64, Complex128, Interface, String: + // Float keys can be updated from +0 to -0. + // String keys can be updated to use a smaller backing store. + // Interfaces might have floats of strings in them. + return true + case Array: + tt := (*arrayType)(unsafe.Pointer(t)) + return needKeyUpdate(tt.Elem) + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for _, f := range tt.Fields { + if needKeyUpdate(f.Typ) { + return true + } + } + return false + default: + // Func, Map, Slice, Invalid + panic("needKeyUpdate called on non-key type " + stringFor(t)) + } +} + +// hashMightPanic reports whether the hash of a map key of type t might panic. +func hashMightPanic(t *abi.Type) bool { + switch Kind(t.Kind()) { + case Interface: + return true + case Array: + tt := (*arrayType)(unsafe.Pointer(t)) + return hashMightPanic(tt.Elem) + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for _, f := range tt.Fields { + if hashMightPanic(f.Typ) { + return true + } + } + return false + default: + return false + } +} + +// Make sure these routines stay in sync with ../runtime/map.go! +// These types exist only for GC, so we only fill out GC relevant info. +// Currently, that's just size and the GC program. We also fill in string +// for possible debugging use. +const ( + bucketSize uintptr = abi.MapBucketCount + maxKeySize uintptr = abi.MapMaxKeyBytes + maxValSize uintptr = abi.MapMaxElemBytes +) + +func bucketOf(ktyp, etyp *abi.Type) *abi.Type { + if ktyp.Size_ > maxKeySize { + ktyp = ptrTo(ktyp) + } + if etyp.Size_ > maxValSize { + etyp = ptrTo(etyp) + } + + // Prepare GC data if any. + // A bucket is at most bucketSize*(1+maxKeySize+maxValSize)+ptrSize bytes, + // or 2064 bytes, or 258 pointer-size words, or 33 bytes of pointer bitmap. + // Note that since the key and value are known to be <= 128 bytes, + // they're guaranteed to have bitmaps instead of GC programs. + var gcdata *byte + var ptrdata uintptr + + size := bucketSize*(1+ktyp.Size_+etyp.Size_) + goarch.PtrSize + if size&uintptr(ktyp.Align_-1) != 0 || size&uintptr(etyp.Align_-1) != 0 { + panic("reflect: bad size computation in MapOf") + } + + if ktyp.PtrBytes != 0 || etyp.PtrBytes != 0 { + nptr := (bucketSize*(1+ktyp.Size_+etyp.Size_) + goarch.PtrSize) / goarch.PtrSize + n := (nptr + 7) / 8 + + // Runtime needs pointer masks to be a multiple of uintptr in size. + n = (n + goarch.PtrSize - 1) &^ (goarch.PtrSize - 1) + mask := make([]byte, n) + base := bucketSize / goarch.PtrSize + + if ktyp.PtrBytes != 0 { + emitGCMask(mask, base, ktyp, bucketSize) + } + base += bucketSize * ktyp.Size_ / goarch.PtrSize + + if etyp.PtrBytes != 0 { + emitGCMask(mask, base, etyp, bucketSize) + } + base += bucketSize * etyp.Size_ / goarch.PtrSize + + word := base + mask[word/8] |= 1 << (word % 8) + gcdata = &mask[0] + ptrdata = (word + 1) * goarch.PtrSize + + // overflow word must be last + if ptrdata != size { + panic("reflect: bad layout computation in MapOf") + } + } + + b := &abi.Type{ + Align_: goarch.PtrSize, + Size_: size, + Kind_: uint8(Struct), + PtrBytes: ptrdata, + GCData: gcdata, + } + s := "bucket(" + stringFor(ktyp) + "," + stringFor(etyp) + ")" + b.Str = resolveReflectName(newName(s, "", false, false)) + return b +} + +func (t *rtype) gcSlice(begin, end uintptr) []byte { + return (*[1 << 30]byte)(unsafe.Pointer(t.t.GCData))[begin:end:end] +} + +// emitGCMask writes the GC mask for [n]typ into out, starting at bit +// offset base. +func emitGCMask(out []byte, base uintptr, typ *abi.Type, n uintptr) { + if typ.Kind_&kindGCProg != 0 { + panic("reflect: unexpected GC program") + } + ptrs := typ.PtrBytes / goarch.PtrSize + words := typ.Size_ / goarch.PtrSize + mask := typ.GcSlice(0, (ptrs+7)/8) + for j := uintptr(0); j < ptrs; j++ { + if (mask[j/8]>>(j%8))&1 != 0 { + for i := uintptr(0); i < n; i++ { + k := base + i*words + j + out[k/8] |= 1 << (k % 8) + } + } + } +} + +// appendGCProg appends the GC program for the first ptrdata bytes of +// typ to dst and returns the extended slice. +func appendGCProg(dst []byte, typ *abi.Type) []byte { + if typ.Kind_&kindGCProg != 0 { + // Element has GC program; emit one element. + n := uintptr(*(*uint32)(unsafe.Pointer(typ.GCData))) + prog := typ.GcSlice(4, 4+n-1) + return append(dst, prog...) + } + + // Element is small with pointer mask; use as literal bits. + ptrs := typ.PtrBytes / goarch.PtrSize + mask := typ.GcSlice(0, (ptrs+7)/8) + + // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes). + for ; ptrs > 120; ptrs -= 120 { + dst = append(dst, 120) + dst = append(dst, mask[:15]...) + mask = mask[15:] + } + + dst = append(dst, byte(ptrs)) + dst = append(dst, mask...) + return dst +} + +// SliceOf returns the slice type with element type t. +// For example, if t represents int, SliceOf(t) represents []int. +func SliceOf(t Type) Type { + typ := t.common() + + // Look in cache. + ckey := cacheKey{Slice, typ, nil, 0} + if slice, ok := lookupCache.Load(ckey); ok { + return slice.(Type) + } + + // Look in known types. + s := "[]" + stringFor(typ) + for _, tt := range typesByString(s) { + slice := (*sliceType)(unsafe.Pointer(tt)) + if slice.Elem == typ { + ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + return ti.(Type) + } + } + + // Make a slice type. + var islice any = ([]unsafe.Pointer)(nil) + prototype := *(**sliceType)(unsafe.Pointer(&islice)) + slice := *prototype + slice.TFlag = 0 + slice.Str = resolveReflectName(newName(s, "", false, false)) + slice.Hash = fnv1(typ.Hash, '[') + slice.Elem = typ + slice.PtrToThis = 0 + + ti, _ := lookupCache.LoadOrStore(ckey, toRType(&slice.Type)) + return ti.(Type) +} + +// The structLookupCache caches StructOf lookups. +// StructOf does not share the common lookupCache since we need to pin +// the memory associated with *structTypeFixedN. +var structLookupCache struct { + sync.Mutex // Guards stores (but not loads) on m. + + // m is a map[uint32][]Type keyed by the hash calculated in StructOf. + // Elements in m are append-only and thus safe for concurrent reading. + m sync.Map +} + +type structTypeUncommon struct { + structType + u uncommonType +} + +// isLetter reports whether a given 'rune' is classified as a Letter. +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch) +} + +// isValidFieldName checks if a string is a valid (struct) field name or not. +// +// According to the language spec, a field name should be an identifier. +// +// identifier = letter { letter | unicode_digit } . +// letter = unicode_letter | "_" . +func isValidFieldName(fieldName string) bool { + for i, c := range fieldName { + if i == 0 && !isLetter(c) { + return false + } + + if !(isLetter(c) || unicode.IsDigit(c)) { + return false + } + } + + return len(fieldName) > 0 +} + +// StructOf returns the struct type containing fields. +// The Offset and Index fields are ignored and computed as they would be +// by the compiler. +// +// StructOf currently does not generate wrapper methods for embedded +// fields and panics if passed unexported StructFields. +// These limitations may be lifted in a future version. +func StructOf(fields []StructField) Type { + var ( + hash = fnv1(0, []byte("struct {")...) + size uintptr + typalign uint8 + comparable = true + methods []abi.Method + + fs = make([]structField, len(fields)) + repr = make([]byte, 0, 64) + fset = map[string]struct{}{} // fields' names + + hasGCProg = false // records whether a struct-field type has a GCProg + ) + + lastzero := uintptr(0) + repr = append(repr, "struct {"...) + pkgpath := "" + for i, field := range fields { + if field.Name == "" { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name") + } + if !isValidFieldName(field.Name) { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has invalid name") + } + if field.Type == nil { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") + } + f, fpkgpath := runtimeStructField(field) + ft := f.Typ + if ft.Kind_&kindGCProg != 0 { + hasGCProg = true + } + if fpkgpath != "" { + if pkgpath == "" { + pkgpath = fpkgpath + } else if pkgpath != fpkgpath { + panic("reflect.Struct: fields with different PkgPath " + pkgpath + " and " + fpkgpath) + } + } + + // Update string and hash + name := f.Name.Name() + hash = fnv1(hash, []byte(name)...) + repr = append(repr, (" " + name)...) + if f.Embedded() { + // Embedded field + if f.Typ.Kind() == abi.Pointer { + // Embedded ** and *interface{} are illegal + elem := ft.Elem() + if k := elem.Kind(); k == abi.Pointer || k == abi.Interface { + panic("reflect.StructOf: illegal embedded field type " + stringFor(ft)) + } + } + + switch Kind(f.Typ.Kind()) { + case Interface: + ift := (*interfaceType)(unsafe.Pointer(ft)) + for im, m := range ift.Methods { + if pkgPath(ift.nameOff(m.Name)) != "" { + // TODO(sbinet). Issue 15924. + panic("reflect: embedded interface with unexported method(s) not implemented") + } + + var ( + mtyp = ift.typeOff(m.Typ) + ifield = i + imethod = im + ifn Value + tfn Value + ) + + if ft.Kind_&kindDirectIface != 0 { + tfn = MakeFunc(toRType(mtyp), func(in []Value) []Value { + var args []Value + var recv = in[0] + if len(in) > 1 { + args = in[1:] + } + return recv.Field(ifield).Method(imethod).Call(args) + }) + ifn = MakeFunc(toRType(mtyp), func(in []Value) []Value { + var args []Value + var recv = in[0] + if len(in) > 1 { + args = in[1:] + } + return recv.Field(ifield).Method(imethod).Call(args) + }) + } else { + tfn = MakeFunc(toRType(mtyp), func(in []Value) []Value { + var args []Value + var recv = in[0] + if len(in) > 1 { + args = in[1:] + } + return recv.Field(ifield).Method(imethod).Call(args) + }) + ifn = MakeFunc(toRType(mtyp), func(in []Value) []Value { + var args []Value + var recv = Indirect(in[0]) + if len(in) > 1 { + args = in[1:] + } + return recv.Field(ifield).Method(imethod).Call(args) + }) + } + + methods = append(methods, abi.Method{ + Name: resolveReflectName(ift.nameOff(m.Name)), + Mtyp: resolveReflectType(mtyp), + Ifn: resolveReflectText(unsafe.Pointer(&ifn)), + Tfn: resolveReflectText(unsafe.Pointer(&tfn)), + }) + } + case Pointer: + ptr := (*ptrType)(unsafe.Pointer(ft)) + if unt := ptr.Uncommon(); unt != nil { + if i > 0 && unt.Mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 { + panic("reflect: embedded type with methods not implemented if there is more than one field") + } + for _, m := range unt.Methods() { + mname := nameOffFor(ft, m.Name) + if pkgPath(mname) != "" { + // TODO(sbinet). + // Issue 15924. + panic("reflect: embedded interface with unexported method(s) not implemented") + } + methods = append(methods, abi.Method{ + Name: resolveReflectName(mname), + Mtyp: resolveReflectType(typeOffFor(ft, m.Mtyp)), + Ifn: resolveReflectText(textOffFor(ft, m.Ifn)), + Tfn: resolveReflectText(textOffFor(ft, m.Tfn)), + }) + } + } + if unt := ptr.Elem.Uncommon(); unt != nil { + for _, m := range unt.Methods() { + mname := nameOffFor(ft, m.Name) + if pkgPath(mname) != "" { + // TODO(sbinet) + // Issue 15924. + panic("reflect: embedded interface with unexported method(s) not implemented") + } + methods = append(methods, abi.Method{ + Name: resolveReflectName(mname), + Mtyp: resolveReflectType(typeOffFor(ptr.Elem, m.Mtyp)), + Ifn: resolveReflectText(textOffFor(ptr.Elem, m.Ifn)), + Tfn: resolveReflectText(textOffFor(ptr.Elem, m.Tfn)), + }) + } + } + default: + if unt := ft.Uncommon(); unt != nil { + if i > 0 && unt.Mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 && ft.Kind_&kindDirectIface != 0 { + panic("reflect: embedded type with methods not implemented for non-pointer type") + } + for _, m := range unt.Methods() { + mname := nameOffFor(ft, m.Name) + if pkgPath(mname) != "" { + // TODO(sbinet) + // Issue 15924. + panic("reflect: embedded interface with unexported method(s) not implemented") + } + methods = append(methods, abi.Method{ + Name: resolveReflectName(mname), + Mtyp: resolveReflectType(typeOffFor(ft, m.Mtyp)), + Ifn: resolveReflectText(textOffFor(ft, m.Ifn)), + Tfn: resolveReflectText(textOffFor(ft, m.Tfn)), + }) + + } + } + } + } + if _, dup := fset[name]; dup && name != "_" { + panic("reflect.StructOf: duplicate field " + name) + } + fset[name] = struct{}{} + + hash = fnv1(hash, byte(ft.Hash>>24), byte(ft.Hash>>16), byte(ft.Hash>>8), byte(ft.Hash)) + + repr = append(repr, (" " + stringFor(ft))...) + if f.Name.HasTag() { + hash = fnv1(hash, []byte(f.Name.Tag())...) + repr = append(repr, (" " + strconv.Quote(f.Name.Tag()))...) + } + if i < len(fields)-1 { + repr = append(repr, ';') + } + + comparable = comparable && (ft.Equal != nil) + + offset := align(size, uintptr(ft.Align_)) + if offset < size { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + if ft.Align_ > typalign { + typalign = ft.Align_ + } + size = offset + ft.Size_ + if size < offset { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + f.Offset = offset + + if ft.Size_ == 0 { + lastzero = size + } + + fs[i] = f + } + + if size > 0 && lastzero == size { + // This is a non-zero sized struct that ends in a + // zero-sized field. We add an extra byte of padding, + // to ensure that taking the address of the final + // zero-sized field can't manufacture a pointer to the + // next object in the heap. See issue 9401. + size++ + if size == 0 { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + } + + var typ *structType + var ut *uncommonType + + if len(methods) == 0 { + t := new(structTypeUncommon) + typ = &t.structType + ut = &t.u + } else { + // A *rtype representing a struct is followed directly in memory by an + // array of method objects representing the methods attached to the + // struct. To get the same layout for a run time generated type, we + // need an array directly following the uncommonType memory. + // A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN. + tt := New(StructOf([]StructField{ + {Name: "S", Type: TypeOf(structType{})}, + {Name: "U", Type: TypeOf(uncommonType{})}, + {Name: "M", Type: ArrayOf(len(methods), TypeOf(methods[0]))}, + })) + + typ = (*structType)(tt.Elem().Field(0).Addr().UnsafePointer()) + ut = (*uncommonType)(tt.Elem().Field(1).Addr().UnsafePointer()) + + copy(tt.Elem().Field(2).Slice(0, len(methods)).Interface().([]abi.Method), methods) + } + // TODO(sbinet): Once we allow embedding multiple types, + // methods will need to be sorted like the compiler does. + // TODO(sbinet): Once we allow non-exported methods, we will + // need to compute xcount as the number of exported methods. + ut.Mcount = uint16(len(methods)) + ut.Xcount = ut.Mcount + ut.Moff = uint32(unsafe.Sizeof(uncommonType{})) + + if len(fs) > 0 { + repr = append(repr, ' ') + } + repr = append(repr, '}') + hash = fnv1(hash, '}') + str := string(repr) + + // Round the size up to be a multiple of the alignment. + s := align(size, uintptr(typalign)) + if s < size { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + size = s + + // Make the struct type. + var istruct any = struct{}{} + prototype := *(**structType)(unsafe.Pointer(&istruct)) + *typ = *prototype + typ.Fields = fs + if pkgpath != "" { + typ.PkgPath = newName(pkgpath, "", false, false) + } + + // Look in cache. + if ts, ok := structLookupCache.m.Load(hash); ok { + for _, st := range ts.([]Type) { + t := st.common() + if haveIdenticalUnderlyingType(&typ.Type, t, true) { + return toType(t) + } + } + } + + // Not in cache, lock and retry. + structLookupCache.Lock() + defer structLookupCache.Unlock() + if ts, ok := structLookupCache.m.Load(hash); ok { + for _, st := range ts.([]Type) { + t := st.common() + if haveIdenticalUnderlyingType(&typ.Type, t, true) { + return toType(t) + } + } + } + + addToCache := func(t Type) Type { + var ts []Type + if ti, ok := structLookupCache.m.Load(hash); ok { + ts = ti.([]Type) + } + structLookupCache.m.Store(hash, append(ts, t)) + return t + } + + // Look in known types. + for _, t := range typesByString(str) { + if haveIdenticalUnderlyingType(&typ.Type, t, true) { + // even if 't' wasn't a structType with methods, we should be ok + // as the 'u uncommonType' field won't be accessed except when + // tflag&abi.TFlagUncommon is set. + return addToCache(toType(t)) + } + } + + typ.Str = resolveReflectName(newName(str, "", false, false)) + typ.TFlag = 0 // TODO: set tflagRegularMemory + typ.Hash = hash + typ.Size_ = size + typ.PtrBytes = typeptrdata(&typ.Type) + typ.Align_ = typalign + typ.FieldAlign_ = typalign + typ.PtrToThis = 0 + if len(methods) > 0 { + typ.TFlag |= abi.TFlagUncommon + } + + if hasGCProg { + lastPtrField := 0 + for i, ft := range fs { + if ft.Typ.Pointers() { + lastPtrField = i + } + } + prog := []byte{0, 0, 0, 0} // will be length of prog + var off uintptr + for i, ft := range fs { + if i > lastPtrField { + // gcprog should not include anything for any field after + // the last field that contains pointer data + break + } + if !ft.Typ.Pointers() { + // Ignore pointerless fields. + continue + } + // Pad to start of this field with zeros. + if ft.Offset > off { + n := (ft.Offset - off) / goarch.PtrSize + prog = append(prog, 0x01, 0x00) // emit a 0 bit + if n > 1 { + prog = append(prog, 0x81) // repeat previous bit + prog = appendVarint(prog, n-1) // n-1 times + } + off = ft.Offset + } + + prog = appendGCProg(prog, ft.Typ) + off += ft.Typ.PtrBytes + } + prog = append(prog, 0) + *(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4) + typ.Kind_ |= kindGCProg + typ.GCData = &prog[0] + } else { + typ.Kind_ &^= kindGCProg + bv := new(bitVector) + addTypeBits(bv, 0, &typ.Type) + if len(bv.data) > 0 { + typ.GCData = &bv.data[0] + } + } + typ.Equal = nil + if comparable { + typ.Equal = func(p, q unsafe.Pointer) bool { + for _, ft := range typ.Fields { + pi := add(p, ft.Offset, "&x.field safe") + qi := add(q, ft.Offset, "&x.field safe") + if !ft.Typ.Equal(pi, qi) { + return false + } + } + return true + } + } + + switch { + case len(fs) == 1 && !ifaceIndir(fs[0].Typ): + // structs of 1 direct iface type can be direct + typ.Kind_ |= kindDirectIface + default: + typ.Kind_ &^= kindDirectIface + } + + return addToCache(toType(&typ.Type)) +} + +// runtimeStructField takes a StructField value passed to StructOf and +// returns both the corresponding internal representation, of type +// structField, and the pkgpath value to use for this field. +func runtimeStructField(field StructField) (structField, string) { + if field.Anonymous && field.PkgPath != "" { + panic("reflect.StructOf: field \"" + field.Name + "\" is anonymous but has PkgPath set") + } + + if field.IsExported() { + // Best-effort check for misuse. + // Since this field will be treated as exported, not much harm done if Unicode lowercase slips through. + c := field.Name[0] + if 'a' <= c && c <= 'z' || c == '_' { + panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath") + } + } + + resolveReflectType(field.Type.common()) // install in runtime + f := structField{ + Name: newName(field.Name, string(field.Tag), field.IsExported(), field.Anonymous), + Typ: field.Type.common(), + Offset: 0, + } + return f, field.PkgPath +} + +// typeptrdata returns the length in bytes of the prefix of t +// containing pointer data. Anything after this offset is scalar data. +// keep in sync with ../cmd/compile/internal/reflectdata/reflect.go +func typeptrdata(t *abi.Type) uintptr { + switch t.Kind() { + case abi.Struct: + st := (*structType)(unsafe.Pointer(t)) + // find the last field that has pointers. + field := -1 + for i := range st.Fields { + ft := st.Fields[i].Typ + if ft.Pointers() { + field = i + } + } + if field == -1 { + return 0 + } + f := st.Fields[field] + return f.Offset + f.Typ.PtrBytes + + default: + panic("reflect.typeptrdata: unexpected type, " + stringFor(t)) + } +} + +// See cmd/compile/internal/reflectdata/reflect.go for derivation of constant. +const maxPtrmaskBytes = 2048 + +// ArrayOf returns the array type with the given length and element type. +// For example, if t represents int, ArrayOf(5, t) represents [5]int. +// +// If the resulting type would be larger than the available address space, +// ArrayOf panics. +func ArrayOf(length int, elem Type) Type { + if length < 0 { + panic("reflect: negative length passed to ArrayOf") + } + + typ := elem.common() + + // Look in cache. + ckey := cacheKey{Array, typ, nil, uintptr(length)} + if array, ok := lookupCache.Load(ckey); ok { + return array.(Type) + } + + // Look in known types. + s := "[" + strconv.Itoa(length) + "]" + stringFor(typ) + for _, tt := range typesByString(s) { + array := (*arrayType)(unsafe.Pointer(tt)) + if array.Elem == typ { + ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + return ti.(Type) + } + } + + // Make an array type. + var iarray any = [1]unsafe.Pointer{} + prototype := *(**arrayType)(unsafe.Pointer(&iarray)) + array := *prototype + array.TFlag = typ.TFlag & abi.TFlagRegularMemory + array.Str = resolveReflectName(newName(s, "", false, false)) + array.Hash = fnv1(typ.Hash, '[') + for n := uint32(length); n > 0; n >>= 8 { + array.Hash = fnv1(array.Hash, byte(n)) + } + array.Hash = fnv1(array.Hash, ']') + array.Elem = typ + array.PtrToThis = 0 + if typ.Size_ > 0 { + max := ^uintptr(0) / typ.Size_ + if uintptr(length) > max { + panic("reflect.ArrayOf: array size would exceed virtual address space") + } + } + array.Size_ = typ.Size_ * uintptr(length) + if length > 0 && typ.PtrBytes != 0 { + array.PtrBytes = typ.Size_*uintptr(length-1) + typ.PtrBytes + } + array.Align_ = typ.Align_ + array.FieldAlign_ = typ.FieldAlign_ + array.Len = uintptr(length) + array.Slice = &(SliceOf(elem).(*rtype).t) + + switch { + case typ.PtrBytes == 0 || array.Size_ == 0: + // No pointers. + array.GCData = nil + array.PtrBytes = 0 + + case length == 1: + // In memory, 1-element array looks just like the element. + array.Kind_ |= typ.Kind_ & kindGCProg + array.GCData = typ.GCData + array.PtrBytes = typ.PtrBytes + + case typ.Kind_&kindGCProg == 0 && array.Size_ <= maxPtrmaskBytes*8*goarch.PtrSize: + // Element is small with pointer mask; array is still small. + // Create direct pointer mask by turning each 1 bit in elem + // into length 1 bits in larger mask. + n := (array.PtrBytes/goarch.PtrSize + 7) / 8 + // Runtime needs pointer masks to be a multiple of uintptr in size. + n = (n + goarch.PtrSize - 1) &^ (goarch.PtrSize - 1) + mask := make([]byte, n) + emitGCMask(mask, 0, typ, array.Len) + array.GCData = &mask[0] + + default: + // Create program that emits one element + // and then repeats to make the array. + prog := []byte{0, 0, 0, 0} // will be length of prog + prog = appendGCProg(prog, typ) + // Pad from ptrdata to size. + elemPtrs := typ.PtrBytes / goarch.PtrSize + elemWords := typ.Size_ / goarch.PtrSize + if elemPtrs < elemWords { + // Emit literal 0 bit, then repeat as needed. + prog = append(prog, 0x01, 0x00) + if elemPtrs+1 < elemWords { + prog = append(prog, 0x81) + prog = appendVarint(prog, elemWords-elemPtrs-1) + } + } + // Repeat length-1 times. + if elemWords < 0x80 { + prog = append(prog, byte(elemWords|0x80)) + } else { + prog = append(prog, 0x80) + prog = appendVarint(prog, elemWords) + } + prog = appendVarint(prog, uintptr(length)-1) + prog = append(prog, 0) + *(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4) + array.Kind_ |= kindGCProg + array.GCData = &prog[0] + array.PtrBytes = array.Size_ // overestimate but ok; must match program + } + + etyp := typ + esize := etyp.Size() + + array.Equal = nil + if eequal := etyp.Equal; eequal != nil { + array.Equal = func(p, q unsafe.Pointer) bool { + for i := 0; i < length; i++ { + pi := arrayAt(p, i, esize, "i < length") + qi := arrayAt(q, i, esize, "i < length") + if !eequal(pi, qi) { + return false + } + + } + return true + } + } + + switch { + case length == 1 && !ifaceIndir(typ): + // array of 1 direct iface type can be direct + array.Kind_ |= kindDirectIface + default: + array.Kind_ &^= kindDirectIface + } + + ti, _ := lookupCache.LoadOrStore(ckey, toRType(&array.Type)) + return ti.(Type) +} + +func appendVarint(x []byte, v uintptr) []byte { + for ; v >= 0x80; v >>= 7 { + x = append(x, byte(v|0x80)) + } + x = append(x, byte(v)) + return x +} + +// toType converts from a *rtype to a Type that can be returned +// to the client of package reflect. In gc, the only concern is that +// a nil *rtype must be replaced by a nil Type, but in gccgo this +// function takes care of ensuring that multiple *rtype for the same +// type are coalesced into a single Type. +func toType(t *abi.Type) Type { + if t == nil { + return nil + } + return toRType(t) +} + +type layoutKey struct { + ftyp *funcType // function signature + rcvr *abi.Type // receiver type, or nil if none +} + +type layoutType struct { + t *abi.Type + framePool *sync.Pool + abid abiDesc +} + +var layoutCache sync.Map // map[layoutKey]layoutType + +// funcLayout computes a struct type representing the layout of the +// stack-assigned function arguments and return values for the function +// type t. +// If rcvr != nil, rcvr specifies the type of the receiver. +// The returned type exists only for GC, so we only fill out GC relevant info. +// Currently, that's just size and the GC program. We also fill in +// the name for possible debugging use. +func funcLayout(t *funcType, rcvr *abi.Type) (frametype *abi.Type, framePool *sync.Pool, abid abiDesc) { + if t.Kind() != abi.Func { + panic("reflect: funcLayout of non-func type " + stringFor(&t.Type)) + } + if rcvr != nil && rcvr.Kind() == abi.Interface { + panic("reflect: funcLayout with interface receiver " + stringFor(rcvr)) + } + k := layoutKey{t, rcvr} + if lti, ok := layoutCache.Load(k); ok { + lt := lti.(layoutType) + return lt.t, lt.framePool, lt.abid + } + + // Compute the ABI layout. + abid = newAbiDesc(t, rcvr) + + // build dummy rtype holding gc program + x := &abi.Type{ + Align_: goarch.PtrSize, + // Don't add spill space here; it's only necessary in + // reflectcall's frame, not in the allocated frame. + // TODO(mknyszek): Remove this comment when register + // spill space in the frame is no longer required. + Size_: align(abid.retOffset+abid.ret.stackBytes, goarch.PtrSize), + PtrBytes: uintptr(abid.stackPtrs.n) * goarch.PtrSize, + } + if abid.stackPtrs.n > 0 { + x.GCData = &abid.stackPtrs.data[0] + } + + var s string + if rcvr != nil { + s = "methodargs(" + stringFor(rcvr) + ")(" + stringFor(&t.Type) + ")" + } else { + s = "funcargs(" + stringFor(&t.Type) + ")" + } + x.Str = resolveReflectName(newName(s, "", false, false)) + + // cache result for future callers + framePool = &sync.Pool{New: func() any { + return unsafe_New(x) + }} + lti, _ := layoutCache.LoadOrStore(k, layoutType{ + t: x, + framePool: framePool, + abid: abid, + }) + lt := lti.(layoutType) + return lt.t, lt.framePool, lt.abid +} + +// ifaceIndir reports whether t is stored indirectly in an interface value. +func ifaceIndir(t *abi.Type) bool { + return t.Kind_&kindDirectIface == 0 +} + +// Note: this type must agree with runtime.bitvector. +type bitVector struct { + n uint32 // number of bits + data []byte +} + +// append a bit to the bitmap. +func (bv *bitVector) append(bit uint8) { + if bv.n%(8*goarch.PtrSize) == 0 { + // Runtime needs pointer masks to be a multiple of uintptr in size. + // Since reflect passes bv.data directly to the runtime as a pointer mask, + // we append a full uintptr of zeros at a time. + for i := 0; i < goarch.PtrSize; i++ { + bv.data = append(bv.data, 0) + } + } + bv.data[bv.n/8] |= bit << (bv.n % 8) + bv.n++ +} + +func addTypeBits(bv *bitVector, offset uintptr, t *abi.Type) { + if t.PtrBytes == 0 { + return + } + + switch Kind(t.Kind_ & kindMask) { + case Chan, Func, Map, Pointer, Slice, String, UnsafePointer: + // 1 pointer at start of representation + for bv.n < uint32(offset/uintptr(goarch.PtrSize)) { + bv.append(0) + } + bv.append(1) + + case Interface: + // 2 pointers + for bv.n < uint32(offset/uintptr(goarch.PtrSize)) { + bv.append(0) + } + bv.append(1) + bv.append(1) + + case Array: + // repeat inner type + tt := (*arrayType)(unsafe.Pointer(t)) + for i := 0; i < int(tt.Len); i++ { + addTypeBits(bv, offset+uintptr(i)*tt.Elem.Size_, tt.Elem) + } + + case Struct: + // apply fields + tt := (*structType)(unsafe.Pointer(t)) + for i := range tt.Fields { + f := &tt.Fields[i] + addTypeBits(bv, offset+f.Offset, f.Typ) + } + } +} |