@@ -252,10 +252,18 @@ type structField struct {
252252 data unsafe.Pointer // various bits of information, packed in a byte array
253253}
254254
255+ // Method set entry, as emitted by the compiler. Each entry pairs a signature
256+ // identity pointer (for Implements/AssignableTo comparison) with a pointer to
257+ // the method's null-terminated name string.
258+ type methodEntry struct {
259+ signature unsafe.Pointer
260+ name * byte
261+ }
262+
255263// Method set, as emitted by the compiler.
256264type methodSet struct {
257265 length uintptr
258- methods [0 ]unsafe. Pointer // variable number of method signature pointers
266+ methods [0 ]methodEntry
259267}
260268
261269// Equivalent to (go/types.Type).Underlying(): if this is a named type return
@@ -805,16 +813,16 @@ func (t *RawType) Implements(u Type) bool {
805813
806814// typeImplementsMethodSet checks whether the concrete type (identified by its
807815// typecode pointer) implements the given method set. Both the concrete type's
808- // method set and the asserted method set are sorted arrays of method signature
809- // pointers , so comparison is O(n+m).
816+ // method set and the asserted method set are sorted arrays of { signature, name}
817+ // entries , so comparison is O(n+m). Only the signature field is compared .
810818//
811819//go:linkname typeImplementsMethodSet runtime.typeImplementsMethodSet
812820func typeImplementsMethodSet (concreteType , assertedMethodSet unsafe.Pointer ) bool {
813821 if concreteType == nil {
814822 return false
815823 }
816824
817- const ptrSize = unsafe .Sizeof (( * byte )( nil ) )
825+ const entrySize = unsafe .Sizeof (methodEntry {} )
818826 itfNumMethod := * (* uintptr )(assertedMethodSet )
819827 if itfNumMethod == 0 {
820828 return true
@@ -853,13 +861,14 @@ func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) boo
853861 }
854862
855863 concreteTypePtr := unsafe .Pointer (& methods .methods )
856- concreteTypeEnd := unsafe .Add (concreteTypePtr , uintptr (methods .length )* ptrSize )
864+ concreteTypeEnd := unsafe .Add (concreteTypePtr , uintptr (methods .length )* entrySize )
857865
858866 // Iterate over each method in the interface method set, and check whether
859867 // the method exists in the method set of the concrete type.
860868 // Both method sets are sorted, so we can use a linear scan.
861- assertedTypePtr := unsafe .Add (assertedMethodSet , ptrSize )
862- assertedTypeEnd := unsafe .Add (assertedTypePtr , itfNumMethod * ptrSize )
869+ // Each entry is a {signature, name} pair; we compare only the signature.
870+ assertedTypePtr := unsafe .Add (assertedMethodSet , unsafe .Sizeof (uintptr (0 )))
871+ assertedTypeEnd := unsafe .Add (assertedTypePtr , itfNumMethod * entrySize )
863872 for assertedTypePtr != assertedTypeEnd {
864873 assertedMethod := * (* unsafe .Pointer )(assertedTypePtr )
865874
@@ -868,13 +877,13 @@ func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) boo
868877 return false
869878 }
870879 concreteMethod := * (* unsafe .Pointer )(concreteTypePtr )
871- concreteTypePtr = unsafe .Add (concreteTypePtr , ptrSize )
880+ concreteTypePtr = unsafe .Add (concreteTypePtr , entrySize )
872881 if concreteMethod == assertedMethod {
873882 break
874883 }
875884 }
876885
877- assertedTypePtr = unsafe .Add (assertedTypePtr , ptrSize )
886+ assertedTypePtr = unsafe .Add (assertedTypePtr , entrySize )
878887 }
879888
880889 return true
@@ -921,6 +930,104 @@ func (t *RawType) NumMethod() int {
921930 return 0
922931}
923932
933+ // getMethodSet returns the method set for a type, or nil if the type has no
934+ // inline method set.
935+ func (t * RawType ) getMethodSet () * methodSet {
936+ if t .isNamed () {
937+ ct := (* namedType )(unsafe .Pointer (t ))
938+ if ct .numMethod & numMethodHasMethodSet == 0 {
939+ return nil
940+ }
941+ return (* methodSet )(unsafe .Add (unsafe .Pointer (ct ), unsafe .Sizeof (* ct )))
942+ }
943+ switch t .Kind () {
944+ case Interface :
945+ ct := (* interfaceType )(unsafe .Pointer (t .underlying ()))
946+ return & ct .methods
947+ case Pointer :
948+ ct := (* ptrType )(unsafe .Pointer (t ))
949+ if ct .numMethod & numMethodHasMethodSet == 0 {
950+ return nil
951+ }
952+ return & ct .methods
953+ case Struct :
954+ ct := (* structType )(unsafe .Pointer (t ))
955+ if ct .numMethod & numMethodHasMethodSet == 0 {
956+ return nil
957+ }
958+ fieldSize := unsafe .Sizeof (structField {})
959+ methodsPtr := unsafe .Add (unsafe .Pointer (& ct .fields [0 ]), uintptr (ct .numField )* fieldSize )
960+ return (* methodSet )(methodsPtr )
961+ }
962+ return nil
963+ }
964+
965+ // methodSetEntry returns the i-th entry in the method set.
966+ func methodSetEntry (ms * methodSet , i int ) * methodEntry {
967+ return (* methodEntry )(unsafe .Add (unsafe .Pointer (& ms .methods ), uintptr (i )* unsafe .Sizeof (methodEntry {})))
968+ }
969+
970+ // Method returns the i-th method in the type's method set.
971+ //
972+ //go:linkname reflectTypeMethodByIndex reflect.(*rawType).Method
973+ func (t * RawType ) Method (i int ) MethodInfo {
974+ methodSetLookup () // Ensure method name data is preserved.
975+ n := t .NumMethod ()
976+ if i < 0 || i >= n {
977+ panic ("reflect: Method index out of range" )
978+ }
979+ ms := t .getMethodSet ()
980+ if ms == nil || int (ms .length ) <= i {
981+ // Method set was pruned or stripped; name unavailable.
982+ return MethodInfo {Index : i }
983+ }
984+ entry := methodSetEntry (ms , i )
985+ name := readStringZ (unsafe .Pointer (entry .name ))
986+ return MethodInfo {
987+ Name : name ,
988+ Index : i ,
989+ }
990+ }
991+
992+ // MethodByName returns the method with the given name in the type's method
993+ // set, and a boolean indicating if the method was found.
994+ //
995+ //go:linkname reflectTypeMethodByName reflect.(*rawType).MethodByName
996+ func (t * RawType ) MethodByName (name string ) (MethodInfo , bool ) {
997+ methodSetLookup () // Ensure method name data is preserved.
998+ ms := t .getMethodSet ()
999+ if ms == nil {
1000+ return MethodInfo {}, false
1001+ }
1002+ n := int (ms .length )
1003+ for i := 0 ; i < n ; i ++ {
1004+ entry := methodSetEntry (ms , i )
1005+ ename := readStringZ (unsafe .Pointer (entry .name ))
1006+ if ename == name {
1007+ return MethodInfo {
1008+ Name : name ,
1009+ Index : i ,
1010+ }, true
1011+ }
1012+ }
1013+ return MethodInfo {}, false
1014+ }
1015+
1016+ // MethodInfo describes a single method. This is the internal reflectlite
1017+ // representation; the reflect package wraps this in reflect.Method.
1018+ type MethodInfo struct {
1019+ Name string
1020+ PkgPath string
1021+ Index int
1022+ }
1023+
1024+ // methodSetLookup is a sentinel function whose presence signals to the
1025+ // interface lowering pass that method name data must be preserved.
1026+ //
1027+ //go:linkname methodSetLookup runtime.methodSetLookup
1028+ //go:noinline
1029+ func methodSetLookup () {}
1030+
9241031// Read and return a null terminated string starting from data.
9251032func readStringZ (data unsafe.Pointer ) string {
9261033 start := data
@@ -942,8 +1049,8 @@ func (t *RawType) name() string {
9421049 ptr := unsafe .Add (unsafe .Pointer (ntype ), unsafe .Sizeof (* ntype ))
9431050 if ntype .numMethod & numMethodHasMethodSet != 0 {
9441051 ms := (* methodSet )(ptr )
945- // Skip past the length field and the method pointer entries.
946- ptr = unsafe .Add (ptr , unsafe .Sizeof (uintptr (0 ))+ uintptr (ms .length )* unsafe .Sizeof (unsafe . Pointer ( nil ) ))
1052+ // Skip past the length field and the method entries.
1053+ ptr = unsafe .Add (ptr , unsafe .Sizeof (uintptr (0 ))+ uintptr (ms .length )* unsafe .Sizeof (methodEntry {} ))
9471054 }
9481055 return readStringZ (ptr )
9491056}
0 commit comments