From 8154aa3e314d076d1bfcbfb8e9eadb8d7365806e Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 09:38:49 +0800 Subject: [PATCH 01/13] internal/abi: add type_llgo.go --- internal/abi/kind.go | 4 +- internal/abi/kind_go126.go | 4 +- internal/abi/map_noswiss.go | 2 +- internal/abi/map_swiss.go | 2 +- internal/abi/type.go | 2 + internal/abi/type_llgo.go | 665 ++++++++++++++++++++++++++++++++++++ 6 files changed, 673 insertions(+), 6 deletions(-) create mode 100644 internal/abi/type_llgo.go diff --git a/internal/abi/kind.go b/internal/abi/kind.go index 2900203..e351494 100644 --- a/internal/abi/kind.go +++ b/internal/abi/kind.go @@ -1,5 +1,5 @@ -//go:build !go1.26 -// +build !go1.26 +//go:build !go1.26 && !llgo +// +build !go1.26,!llgo package abi diff --git a/internal/abi/kind_go126.go b/internal/abi/kind_go126.go index fb8a238..a399862 100644 --- a/internal/abi/kind_go126.go +++ b/internal/abi/kind_go126.go @@ -1,5 +1,5 @@ -//go:build go1.26 -// +build go1.26 +//go:build go1.26 && !llgo +// +build go1.26,!llgo package abi diff --git a/internal/abi/map_noswiss.go b/internal/abi/map_noswiss.go index 491fa80..0efe57a 100644 --- a/internal/abi/map_noswiss.go +++ b/internal/abi/map_noswiss.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. -//go:build !go1.24 && !goexperiment.swissmap +//go:build !go1.24 && !goexperiment.swissmap && !llgo package abi diff --git a/internal/abi/map_swiss.go b/internal/abi/map_swiss.go index 2199f45..d336be6 100644 --- a/internal/abi/map_swiss.go +++ b/internal/abi/map_swiss.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. -//go:build (go1.24 && goexperiment.swissmap) || go1.25 +//go:build ((go1.24 && goexperiment.swissmap) || go1.25) && !llgo package abi diff --git a/internal/abi/type.go b/internal/abi/type.go index da433e5..da03775 100644 --- a/internal/abi/type.go +++ b/internal/abi/type.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !llgo + package abi import ( diff --git a/internal/abi/type_llgo.go b/internal/abi/type_llgo.go new file mode 100644 index 0000000..b45bd29 --- /dev/null +++ b/internal/abi/type_llgo.go @@ -0,0 +1,665 @@ +//go:build llgo + +/* + * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package abi + +import ( + "unsafe" +) + +// IsExported reports whether name starts with an upper-case letter. +func IsExported(name string) bool { + if len(name) > 0 { + c := name[0] + return 'A' <= c && c <= 'Z' + } + return false +} + +// ----------------------------------------------------------------------------- + +// Type is the runtime representation of a Go type. +// +// Type is also referenced implicitly +// (in the form of expressions involving constants and arch.PtrSize) +// in cmd/compile/internal/reflectdata/reflect.go +// and cmd/link/internal/ld/decodesym.go +// (e.g. data[2*arch.PtrSize+4] references the TFlag field) +// unsafe.OffsetOf(Type{}.TFlag) cannot be used directly in those +// places because it varies with cross compilation and experiments. +type Type struct { + Size_ uintptr + PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers + Hash uint32 // hash of type; avoids computation in hash tables + TFlag TFlag // extra type information flags + Align_ uint8 // alignment of variable with this type + FieldAlign_ uint8 // alignment of struct field with this type + Kind_ uint8 // enumeration for C + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + Equal func(unsafe.Pointer, unsafe.Pointer) bool + // GCData stores the GC type data for the garbage collector. + // If the KindGCProg bit is set in kind, GCData is a GC program. + // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. + GCData *byte + Str_ string // string form + PtrToThis_ *Type // type for pointer to this type, may be nil +} + +// 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 +) + +const ( + // TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible. + 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 int(k) < len(kindNames) { + return kindNames[k] + } + return kindNames[0] +} + +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", +} + +// TFlag is used by a Type to signal what extra type information is +// available in the memory directly following the Type value. +type TFlag uint8 + +const ( + // TFlagUncommon means that there is a data with a type, UncommonType, + // just beyond the shared-per-type common data. That is, the data + // for struct types will store their UncommonType at one offset, the + // data for interface types will store their UncommonType at a different + // offset. UncommonType is always accessed via a pointer that is computed + // using trust-us-we-are-the-implementors pointer arithmetic. + // + // For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0, + // then t has UncommonType data and it can be accessed as: + // + // type structTypeUncommon struct { + // structType + // u UncommonType + // } + // u := &(*structTypeUncommon)(unsafe.Pointer(t)).u + TFlagUncommon TFlag = 1 << 0 + + // TFlagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + TFlagExtraStar TFlag = 1 << 1 + + // TFlagNamed means the type has a name. + TFlagNamed TFlag = 1 << 2 + + // TFlagRegularMemory means that equal and hash functions can treat + // this type as a single region of t.size bytes. + TFlagRegularMemory TFlag = 1 << 3 + + // TFlagVariadic means a funcType with variadic parameters + TFlagVariadic TFlag = 1 << 4 + + // TFflagClosure means the structType is a closure + TFlagClosure TFlag = 1 << 5 + + // TFlagGCMaskOnDemand means that the GC pointer bitmask will be + // computed on demand at runtime instead of being precomputed at + // compile time. If this flag is set, the GCData field effectively + // has type **byte instead of *byte. The runtime will store a + // pointer to the GC pointer bitmask in *GCData. + TFlagGCMaskOnDemand TFlag = 1 << 7 +) + +// ----------------------------------------------------------------------------- + +// ArrayType represents a fixed array type. +type ArrayType struct { + Type + Elem *Type // array element type + Slice *Type // slice type + Len uintptr +} + +type SliceType struct { + Type + Elem *Type // slice element type +} + +type MapType struct { + Type + Key *Type + Elem *Type + Bucket *Type // internal type representing a hash bucket + // function for hashing keys (ptr to key, seed) -> hash + Hasher func(unsafe.Pointer, uintptr) uintptr + KeySize uint8 // size of key slot + ValueSize uint8 // size of elem slot + BucketSize uint16 // size of bucket + Flags uint32 +} + +// Note: flag values must match those used in the TMAP case +// in ../cmd/compile/internal/reflectdata/reflect.go:writeType. +func (mt *MapType) IndirectKey() bool { // store ptr to key instead of key itself + return mt.Flags&1 != 0 +} +func (mt *MapType) IndirectElem() bool { // store ptr to elem instead of elem itself + return mt.Flags&2 != 0 +} +func (mt *MapType) ReflexiveKey() bool { // true if k==k for all keys + return mt.Flags&4 != 0 +} +func (mt *MapType) NeedKeyUpdate() bool { // true if we need to update key on an overwrite + return mt.Flags&8 != 0 +} +func (mt *MapType) HashMightPanic() bool { // true if hash function might panic + return mt.Flags&16 != 0 +} + +func (t *Type) Key() *Type { + if t.Kind() == Map { + return (*MapType)(unsafe.Pointer(t)).Key + } + return nil +} + +type PtrType struct { + Type + Elem *Type // pointer element (pointed at) type +} + +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan + InvalidDir ChanDir = 0 +) + +// ChanType represents a channel type +type ChanType struct { + Type + Elem *Type + Dir ChanDir +} + +// funcType represents a function type. +type FuncType struct { + Type + In []*Type + Out []*Type +} + +// Variadic reports whether the function type is variadic. +func (p *FuncType) Variadic() bool { + return p.TFlag&TFlagVariadic != 0 +} + +type StructField struct { + Name_ string // name is always non-empty + Typ *Type // type of field + Offset uintptr // byte offset of field + + Tag_ string + Embedded_ bool +} + +// Embedded reports whether the field is embedded. +func (f *StructField) Embedded() bool { + return f.Embedded_ +} + +// Exported reports whether the field is exported. +func (f *StructField) Exported() bool { + return IsExported(f.Name_) +} + +type StructType struct { + Type + PkgPath_ string + Fields []StructField +} + +type InterfaceType struct { + Type + PkgPath_ string // import path + Methods []Imethod // sorted by hash +} + +type Text = unsafe.Pointer // TODO(xsw): to be confirmed + +// Method on non-interface type +type Method struct { + Name_ string // name of method + Mtyp_ *FuncType // method type (without receiver) + Ifn_ Text // fn used in interface call (one-word receiver) + Tfn_ Text // fn used for normal method call +} + +// Exported reports whether the method is exported. +func (p *Method) Exported() bool { + return lastDot(p.Name_) == -1 +} + +// Name returns the tag string for method. +func (p *Method) Name() string { + _, name := splitName(p.Name_) + return name +} + +// PkgPath returns the pkgpath string for method, or empty if there is none. +func (p *Method) PkgPath() string { + pkg, _ := splitName(p.Name_) + return pkg +} + +// 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 struct { + PkgPath_ string // import path; empty for built-in types like int, string + Mcount uint16 // number of methods + Xcount uint16 // number of exported methods + Moff uint32 // offset from this uncommontype to [mcount]Method +} + +func (t *UncommonType) Methods() []Method { + if t.Mcount == 0 { + return nil + } + methodsPtr := addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0") + methods := make([]Method, t.Mcount) + for i := 0; i < int(t.Mcount); i++ { + elemPtr := addChecked(methodsPtr, uintptr(i)*unsafe.Sizeof(Method{}), "accessing method element") + elem := (*Method)(elemPtr) + methods[i] = *elem + } + return methods +} + +func (t *UncommonType) ExportedMethods() []Method { + if t.Xcount == 0 { + return nil + } + mthdsPtr := addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0") + mthds := make([]Method, t.Xcount) + for i := 0; i < int(t.Xcount); i++ { + elemPtr := addChecked(mthdsPtr, uintptr(i)*unsafe.Sizeof(Method{}), "accessing method element") + elem := (*Method)(elemPtr) + mthds[i] = *elem + } + return mthds +} + +// Imethod represents a method on an interface type +type Imethod struct { + Name_ string // name of method + Typ_ *FuncType // .(*FuncType) underneath +} + +// Exported reports whether the imethod is exported. +func (p *Imethod) Exported() bool { + return lastDot(p.Name_) == -1 +} + +// Name returns the tag string for imethod. +func (p *Imethod) Name() string { + _, name := splitName(p.Name_) + return name +} + +// PkgPath returns the pkgpath string for imethod, or empty if there is none. +func (p *Imethod) PkgPath() string { + pkg, _ := splitName(p.Name_) + return pkg +} + +func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) } + +func (t *Type) HasName() bool { + return t.TFlag&TFlagNamed != 0 +} + +func (t *Type) Pointers() bool { return t.PtrBytes != 0 } + +// IfaceIndir reports whether t is stored indirectly in an interface value. +func (t *Type) IfaceIndir() bool { + return t.Kind_&KindDirectIface == 0 +} + +// IsDirectIface reports whether t is stored directly in an interface value. +func (t *Type) IsDirectIface() bool { + return t.Kind_&KindDirectIface != 0 +} + +// IsClosure reports whether t is closure struct +func (t *Type) IsClosure() bool { + return t.TFlag&TFlagClosure != 0 +} + +// Size returns the size of data with type t. +func (t *Type) Size() uintptr { return t.Size_ } + +// Align returns the alignment of data with type t. +func (t *Type) Align() int { return int(t.Align_) } + +func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } + +// String returns string form of type t. +func (t *Type) String() string { + if t.TFlag&TFlagExtraStar != 0 { + return "*" + t.Str_ // TODO(xsw): misunderstand + } + return t.Str_ +} + +func (t *Type) Common() *Type { + return t +} + +type structTypeUncommon struct { + StructType + u UncommonType +} + +// ChanDir returns the direction of t if t is a channel type, otherwise InvalidDir (0). +func (t *Type) ChanDir() ChanDir { + if t.Kind() == Chan { + ch := (*ChanType)(unsafe.Pointer(t)) + return ch.Dir + } + return InvalidDir +} + +// Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil +func (t *Type) Uncommon() *UncommonType { + if t.TFlag&TFlagUncommon == 0 { + return nil + } + switch t.Kind() { + case Struct: + return &(*structTypeUncommon)(unsafe.Pointer(t)).u + case Pointer: + type u struct { + PtrType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Func: + type u struct { + FuncType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Slice: + type u struct { + SliceType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Array: + type u struct { + ArrayType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Chan: + type u struct { + ChanType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Map: + type u struct { + MapType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Interface: + type u struct { + InterfaceType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + default: + type u struct { + Type + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + } +} + +// Len returns the length of t if t is an array type, otherwise 0 +func (t *Type) Len() int { + if t.Kind() == Array { + return int((*ArrayType)(unsafe.Pointer(t)).Len) + } + return 0 +} + +// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil. +func (t *Type) Elem() *Type { + switch t.Kind() { + case Pointer: + tt := (*PtrType)(unsafe.Pointer(t)) + return tt.Elem + case Slice: + tt := (*SliceType)(unsafe.Pointer(t)) + return tt.Elem + case Map: + tt := (*MapType)(unsafe.Pointer(t)) + return tt.Elem + case Array: + tt := (*ArrayType)(unsafe.Pointer(t)) + return tt.Elem + case Chan: + tt := (*ChanType)(unsafe.Pointer(t)) + return tt.Elem + } + return nil +} + +// StructType returns t cast to a *StructType, or nil if its tag does not match. +func (t *Type) StructType() *StructType { + if t.Kind() != Struct { + return nil + } + return (*StructType)(unsafe.Pointer(t)) +} + +// MapType returns t cast to a *MapType, or nil if its tag does not match. +func (t *Type) MapType() *MapType { + if t.Kind() != Map { + return nil + } + return (*MapType)(unsafe.Pointer(t)) +} + +// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match. +func (t *Type) ArrayType() *ArrayType { + if t.Kind() != Array { + return nil + } + return (*ArrayType)(unsafe.Pointer(t)) +} + +// FuncType returns t cast to a *FuncType, or nil if its tag does not match. +func (t *Type) FuncType() *FuncType { + if t.Kind() != Func { + return nil + } + return (*FuncType)(unsafe.Pointer(t)) +} + +// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match. +func (t *Type) InterfaceType() *InterfaceType { + if t.Kind() != Interface { + return nil + } + return (*InterfaceType)(unsafe.Pointer(t)) +} + +func (t *Type) ExportedMethods() []Method { + ut := t.Uncommon() + if ut == nil { + return nil + } + return ut.ExportedMethods() +} + +func (t *Type) NumMethod() int { + if t.Kind() == Interface { + tt := (*InterfaceType)(unsafe.Pointer(t)) + return len(tt.Methods) + } + return len(t.ExportedMethods()) +} + +func (t *Type) GcSlice(begin, end uintptr) []byte { + return unsafe.Slice(t.GCData, int(end))[begin:] +} + +// ----------------------------------------------------------------------------- + +// addChecked 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 addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + _ = whySafe + return unsafe.Pointer(uintptr(p) + x) +} + +func splitName(s string) (pkg string, name string) { + i := lastDot(s) + if i == -1 { + return "", s + } + return s[:i], s[i+1:] +} + +func lastDot(s string) int { + i := len(s) - 1 + for i >= 0 && s[i] != '.' { + i-- + } + return i +} + +// MaxPtrmaskBytes is the maximum length of a GC ptrmask bitmap, +// which holds 1-bit entries describing where pointers are in a given type. +// Above this length, the GC information is recorded as a GC program, +// which can express repetition compactly. In either form, the +// information is used by the runtime to initialize the heap bitmap, +// and for large types (like 128 or more words), they are roughly the +// same speed. GC programs are never much larger and often more +// compact. (If large arrays are involved, they can be arbitrarily +// more compact.) +// +// The cutoff must be large enough that any allocation large enough to +// use a GC program is large enough that it does not share heap bitmap +// bytes with any other objects, allowing the GC program execution to +// assume an aligned start and not use atomic operations. In the current +// runtime, this means all malloc size classes larger than the cutoff must +// be multiples of four words. On 32-bit systems that's 16 bytes, and +// all size classes >= 16 bytes are 16-byte aligned, so no real constraint. +// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed +// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated +// is 32 pointers, the bits for which fit in 4 bytes. So MaxPtrmaskBytes +// must be >= 4. +// +// We used to use 16 because the GC programs do have some constant overhead +// to get started, and processing 128 pointers seems to be enough to +// amortize that overhead well. +// +// To make sure that the runtime's chansend can call typeBitsBulkBarrier, +// we raised the limit to 2048, so that even 32-bit systems are guaranteed to +// use bitmaps for objects up to 64 kB in size. +const MaxPtrmaskBytes = 2048 + +// ----------------------------------------------------------------------------- From ea7a37f6319b7a34898a21737c6918eaf6249590 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 09:58:51 +0800 Subject: [PATCH 02/13] rm rtypeUncommonMethods/rtypeExportedMethods --- rtype.go | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/rtype.go b/rtype.go index d46618b..7287d45 100644 --- a/rtype.go +++ b/rtype.go @@ -26,14 +26,6 @@ func totype(typ reflect.Type) *rtype { return (*rtype)(e.word) } -//go:nocheckptr -func rtypeUncommonMethods(t *uncommonType) []method { - if t == nil || t.Mcount == 0 { - return nil - } - return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount] -} - //go:nocheckptr func rtypeUncommonExportedMethods(t *uncommonType) []method { if t == nil || t.Xcount == 0 { @@ -54,20 +46,12 @@ func rtypeUncommon(t *rtype) *uncommonType { return toUncommonType(t) } -func rtypeExportedMethods(t *rtype) []method { - ut := rtypeUncommon(t) - if ut == nil { - return nil - } - return rtypeUncommonExportedMethods(ut) -} - func rtypeMethods(t *rtype) []method { ut := rtypeUncommon(t) if ut == nil { return nil } - return rtypeUncommonMethods(ut) + return ut.Methods() } func funcTypeIn(t *funcType) []*rtype { @@ -416,7 +400,7 @@ func rtypeMethodByNameX(t *rtype, name string) (m reflect.Method, ok bool) { return toType(t).MethodByName(name) } if ut := rtypeUncommon(t); ut != nil { - for i, p := range rtypeUncommonMethods(ut) { + for i, p := range ut.Methods() { if rtype_nameOff(t, p.Name).Name() == name { return rtypeMethodX(t, i), true } From 6d100effa2118268a08cd06ee3047989890d44e7 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 10:08:50 +0800 Subject: [PATCH 03/13] remove toUncommonType --- linkname_go121.go | 3 --- linkname_go123.go | 3 --- methodof.go | 2 +- reflectx.go | 2 +- rtype.go | 8 ++------ x/reflect/reflect.go | 4 ---- 6 files changed, 4 insertions(+), 18 deletions(-) diff --git a/linkname_go121.go b/linkname_go121.go index 610981f..fad4519 100644 --- a/linkname_go121.go +++ b/linkname_go121.go @@ -12,8 +12,5 @@ import ( //go:linkname interequal runtime.interequal func interequal(p, q unsafe.Pointer) bool -//go:linkname toUncommonType reflect.(*rtype).uncommon -func toUncommonType(t *rtype) *uncommonType - //go:linkname haveIdenticalType reflect.haveIdenticalType func haveIdenticalType(T, V *rtype, cmpTags bool) bool diff --git a/linkname_go123.go b/linkname_go123.go index 4ba2df6..80e6aee 100644 --- a/linkname_go123.go +++ b/linkname_go123.go @@ -12,8 +12,5 @@ import ( //go:linkname interequal github.com/goplus/reflectx/x/reflect.interequal func interequal(p, q unsafe.Pointer) bool -//go:linkname toUncommonType github.com/goplus/reflectx/x/reflect.toUncommonType -func toUncommonType(t *rtype) *uncommonType - //go:linkname haveIdenticalType github.com/goplus/reflectx/x/reflect.haveIdenticalType func haveIdenticalType(T, V *rtype, cmpTags bool) bool diff --git a/methodof.go b/methodof.go index a1fdba0..7fc56e8 100644 --- a/methodof.go +++ b/methodof.go @@ -123,7 +123,7 @@ func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) { func resizeMethod(typ reflect.Type, mcount int, xcount int) error { rt := totype(typ) - ut := toUncommonType(rt) + ut := rt.Uncommon() if ut == nil { return fmt.Errorf("not found uncommonType of %v", typ) } diff --git a/reflectx.go b/reflectx.go index 29c03d3..9a7d160 100644 --- a/reflectx.go +++ b/reflectx.go @@ -91,7 +91,7 @@ func setTypeName(t *rtype, pkgpath string, name string) { t.TFlag |= tflagNamed | tflagExtraStar t.Str = resolveReflectName(newName("*"+name, "", exported)) if t.TFlag&tflagUncommon == tflagUncommon { - toUncommonType(t).PkgPath = resolveReflectName(newName(pkgpath, "", false)) + t.Uncommon().PkgPath = resolveReflectName(newName(pkgpath, "", false)) } switch reflect.Kind(t.Kind()) { case reflect.Struct: diff --git a/rtype.go b/rtype.go index 7287d45..8719a00 100644 --- a/rtype.go +++ b/rtype.go @@ -42,12 +42,8 @@ func toValue(v Value) reflect.Value { return *(*reflect.Value)(unsafe.Pointer(&v)) } -func rtypeUncommon(t *rtype) *uncommonType { - return toUncommonType(t) -} - func rtypeMethods(t *rtype) []method { - ut := rtypeUncommon(t) + ut := t.Uncommon() if ut == nil { return nil } @@ -399,7 +395,7 @@ func rtypeMethodByNameX(t *rtype, name string) (m reflect.Method, ok bool) { if reflect.Kind(t.Kind()) == reflect.Interface { return toType(t).MethodByName(name) } - if ut := rtypeUncommon(t); ut != nil { + if ut := t.Uncommon(); ut != nil { for i, p := range ut.Methods() { if rtype_nameOff(t, p.Name).Name() == name { return rtypeMethodX(t, i), true diff --git a/x/reflect/reflect.go b/x/reflect/reflect.go index 30d464f..fb1218a 100644 --- a/x/reflect/reflect.go +++ b/x/reflect/reflect.go @@ -134,10 +134,6 @@ func funcName(f func([]Value) []Value) string { return "closure" } -func toUncommonType(t *abi.Type) *abi.UncommonType { - return t.Uncommon() -} - type assignToFunc = func(v Value, context string, dst *abi.Type, target unsafe.Pointer) Value var assignTo assignToFunc From 444abcc54519a2e7dbfbea6b585e471ec9382803 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 10:14:47 +0800 Subject: [PATCH 04/13] rm nameData/nameHasTag/readVarint --- name.go | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/name.go b/name.go index b12ffa2..7f69834 100644 --- a/name.go +++ b/name.go @@ -5,25 +5,6 @@ import "unsafe" // name is an alias to abi.Name (defined in type.go) // Helper methods below mirror the original name methods. -func nameData(n name, off int, whySafe string) *byte { - return (*byte)(add(unsafe.Pointer(n.Bytes), uintptr(off), whySafe)) -} - -func nameHasTag(n name) bool { - return (*n.Bytes)&(1<<1) != 0 -} - -func readVarint(n name, off int) (int, int) { - v := 0 - for i := 0; ; i++ { - x := *nameData(n, off+i, "read varint") - v += int(x&0x7f) << (7 * i) - if x&0x80 == 0 { - return i + 1, v - } - } -} - // writeVarint writes n to buf in varint form. Returns the // number of bytes written. n must be nonnegative. // Writes at most 10 bytes. @@ -40,17 +21,17 @@ func writeVarint(buf []byte, n int) int { } func setPkgPath(n name, pkgpath string) { - if n.Bytes == nil || *nameData(n, 0, "name flag pkgPath")&(1<<2) == 0 { + if n.Bytes == nil || *n.DataChecked(0, "name flag pkgPath")&(1<<2) == 0 { return } - i, l := readVarint(n, 1) + i, l := n.ReadVarint(1) off := 1 + i + l - if nameHasTag(n) { - i2, l2 := readVarint(n, off) + if n.HasTag() { + i2, l2 := n.ReadVarint(off) off += i2 + l2 } v := resolveReflectName(newName(pkgpath, "", false)) - copy((*[4]byte)(unsafe.Pointer(nameData(n, off, "name offset pkgPath")))[:], (*[4]byte)(unsafe.Pointer(&v))[:]) + copy((*[4]byte)(unsafe.Pointer(n.DataChecked(off, "name offset pkgPath")))[:], (*[4]byte)(unsafe.Pointer(&v))[:]) } func newNameEx(n, tag string, exported bool, pkgpath bool) name { From e4cfa4a4b2b43927a06b3e827a69b86524d9a524 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 10:29:00 +0800 Subject: [PATCH 05/13] nameOff/typeOff/textOff --- linkname.go | 6 ++++++ type.go | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/linkname.go b/linkname.go index ef6600e..1120758 100644 --- a/linkname.go +++ b/linkname.go @@ -3,8 +3,14 @@ package reflectx import ( "reflect" "unsafe" + + "github.com/goplus/reflectx/internal/abi" ) +type nameOff = abi.NameOff +type typeOff = abi.TypeOff +type textOff = abi.TextOff + // memmove copies size bytes to dst from src. No write barriers are used. // //go:noescape diff --git a/type.go b/type.go index 389cb10..bb586b4 100644 --- a/type.go +++ b/type.go @@ -25,9 +25,6 @@ import ( // Type aliases to internal/abi types type rtype = abi.Type -type nameOff = abi.NameOff -type typeOff = abi.TypeOff -type textOff = abi.TextOff type tflag = abi.TFlag type method = abi.Method type name = abi.Name From 67bed0544b9eaa540d421e671b9f16508c9e777d Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 10:36:18 +0800 Subject: [PATCH 06/13] rm copyType --- reflectx.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/reflectx.go b/reflectx.go index 9a7d160..663e6d1 100644 --- a/reflectx.go +++ b/reflectx.go @@ -103,17 +103,6 @@ func setTypeName(t *rtype, pkgpath string, name string) { } } -func copyType(dst *rtype, src *rtype) { - dst.Size_ = src.Size_ - dst.Kind_ = src.Kind_ - dst.Equal = src.Equal - dst.Align_ = src.Align_ - dst.FieldAlign_ = src.FieldAlign_ - dst.TFlag = src.TFlag - dst.GCData = src.GCData - dst.PtrBytes = src.PtrBytes -} - func isExported(name string) bool { ch, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(ch) From 3cf96b6acbde91720742c763f9a1cc565c792917 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 10:50:41 +0800 Subject: [PATCH 07/13] rm ReplaceType --- reflectx.go | 177 ---------------------------------------------------- 1 file changed, 177 deletions(-) diff --git a/reflectx.go b/reflectx.go index 663e6d1..287a629 100644 --- a/reflectx.go +++ b/reflectx.go @@ -249,180 +249,3 @@ func SetElem(typ reflect.Type, elem reflect.Type) { panic("reflect: Elem of invalid type " + typ.String()) } } - -func typeId(typ reflect.Type) string { - var id string - if path := typ.PkgPath(); path != "" { - id = path + "." - } - return id + typ.Name() -} - -type replaceTypeContext struct { - checking map[reflect.Type]bool -} - -func ReplaceType(pkg string, typ reflect.Type, m map[string]reflect.Type) (rtyp reflect.Type, changed bool) { - ctx := &replaceTypeContext{make(map[reflect.Type]bool)} - return ctx.replace(pkg, typ, m) -} - -func (ctx *replaceTypeContext) replace(pkg string, typ reflect.Type, m map[string]reflect.Type) (rtyp reflect.Type, changed bool) { - if ctx.checking[typ] { - return - } - ctx.checking[typ] = true - rt := totype(typ) - switch typ.Kind() { - case reflect.Struct: - if typ.PkgPath() != pkg { - return - } - st := (*structType)(toKindType(rt)) - for i := 0; i < len(st.Fields); i++ { - et := toType(st.Fields[i].Typ) - if t, ok := m[typeId(et)]; ok { - st.Fields[i].Typ = totype(t) - changed = true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - changed = true - st.Fields[i].Typ = totype(rtyp) - } - } - } - if changed { - return toType(rt), true - } - case reflect.Ptr: - st := (*ptrType)(toKindType(rt)) - et := toType(st.Elem) - if t, ok := m[typeId(et)]; ok { - st.Elem = totype(t) - return reflect.PtrTo(t), true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - return reflect.PtrTo(rtyp), true - } - } - case reflect.Slice: - st := (*sliceType)(toKindType(rt)) - et := toType(st.Elem) - if t, ok := m[typeId(et)]; ok { - st.Elem = totype(t) - return reflect.SliceOf(t), true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - return reflect.SliceOf(rtyp), true - } - } - case reflect.Array: - st := (*arrayType)(toKindType(rt)) - et := toType(st.Elem) - if t, ok := m[typeId(et)]; ok { - st.Elem = totype(t) - return reflect.ArrayOf(int(st.Len), t), true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - return reflect.ArrayOf(int(st.Len), rtyp), true - } - } - case reflect.Map: - st := (*mapType)(toKindType(rt)) - kt := toType(st.Key) - et := toType(st.Elem) - if t, ok := m[typeId(kt)]; ok { - kt = t - changed = true - } else { - if rtyp, ok := ctx.replace(pkg, kt, m); ok { - kt = rtyp - changed = true - } - } - if t, ok := m[typeId(et)]; ok { - et = t - changed = true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - et = rtyp - changed = true - } - } - if changed { - return reflect.MapOf(kt, et), true - } - case reflect.Chan: - st := (*chanType)(toKindType(rt)) - et := toType(st.Elem) - if t, ok := m[typeId(et)]; ok { - st.Elem = totype(t) - return reflect.ChanOf(typ.ChanDir(), t), true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - return reflect.ChanOf(typ.ChanDir(), rtyp), true - } - } - case reflect.Func: - st := (*funcType)(toKindType(rt)) - in := funcTypeIn(st) - out := funcTypeOut(st) - for i := 0; i < len(in); i++ { - et := toType(in[i]) - if t, ok := m[typeId(et)]; ok { - in[i] = totype(t) - changed = true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - in[i] = totype(rtyp) - changed = true - } - } - } - for i := 0; i < len(out); i++ { - et := toType(out[i]) - if t, ok := m[typeId(et)]; ok { - out[i] = totype(t) - changed = true - } else { - if rtyp, ok := ctx.replace(pkg, et, m); ok { - out[i] = totype(rtyp) - changed = true - } - } - } - if changed { - ins := make([]reflect.Type, len(in)) - for i := 0; i < len(in); i++ { - ins[i] = toType(in[i]) - } - outs := make([]reflect.Type, len(out)) - for i := 0; i < len(out); i++ { - outs[i] = toType(out[i]) - } - return reflect.FuncOf(ins, outs, typ.IsVariadic()), true - } - case reflect.Interface: - if typ.PkgPath() != pkg { - return - } - if typ == tyErrorInterface { - return - } - st := (*interfaceType)(toKindType(rt)) - for i := 0; i < len(st.Methods); i++ { - tt := typ.Method(i).Type - if t, ok := m[typeId(tt)]; ok { - st.Methods[i].Typ = resolveReflectType(totype(t)) - changed = true - } else if rtyp, ok := ctx.replace(pkg, tt, m); ok { - st.Methods[i].Typ = resolveReflectType(totype(rtyp)) - changed = true - } - } - if changed { - return toType(rt), true - } - } - return nil, false -} From 584ec9c8b97504e019af51162be6393ebcc60d6a Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 10:59:44 +0800 Subject: [PATCH 08/13] rm rtypeUncommonExportedMethods --- rtype.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/rtype.go b/rtype.go index 8719a00..bb5eef5 100644 --- a/rtype.go +++ b/rtype.go @@ -26,14 +26,6 @@ func totype(typ reflect.Type) *rtype { return (*rtype)(e.word) } -//go:nocheckptr -func rtypeUncommonExportedMethods(t *uncommonType) []method { - if t == nil || t.Xcount == 0 { - return nil - } - return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount] -} - func tovalue(v *reflect.Value) *Value { return (*Value)(unsafe.Pointer(v)) } From 1935385d339758bb515f58c3c5adf00794ea1ee9 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 12:06:44 +0800 Subject: [PATCH 09/13] build for llgo --- linkname.go | 6 +-- linkname_go121.go | 4 +- linkname_go123.go | 4 +- map.go | 3 +- map_go124.go | 3 +- method.go | 17 +----- methodof.go | 2 + name.go | 10 +++- reflectx.go | 91 -------------------------------- rtype.go | 130 +++++++++++++++++++++++++++++++--------------- rtype_llgo.go | 107 ++++++++++++++++++++++++++++++++++++++ type.go | 46 +++++++++++++++- xcall.go | 19 +++++++ 13 files changed, 281 insertions(+), 161 deletions(-) create mode 100644 rtype_llgo.go diff --git a/linkname.go b/linkname.go index 1120758..0c21c59 100644 --- a/linkname.go +++ b/linkname.go @@ -1,7 +1,8 @@ +//go:build !llgo + package reflectx import ( - "reflect" "unsafe" "github.com/goplus/reflectx/internal/abi" @@ -66,9 +67,6 @@ func resolveReflectName(n name) nameOff { //go:linkname resolveNameOff reflect.resolveNameOff func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer -//go:linkname toType reflect.toType -func toType(t *rtype) reflect.Type - func rtype_nameOff(t *rtype, off nameOff) name { return name{Bytes: (*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} } diff --git a/linkname_go121.go b/linkname_go121.go index fad4519..0c22b44 100644 --- a/linkname_go121.go +++ b/linkname_go121.go @@ -1,5 +1,5 @@ -//go:build !go1.23 || linknamefix -// +build !go1.23 linknamefix +//go:build !go1.23 || linknamefix || llgo +// +build !go1.23 linknamefix llgo package reflectx diff --git a/linkname_go123.go b/linkname_go123.go index 80e6aee..6425896 100644 --- a/linkname_go123.go +++ b/linkname_go123.go @@ -1,5 +1,5 @@ -//go:build go1.23 && !linknamefix -// +build go1.23,!linknamefix +//go:build go1.23 && !linknamefix && !llgo +// +build go1.23,!linknamefix,!llgo package reflectx diff --git a/map.go b/map.go index 6be2136..1dd5b35 100644 --- a/map.go +++ b/map.go @@ -1,5 +1,4 @@ -//go:build !go1.24 -// +build !go1.24 +//go:build !go1.24 || llgo package reflectx diff --git a/map_go124.go b/map_go124.go index d8f24a0..4f4b9b2 100644 --- a/map_go124.go +++ b/map_go124.go @@ -1,5 +1,4 @@ -//go:build go1.24 -// +build go1.24 +//go:build go1.24 && !llgo package reflectx diff --git a/method.go b/method.go index 3a90fb8..efae96c 100644 --- a/method.go +++ b/method.go @@ -1,3 +1,5 @@ +//go:build !llgo + package reflectx import ( @@ -35,21 +37,6 @@ func MakeMethod(name string, pkgpath string, pointer bool, typ reflect.Type, fn } } -// Method struct for MethodOf -// - name: method name -// - pointer: flag receiver struct or pointer -// - typ: method func type without receiver -// - func: func with receiver as first argument -// - funcId: func internal id -type Method struct { - Name string - PkgPath string - Pointer bool - Type reflect.Type - Func func([]reflect.Value) []reflect.Value - FuncId int -} - func extraFieldMethod(ifield int, typ reflect.Type, skip map[string]bool) (methods []Method) { isPtr := typ.Kind() == reflect.Ptr for i := 0; i < typ.NumMethod(); i++ { diff --git a/methodof.go b/methodof.go index 7fc56e8..7a1e2da 100644 --- a/methodof.go +++ b/methodof.go @@ -1,3 +1,5 @@ +//go:build !llgo + package reflectx import ( diff --git a/name.go b/name.go index 7f69834..aa1b8c8 100644 --- a/name.go +++ b/name.go @@ -1,6 +1,14 @@ +//go:build !llgo + package reflectx -import "unsafe" +import ( + "unsafe" + + "github.com/goplus/reflectx/internal/abi" +) + +type name = abi.Name // name is an alias to abi.Name (defined in type.go) // Helper methods below mirror the original name methods. diff --git a/reflectx.go b/reflectx.go index 287a629..87322d3 100644 --- a/reflectx.go +++ b/reflectx.go @@ -17,9 +17,7 @@ package reflectx import ( - "path" "reflect" - "strconv" "unicode" "unicode/utf8" "unsafe" @@ -79,30 +77,6 @@ func SetTypeName(typ reflect.Type, pkgpath string, name string) { setTypeName(totype(typ), pkgpath, name) } -func setTypeName(t *rtype, pkgpath string, name string) { - if pkgpath == "" && name == "" { - return - } - exported := isExported(name) - if pkgpath != "" { - _, f := path.Split(pkgpath) - name = f + "." + name - } - t.TFlag |= tflagNamed | tflagExtraStar - t.Str = resolveReflectName(newName("*"+name, "", exported)) - if t.TFlag&tflagUncommon == tflagUncommon { - t.Uncommon().PkgPath = resolveReflectName(newName(pkgpath, "", false)) - } - switch reflect.Kind(t.Kind()) { - case reflect.Struct: - st := (*structType)(toKindType(t)) - st.PkgPath = newName(pkgpath, "", false) - case reflect.Interface: - st := (*interfaceType)(toKindType(t)) - st.PkgPath = newName(pkgpath, "", false) - } -} - func isExported(name string) bool { ch, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(ch) @@ -132,71 +106,6 @@ func StructOf(fields []reflect.StructField) reflect.Type { return Default.StructOf(fields) } -func (ctx *Context) StructOf(fields []reflect.StructField) reflect.Type { - var anonymous []int - underscore := make(map[int]name) - var underscoreCount int - fs := make([]reflect.StructField, len(fields)) - for i := 0; i < len(fields); i++ { - f := fields[i] - if f.Anonymous { - anonymous = append(anonymous, i) - f.Anonymous = false - if f.Name == "" { - f.Name = typeName(f.Type) - } - } else if f.Name == "_" { - if underscoreCount > 0 { - underscore[i] = newName("_", string(f.Tag), false) - f.Name = "_gop_underscore_" + strconv.Itoa(i) - } - underscoreCount++ - } - fs[i] = f - } - typ := reflect.StructOf(fs) - rt := totype(typ) - st := toStructType(rt) - for _, i := range anonymous { - setEmbedded(&st.Fields[i]) - } - for i, n := range underscore { - st.Fields[i].Name = n - } - str := typ.String() - if ts, ok := ctx.structLookupCache[str]; ok { - for _, t := range ts { - if haveIdenticalType(totype(t), totype(typ), true) { - return t - } - } - ts = append(ts, typ) - } else { - ctx.structLookupCache[str] = []reflect.Type{typ} - } - // fix equal for blank fields and uncomparable type - if rt.Equal != nil && underscoreCount > 0 { - rt.Equal = func(p, q unsafe.Pointer) bool { - for i, ft := range st.Fields { - if fields[i].Name == "_" { - continue - } - 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 - } - } - - if rt.TFlag == 0 && isRegularMemory(typ) { - rt.TFlag |= tflagRegularMemory - } - return typ -} - func SetValue(v reflect.Value, x reflect.Value) { switch v.Kind() { case reflect.Bool: diff --git a/rtype.go b/rtype.go index bb5eef5..56ae899 100644 --- a/rtype.go +++ b/rtype.go @@ -1,39 +1,16 @@ +//go:build !llgo + package reflectx import ( "fmt" "io" + "path" "reflect" + "strconv" "unsafe" ) -func toStructType(t *rtype) *structType { - return (*structType)(unsafe.Pointer(t)) -} - -func toKindType(t *rtype) unsafe.Pointer { - return unsafe.Pointer(t) -} - -// emptyInterface is the header for an interface{} value. -type emptyInterface struct { - typ *rtype - word unsafe.Pointer -} - -func totype(typ reflect.Type) *rtype { - e := (*emptyInterface)(unsafe.Pointer(&typ)) - return (*rtype)(e.word) -} - -func tovalue(v *reflect.Value) *Value { - return (*Value)(unsafe.Pointer(v)) -} - -func toValue(v Value) reflect.Value { - return *(*reflect.Value)(unsafe.Pointer(&v)) -} - func rtypeMethods(t *rtype) []method { ut := t.Uncommon() if ut == nil { @@ -397,22 +374,93 @@ func rtypeMethodByNameX(t *rtype, name string) (m reflect.Method, ok bool) { return reflect.Method{}, false } -// Field returns the i'th field of the struct v. -// It panics if v's Kind is not Struct or i is out of range. -func FieldX(v reflect.Value, i int) reflect.Value { - mustBe("reflect.Value.Field", v, reflect.Struct) - rv := tovalue(&v) - tt := (*structType)(unsafe.Pointer(rv.typ)) - if uint(i) >= uint(len(tt.Fields)) { - panic("reflect: Field index out of range") +func setTypeName(t *rtype, pkgpath string, name string) { + if pkgpath == "" && name == "" { + return + } + exported := isExported(name) + if pkgpath != "" { + _, f := path.Split(pkgpath) + name = f + "." + name + } + t.TFlag |= tflagNamed | tflagExtraStar + t.Str = resolveReflectName(newName("*"+name, "", exported)) + if t.TFlag&tflagUncommon == tflagUncommon { + t.Uncommon().PkgPath = resolveReflectName(newName(pkgpath, "", false)) + } + switch reflect.Kind(t.Kind()) { + case reflect.Struct: + st := (*structType)(toKindType(t)) + st.PkgPath = newName(pkgpath, "", false) + case reflect.Interface: + st := (*interfaceType)(toKindType(t)) + st.PkgPath = newName(pkgpath, "", false) + } +} + +func (ctx *Context) StructOf(fields []reflect.StructField) reflect.Type { + var anonymous []int + underscore := make(map[int]name) + var underscoreCount int + fs := make([]reflect.StructField, len(fields)) + for i := 0; i < len(fields); i++ { + f := fields[i] + if f.Anonymous { + anonymous = append(anonymous, i) + f.Anonymous = false + if f.Name == "" { + f.Name = typeName(f.Type) + } + } else if f.Name == "_" { + if underscoreCount > 0 { + underscore[i] = newName("_", string(f.Tag), false) + f.Name = "_gop_underscore_" + strconv.Itoa(i) + } + underscoreCount++ + } + fs[i] = f + } + typ := reflect.StructOf(fs) + rt := totype(typ) + st := toStructType(rt) + for _, i := range anonymous { + setEmbedded(&st.Fields[i]) + } + for i, n := range underscore { + st.Fields[i].Name = n + } + str := typ.String() + if ts, ok := ctx.structLookupCache[str]; ok { + for _, t := range ts { + if haveIdenticalType(totype(t), totype(typ), true) { + return t + } + } + ts = append(ts, typ) + } else { + ctx.structLookupCache[str] = []reflect.Type{typ} + } + // fix equal for blank fields and uncomparable type + if rt.Equal != nil && underscoreCount > 0 { + rt.Equal = func(p, q unsafe.Pointer) bool { + for i, ft := range st.Fields { + if fields[i].Name == "_" { + continue + } + 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 + } } - field := &tt.Fields[i] - typ := field.Typ - // Inherit permission bits from v, but clear flagEmbedRO. - fl := rv.flag&(flagStickyRO|flagIndir|flagAddr) | flag(reflect.Kind(typ.Kind())) - ptr := add(rv.ptr, field.Offset, "same as non-reflect &v.field") - return toValue(Value{typ, ptr, fl}) + if rt.TFlag == 0 && isRegularMemory(typ) { + rt.TFlag |= tflagRegularMemory + } + return typ } func setEmbedded(f *structField) { diff --git a/rtype_llgo.go b/rtype_llgo.go new file mode 100644 index 0000000..00185dd --- /dev/null +++ b/rtype_llgo.go @@ -0,0 +1,107 @@ +//go:build llgo + +package reflectx + +import ( + "path" + "reflect" + "strconv" + "unsafe" +) + +func NamedTypeOf(pkgpath string, name string, from reflect.Type) reflect.Type { + panic("TODO: NamedTypeOf") +} + +func setTypeName(t *rtype, pkgpath string, name string) { + if pkgpath == "" && name == "" { + return + } + //exported := isExported(name) + if pkgpath != "" { + _, f := path.Split(pkgpath) + name = f + "." + name + } + t.TFlag |= tflagNamed | tflagExtraStar + t.Str_ = "*" + name + if t.TFlag&tflagUncommon == tflagUncommon { + t.Uncommon().PkgPath_ = pkgpath + } + switch reflect.Kind(t.Kind()) { + case reflect.Struct: + st := (*structType)(toKindType(t)) + st.PkgPath_ = pkgpath + case reflect.Interface: + st := (*interfaceType)(toKindType(t)) + st.PkgPath_ = pkgpath + } +} + +func (ctx *Context) StructOf(fields []reflect.StructField) reflect.Type { + var anonymous []int + underscore := make(map[int]string) + var underscoreCount int + fs := make([]reflect.StructField, len(fields)) + for i := 0; i < len(fields); i++ { + f := fields[i] + if f.Anonymous { + anonymous = append(anonymous, i) + f.Anonymous = false + if f.Name == "" { + f.Name = typeName(f.Type) + } + } else if f.Name == "_" { + if underscoreCount > 0 { + underscore[i] = f.Name + f.Name = "_gop_underscore_" + strconv.Itoa(i) + } + underscoreCount++ + } + fs[i] = f + } + typ := reflect.StructOf(fs) + rt := totype(typ) + st := toStructType(rt) + for _, i := range anonymous { + setEmbedded(&st.Fields[i]) + } + for i, n := range underscore { + st.Fields[i].Name_ = n + } + str := typ.String() + if ts, ok := ctx.structLookupCache[str]; ok { + for _, t := range ts { + if haveIdenticalType(totype(t), totype(typ), true) { + return t + } + } + ts = append(ts, typ) + } else { + ctx.structLookupCache[str] = []reflect.Type{typ} + } + // fix equal for blank fields and uncomparable type + if rt.Equal != nil && underscoreCount > 0 { + rt.Equal = func(p, q unsafe.Pointer) bool { + for i, ft := range st.Fields { + if fields[i].Name == "_" { + continue + } + 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 + } + } + + if rt.TFlag == 0 && isRegularMemory(typ) { + rt.TFlag |= tflagRegularMemory + } + return typ +} + +func setEmbedded(f *structField) { + f.Embedded_ = true +} diff --git a/type.go b/type.go index bb586b4..7e11b95 100644 --- a/type.go +++ b/type.go @@ -27,7 +27,6 @@ import ( type rtype = abi.Type type tflag = abi.TFlag type method = abi.Method -type name = abi.Name type imethod = abi.Imethod type uncommonType = abi.UncommonType type structField = abi.StructField @@ -109,3 +108,48 @@ func ispaddedfield(t reflect.Type, i int) bool { fd := t.Field(i) return fd.Offset+fd.Type.Size() != end } + +// Method struct for MethodOf +// - name: method name +// - pointer: flag receiver struct or pointer +// - typ: method func type without receiver +// - func: func with receiver as first argument +// - funcId: func internal id +type Method struct { + Name string + PkgPath string + Pointer bool + Type reflect.Type + Func func([]reflect.Value) []reflect.Value + FuncId int +} + +func toStructType(t *rtype) *structType { + return (*structType)(unsafe.Pointer(t)) +} + +func toKindType(t *rtype) unsafe.Pointer { + return unsafe.Pointer(t) +} + +// emptyInterface is the header for an interface{} value. +type emptyInterface struct { + typ *rtype + word unsafe.Pointer +} + +func totype(typ reflect.Type) *rtype { + e := (*emptyInterface)(unsafe.Pointer(&typ)) + return (*rtype)(e.word) +} + +func tovalue(v *reflect.Value) *Value { + return (*Value)(unsafe.Pointer(v)) +} + +func toValue(v Value) reflect.Value { + return *(*reflect.Value)(unsafe.Pointer(&v)) +} + +//go:linkname toType reflect.toType +func toType(t *rtype) reflect.Type diff --git a/xcall.go b/xcall.go index ad6e50f..c5aecfc 100644 --- a/xcall.go +++ b/xcall.go @@ -2,6 +2,7 @@ package reflectx import ( "reflect" + "unsafe" ) func FieldByIndexX(v reflect.Value, index []int) reflect.Value { @@ -47,3 +48,21 @@ func FieldByNameFuncX(v reflect.Value, match func(string) bool) reflect.Value { } return reflect.Value{} } + +// Field returns the i'th field of the struct v. +// It panics if v's Kind is not Struct or i is out of range. +func FieldX(v reflect.Value, i int) reflect.Value { + mustBe("reflect.Value.Field", v, reflect.Struct) + rv := tovalue(&v) + tt := (*structType)(unsafe.Pointer(rv.typ)) + if uint(i) >= uint(len(tt.Fields)) { + panic("reflect: Field index out of range") + } + field := &tt.Fields[i] + typ := field.Typ + + // Inherit permission bits from v, but clear flagEmbedRO. + fl := rv.flag&(flagStickyRO|flagIndir|flagAddr) | flag(reflect.Kind(typ.Kind())) + ptr := add(rv.ptr, field.Offset, "same as non-reflect &v.field") + return toValue(Value{typ, ptr, fl}) +} From 6142bcb0e197e2d42167ac44f8b3ff0eac16623c Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 15:13:38 +0800 Subject: [PATCH 10/13] llgo: rtypeMethodX --- method_test.go | 3 ++- methodof.go | 9 --------- reflectx.go | 21 ++++++++++++++++++++ reflectx_test.go | 4 ++++ rtype.go | 12 ------------ rtype_llgo.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 22 deletions(-) diff --git a/method_test.go b/method_test.go index fe884e4..9c6524b 100644 --- a/method_test.go +++ b/method_test.go @@ -1,3 +1,5 @@ +//go:build !llgo + package reflectx_test import ( @@ -15,7 +17,6 @@ import ( var ( tyByte = reflect.TypeOf(byte('a')) tyBool = reflect.TypeOf(true) - tyInt = reflect.TypeOf(0) tyString = reflect.TypeOf("") tyError = reflect.TypeOf((*error)(nil)).Elem() tyEmptyStruct = reflect.TypeOf((*struct{})(nil)).Elem() diff --git a/methodof.go b/methodof.go index 7a1e2da..e141ab1 100644 --- a/methodof.go +++ b/methodof.go @@ -114,15 +114,6 @@ type MethodInfo struct { OnePtr bool } -func MethodByIndex(typ reflect.Type, index int) reflect.Method { - return rtypeMethodX(totype(typ), index) -} - -func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) { - m, ok = rtypeMethodByNameX(totype(typ), name) - return -} - func resizeMethod(typ reflect.Type, mcount int, xcount int) error { rt := totype(typ) ut := rt.Uncommon() diff --git a/reflectx.go b/reflectx.go index 87322d3..7c36a8a 100644 --- a/reflectx.go +++ b/reflectx.go @@ -158,3 +158,24 @@ func SetElem(typ reflect.Type, elem reflect.Type) { panic("reflect: Elem of invalid type " + typ.String()) } } + +func rtypeMethods(t *rtype) []method { + ut := t.Uncommon() + if ut == nil { + return nil + } + return ut.Methods() +} + +func NumMethodX(typ reflect.Type) int { + return len(rtypeMethods(totype(typ))) +} + +func MethodByIndex(typ reflect.Type, index int) reflect.Method { + return rtypeMethodX(totype(typ), index) +} + +func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) { + m, ok = rtypeMethodByNameX(totype(typ), name) + return +} diff --git a/reflectx_test.go b/reflectx_test.go index 79fd04b..4239cf2 100644 --- a/reflectx_test.go +++ b/reflectx_test.go @@ -11,6 +11,10 @@ import ( "github.com/goplus/reflectx" ) +var ( + tyInt = reflect.TypeOf(0) +) + type nPoint struct { x int y int diff --git a/rtype.go b/rtype.go index 56ae899..c2f9c03 100644 --- a/rtype.go +++ b/rtype.go @@ -11,14 +11,6 @@ import ( "unsafe" ) -func rtypeMethods(t *rtype) []method { - ut := t.Uncommon() - if ut == nil { - return nil - } - return ut.Methods() -} - func funcTypeIn(t *funcType) []*rtype { return t.InSlice() } @@ -312,10 +304,6 @@ func DumpType(w io.Writer, typ reflect.Type) { } } -func NumMethodX(typ reflect.Type) int { - return len(rtypeMethods(totype(typ))) -} - func MethodX(typ reflect.Type, i int) reflect.Method { return rtypeMethodX(totype(typ), i) } diff --git a/rtype_llgo.go b/rtype_llgo.go index 00185dd..9f4ca7d 100644 --- a/rtype_llgo.go +++ b/rtype_llgo.go @@ -105,3 +105,53 @@ func (ctx *Context) StructOf(fields []reflect.StructField) reflect.Type { func setEmbedded(f *structField) { f.Embedded_ = true } + +func rtypeMethodByNameX(t *rtype, name string) (m reflect.Method, ok bool) { + if reflect.Kind(t.Kind()) == reflect.Interface { + return toType(t).MethodByName(name) + } + if ut := t.Uncommon(); ut != nil { + for i, p := range ut.Methods() { + if p.Name_ == name { + return rtypeMethodX(t, i), true + } + } + } + return reflect.Method{}, false +} + +//go:linkname closureOf reflect.closureOf +func closureOf(ftyp *funcType) *rtype + +func rtypeMethodX(t *rtype, i int) (m reflect.Method) { + if reflect.Kind(t.Kind()) == reflect.Interface { + return toType(t).Method(i) + } + methods := rtypeMethods(t) + if i < 0 || i >= len(methods) { + panic("reflect: Method index out of range") + } + p := methods[i] + m.Name = p.Name() + fl := flag(reflect.Func) + ft := p.Mtyp_ + in := make([]reflect.Type, 0, 1+len(ft.In)) + in = append(in, toType(t)) + for _, arg := range ft.In { + in = append(in, toType(arg)) + } + out := make([]reflect.Type, 0, len(ft.Out)) + for _, ret := range ft.Out { + out = append(out, toType(ret)) + } + mt := reflect.FuncOf(in, out, ft.Variadic()) + m.Type = mt + mtfn := (*funcType)(unsafe.Pointer(totype(mt))) + fv := &struct { + fn unsafe.Pointer + env unsafe.Pointer + }{p.Tfn_, nil} + m.Func = toValue(Value{closureOf(mtfn), unsafe.Pointer(fv), fl | flagIndir}) + m.Index = i + return m +} From 9955a97bad9e483f1f75fc676950e85deb872c94 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 9 Jun 2026 16:22:59 +0800 Subject: [PATCH 11/13] llgo: newType --- rtype_llgo.go | 131 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/rtype_llgo.go b/rtype_llgo.go index 9f4ca7d..ae8d120 100644 --- a/rtype_llgo.go +++ b/rtype_llgo.go @@ -10,7 +10,9 @@ import ( ) func NamedTypeOf(pkgpath string, name string, from reflect.Type) reflect.Type { - panic("TODO: NamedTypeOf") + rt, _ := newType(pkgpath, name, from, 0, 0) + setTypeName(rt, pkgpath, name) + return toType(rt) } func setTypeName(t *rtype, pkgpath string, name string) { @@ -155,3 +157,130 @@ func rtypeMethodX(t *rtype, i int) (m reflect.Method) { m.Index = i return m } + +func newType(pkg string, name string, styp reflect.Type, mcount int, xcount int) (*rtype, []method) { + var rt *rtype + var fnoff uint32 + var tt reflect.Value + ort := totype(styp) + skind := styp.Kind() + switch skind { + case reflect.Struct: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(structType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*structType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + ost := (*structType)(unsafe.Pointer(ort)) + st.Fields = ost.Fields + case reflect.Ptr: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(ptrType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*ptrType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + st.Elem = totype(styp.Elem()) + case reflect.Interface: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(interfaceType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + })) + st := (*interfaceType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + ost := (*interfaceType)(unsafe.Pointer(ort)) + for _, m := range ost.Methods { + st.Methods = append(st.Methods, imethod{ + Name_: m.Name_, + Typ_: m.Typ_, + }) + } + case reflect.Slice: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(sliceType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*sliceType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + st.Elem = totype(styp.Elem()) + case reflect.Array: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(arrayType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*arrayType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + ost := (*arrayType)(unsafe.Pointer(ort)) + st.Elem = ost.Elem + st.Slice = ost.Slice + st.Len = ost.Len + case reflect.Chan: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(chanType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*chanType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + ost := (*chanType)(unsafe.Pointer(ort)) + st.Elem = ost.Elem + st.Dir = ost.Dir + case reflect.Func: + numIn := styp.NumIn() + numOut := styp.NumOut() + narg := numIn + numOut + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(funcType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "N", Type: reflect.ArrayOf(narg, reflect.TypeOf((*rtype)(nil)))}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*funcType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + ost := (*funcType)(unsafe.Pointer(ort)) + st.In = ost.In + st.Out = ost.Out + case reflect.Map: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(mapType{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + st := (*mapType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + ost := (*mapType)(unsafe.Pointer(ort)) + cloneMap(st, ost) + default: + tt = reflect.New(reflect.StructOf([]reflect.StructField{ + {Name: "S", Type: reflect.TypeOf(rtype{})}, + {Name: "U", Type: reflect.TypeOf(uncommonType{})}, + {Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))}, + })) + } + rt = (*rtype)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) + rt.Size_ = ort.Size_ + rt.TFlag = ort.TFlag | tflagUncommon + rt.Kind_ = ort.Kind_ + rt.Align_ = ort.Align_ + rt.FieldAlign_ = ort.FieldAlign_ + rt.GCData = ort.GCData + rt.PtrBytes = ort.PtrBytes + rt.Equal = ort.Equal + rt.Str_ = ort.Str_ + ut := (*uncommonType)(unsafe.Pointer(tt.Elem().Field(1).UnsafeAddr())) + ut.Mcount = uint16(mcount) + ut.Xcount = uint16(xcount) + ut.Moff = uint32(unsafe.Sizeof(uncommonType{})) + if skind == reflect.Interface { + return rt, nil + } else if skind == reflect.Func { + ut.Moff += fnoff + //return rt, tt.Elem().Field(3).Slice(0, mcount).Interface().([]method) + data := toWord(tt.Elem().Field(3).Slice(0, mcount).Interface()) + return rt, *(*[]method)(data) + } + //return rt, tt.Elem().Field(2).Slice(0, mcount).Interface().([]method) + data := toWord(tt.Elem().Field(2).Slice(0, mcount).Interface()) + return rt, *(*[]method)(data) +} + +func toWord(i interface{}) unsafe.Pointer { + return (*emptyInterface)(unsafe.Pointer(&i)).word +} From 7dca8112f77618e232ed02475833b2690f818dae Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 11 Jun 2026 15:44:10 +0800 Subject: [PATCH 12/13] llgo: fix newType for funcType --- reflectx_test.go | 4 ++-- rtype_llgo.go | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/reflectx_test.go b/reflectx_test.go index 4239cf2..e4673cd 100644 --- a/reflectx_test.go +++ b/reflectx_test.go @@ -249,7 +249,7 @@ func TestNamedType(t *testing.T) { typ := value.Type() nt := reflectx.NamedTypeOf("github.com/goplus/reflectx", fmt.Sprintf("MyType%v", i), typ) if nt.Kind() != typ.Kind() { - t.Errorf("kind: have %v, want %v", nt.Kind(), typ.Kind()) + t.Errorf("kind: %v have %v, want %v", typ, nt.Kind(), typ.Kind()) } if nt == typ { t.Errorf("same type, %v", typ) @@ -270,7 +270,7 @@ func TestNamedType(t *testing.T) { t.Errorf("name: have %v, want %v", nt2.Name(), name) } if nt2.PkgPath() != pkgpath { - t.Errorf("pkgpath: have %v, want %v", nt2.PkgPath(), pkgpath) + t.Errorf("pkgpath: %v have %v, want %v", typ, nt2.PkgPath(), pkgpath) } } } diff --git a/rtype_llgo.go b/rtype_llgo.go index ae8d120..e9320dd 100644 --- a/rtype_llgo.go +++ b/rtype_llgo.go @@ -10,8 +10,15 @@ import ( ) func NamedTypeOf(pkgpath string, name string, from reflect.Type) reflect.Type { + if from.Kind() == reflect.Func { + from = toType(closureOf(totype(from).FuncType())) + } rt, _ := newType(pkgpath, name, from, 0, 0) setTypeName(rt, pkgpath, name) + if rt.IsClosure() { + ft := toFuncType(rt.StructType()) + rt = &ft.Type + } return toType(rt) } @@ -24,15 +31,17 @@ func setTypeName(t *rtype, pkgpath string, name string) { _, f := path.Split(pkgpath) name = f + "." + name } - t.TFlag |= tflagNamed | tflagExtraStar - t.Str_ = "*" + name + t.TFlag |= tflagNamed + t.Str_ = name if t.TFlag&tflagUncommon == tflagUncommon { t.Uncommon().PkgPath_ = pkgpath } switch reflect.Kind(t.Kind()) { case reflect.Struct: st := (*structType)(toKindType(t)) - st.PkgPath_ = pkgpath + if !st.IsClosure() { + st.PkgPath_ = pkgpath + } case reflect.Interface: st := (*interfaceType)(toKindType(t)) st.PkgPath_ = pkgpath @@ -125,6 +134,9 @@ func rtypeMethodByNameX(t *rtype, name string) (m reflect.Method, ok bool) { //go:linkname closureOf reflect.closureOf func closureOf(ftyp *funcType) *rtype +//go:linkname toFuncType reflect.toFuncType +func toFuncType(ftyp *structType) *funcType + func rtypeMethodX(t *rtype, i int) (m reflect.Method) { if reflect.Kind(t.Kind()) == reflect.Interface { return toType(t).Method(i) From 8aaf4d291428f2422aacfd331d5830e433709e02 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 11 Jun 2026 15:48:14 +0800 Subject: [PATCH 13/13] llgo: fix rtypeMethodByNameX --- rtype_llgo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtype_llgo.go b/rtype_llgo.go index e9320dd..17de83d 100644 --- a/rtype_llgo.go +++ b/rtype_llgo.go @@ -123,7 +123,7 @@ func rtypeMethodByNameX(t *rtype, name string) (m reflect.Method, ok bool) { } if ut := t.Uncommon(); ut != nil { for i, p := range ut.Methods() { - if p.Name_ == name { + if p.Name() == name { return rtypeMethodX(t, i), true } }