@@ -260,10 +260,18 @@ type structField struct {
260260 data unsafe.Pointer // various bits of information, packed in a byte array
261261}
262262
263+ // Method set entry, as emitted by the compiler. Each entry pairs a signature
264+ // identity pointer (for Implements/AssignableTo comparison) with a pointer to
265+ // the method's null-terminated name string.
266+ type methodEntry struct {
267+ signature unsafe.Pointer
268+ name * byte
269+ }
270+
263271// Method set, as emitted by the compiler.
264272type methodSet struct {
265273 length uintptr
266- methods [0 ]unsafe. Pointer // variable number of method signature pointers
274+ methods [0 ]methodEntry
267275}
268276
269277// Equivalent to (go/types.Type).Underlying(): if this is a named type return
@@ -917,16 +925,16 @@ func (t *RawType) Implements(u Type) bool {
917925
918926// typeImplementsMethodSet checks whether the concrete type (identified by its
919927// typecode pointer) implements the given method set. Both the concrete type's
920- // method set and the asserted method set are sorted arrays of method signature
921- // pointers , so comparison is O(n+m).
928+ // method set and the asserted method set are sorted arrays of { signature, name}
929+ // entries , so comparison is O(n+m). Only the signature field is compared .
922930//
923931//go:linkname typeImplementsMethodSet runtime.typeImplementsMethodSet
924932func typeImplementsMethodSet (concreteType , assertedMethodSet unsafe.Pointer ) bool {
925933 if concreteType == nil {
926934 return false
927935 }
928936
929- const ptrSize = unsafe .Sizeof (( * byte )( nil ) )
937+ const entrySize = unsafe .Sizeof (methodEntry {} )
930938 itfNumMethod := * (* uintptr )(assertedMethodSet )
931939 if itfNumMethod == 0 {
932940 return true
@@ -965,13 +973,14 @@ func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) boo
965973 }
966974
967975 concreteTypePtr := unsafe .Pointer (& methods .methods )
968- concreteTypeEnd := unsafe .Add (concreteTypePtr , uintptr (methods .length )* ptrSize )
976+ concreteTypeEnd := unsafe .Add (concreteTypePtr , uintptr (methods .length )* entrySize )
969977
970978 // Iterate over each method in the interface method set, and check whether
971979 // the method exists in the method set of the concrete type.
972980 // Both method sets are sorted, so we can use a linear scan.
973- assertedTypePtr := unsafe .Add (assertedMethodSet , ptrSize )
974- assertedTypeEnd := unsafe .Add (assertedTypePtr , itfNumMethod * ptrSize )
981+ // Each entry is a {signature, name} pair; we compare only the signature.
982+ assertedTypePtr := unsafe .Add (assertedMethodSet , unsafe .Sizeof (uintptr (0 )))
983+ assertedTypeEnd := unsafe .Add (assertedTypePtr , itfNumMethod * entrySize )
975984 for assertedTypePtr != assertedTypeEnd {
976985 assertedMethod := * (* unsafe .Pointer )(assertedTypePtr )
977986
@@ -980,13 +989,13 @@ func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) boo
980989 return false
981990 }
982991 concreteMethod := * (* unsafe .Pointer )(concreteTypePtr )
983- concreteTypePtr = unsafe .Add (concreteTypePtr , ptrSize )
992+ concreteTypePtr = unsafe .Add (concreteTypePtr , entrySize )
984993 if concreteMethod == assertedMethod {
985994 break
986995 }
987996 }
988997
989- assertedTypePtr = unsafe .Add (assertedTypePtr , ptrSize )
998+ assertedTypePtr = unsafe .Add (assertedTypePtr , entrySize )
990999 }
9911000
9921001 return true
@@ -1025,14 +1034,157 @@ func (t *RawType) NumMethod() int {
10251034 case Struct :
10261035 return int ((* structType )(unsafe .Pointer (t )).numMethod & ^ uint16 (numMethodHasMethodSet ))
10271036 case Interface :
1028- //FIXME: Use len(methods )
1029- return ( * interfaceType )( unsafe . Pointer ( t )). ptrTo . NumMethod ( )
1037+ ct := ( * interfaceType )( unsafe . Pointer ( t . underlying ()) )
1038+ return int ( ct . methods . length )
10301039 }
10311040
10321041 // Other types have no methods attached. Note we don't panic here.
10331042 return 0
10341043}
10351044
1045+ // getMethodSet returns the method set for a type, or nil if the type has no
1046+ // inline method set.
1047+ func (t * RawType ) getMethodSet () * methodSet {
1048+ if t .isNamed () {
1049+ ct := (* namedType )(unsafe .Pointer (t ))
1050+ if ct .numMethod & numMethodHasMethodSet == 0 {
1051+ return nil
1052+ }
1053+ return (* methodSet )(unsafe .Add (unsafe .Pointer (ct ), unsafe .Sizeof (* ct )))
1054+ }
1055+ switch t .Kind () {
1056+ case Interface :
1057+ ct := (* interfaceType )(unsafe .Pointer (t .underlying ()))
1058+ return & ct .methods
1059+ case Pointer :
1060+ ct := (* ptrType )(unsafe .Pointer (t ))
1061+ if ct .numMethod & numMethodHasMethodSet == 0 {
1062+ return nil
1063+ }
1064+ return & ct .methods
1065+ case Struct :
1066+ ct := (* structType )(unsafe .Pointer (t ))
1067+ if ct .numMethod & numMethodHasMethodSet == 0 {
1068+ return nil
1069+ }
1070+ fieldSize := unsafe .Sizeof (structField {})
1071+ methodsPtr := unsafe .Add (unsafe .Pointer (& ct .fields [0 ]), uintptr (ct .numField )* fieldSize )
1072+ return (* methodSet )(methodsPtr )
1073+ }
1074+ return nil
1075+ }
1076+
1077+ // methodSetEntry returns the i-th entry in the method set.
1078+ func methodSetEntry (ms * methodSet , i int ) * methodEntry {
1079+ return (* methodEntry )(unsafe .Add (unsafe .Pointer (& ms .methods ), uintptr (i )* unsafe .Sizeof (methodEntry {})))
1080+ }
1081+
1082+ // isExportedMethod reports whether the method entry has an exported name.
1083+ // Unexported method names are stored as "pkg/path.name" by the compiler.
1084+ func isExportedMethod (entry * methodEntry ) bool {
1085+ if entry .name == nil {
1086+ return false // name was stripped by DCE
1087+ }
1088+ for name := entry .name ; * name != 0 ; name = (* byte )(unsafe .Add (unsafe .Pointer (name ), 1 )) {
1089+ if * name == '.' {
1090+ return false
1091+ }
1092+ }
1093+ return true
1094+ }
1095+
1096+ // methodName returns the name and pkgPath of a method entry.
1097+ // For exported methods, name is the method name and pkgPath is empty.
1098+ // For unexported methods, name is just the method name and pkgPath is
1099+ // the package path (stored as "pkg/path.name" by the compiler).
1100+ func methodName (entry * methodEntry ) (name , pkgPath string ) {
1101+ if entry .name == nil {
1102+ return "" , ""
1103+ }
1104+ full := readStringZ (unsafe .Pointer (entry .name ))
1105+ // Unexported methods are stored as "pkg/path.name".
1106+ for i := len (full ) - 1 ; i >= 0 ; i -- {
1107+ if full [i ] == '.' {
1108+ return full [i + 1 :], full [:i ]
1109+ }
1110+ }
1111+ return full , ""
1112+ }
1113+
1114+ // Method returns the i-th method in the type's method set.
1115+ // For non-interface types, this indexes only exported methods.
1116+ // For interface types, all methods are included.
1117+ //
1118+ //go:linkname reflectTypeMethodByIndex reflect.(*rawType).Method
1119+ func (t * RawType ) Method (i int ) MethodInfo {
1120+ n := t .NumMethod ()
1121+ if i < 0 || i >= n {
1122+ panic ("reflect: Method index out of range" )
1123+ }
1124+ ms := t .getMethodSet ()
1125+ if ms == nil {
1126+ // Method set was pruned or stripped; name unavailable.
1127+ return MethodInfo {Index : i }
1128+ }
1129+ isIface := t .Kind () == Interface
1130+ exportedIdx := 0
1131+ for j := 0 ; j < int (ms .length ); j ++ {
1132+ entry := methodSetEntry (ms , j )
1133+ if ! isIface && ! isExportedMethod (entry ) {
1134+ continue
1135+ }
1136+ if exportedIdx == i {
1137+ name , pkgPath := methodName (entry )
1138+ return MethodInfo {
1139+ Name : name ,
1140+ PkgPath : pkgPath ,
1141+ Index : i ,
1142+ }
1143+ }
1144+ exportedIdx ++
1145+ }
1146+ // Method set was pruned; name unavailable.
1147+ return MethodInfo {Index : i }
1148+ }
1149+
1150+ // MethodByName returns the method with the given name in the type's method
1151+ // set, and a boolean indicating if the method was found.
1152+ // For non-interface types, only exported methods are searched.
1153+ //
1154+ //go:linkname reflectTypeMethodByName reflect.(*rawType).MethodByName
1155+ func (t * RawType ) MethodByName (name string ) (MethodInfo , bool ) {
1156+ ms := t .getMethodSet ()
1157+ if ms == nil {
1158+ return MethodInfo {}, false
1159+ }
1160+ isIface := t .Kind () == Interface
1161+ exportedIdx := 0
1162+ for j := 0 ; j < int (ms .length ); j ++ {
1163+ entry := methodSetEntry (ms , j )
1164+ if ! isIface && ! isExportedMethod (entry ) {
1165+ continue
1166+ }
1167+ ename , pkgPath := methodName (entry )
1168+ if ename == name {
1169+ return MethodInfo {
1170+ Name : name ,
1171+ PkgPath : pkgPath ,
1172+ Index : exportedIdx ,
1173+ }, true
1174+ }
1175+ exportedIdx ++
1176+ }
1177+ return MethodInfo {}, false
1178+ }
1179+
1180+ // MethodInfo describes a single method. This is the internal reflectlite
1181+ // representation; the reflect package wraps this in reflect.Method.
1182+ type MethodInfo struct {
1183+ Name string
1184+ PkgPath string
1185+ Index int
1186+ }
1187+
10361188// Read and return a null terminated string starting from data.
10371189func readStringZ (data unsafe.Pointer ) string {
10381190 start := data
@@ -1051,8 +1203,8 @@ func (t *RawType) name() string {
10511203 ptr := unsafe .Add (unsafe .Pointer (ntype ), unsafe .Sizeof (* ntype ))
10521204 if ntype .numMethod & numMethodHasMethodSet != 0 {
10531205 ms := (* methodSet )(ptr )
1054- // Skip past the length field and the method pointer entries.
1055- ptr = unsafe .Add (ptr , unsafe .Sizeof (uintptr (0 ))+ uintptr (ms .length )* unsafe .Sizeof (unsafe . Pointer ( nil ) ))
1206+ // Skip past the length field and the method entries.
1207+ ptr = unsafe .Add (ptr , unsafe .Sizeof (uintptr (0 ))+ uintptr (ms .length )* unsafe .Sizeof (methodEntry {} ))
10561208 }
10571209 return readStringZ (ptr )
10581210}
0 commit comments