Skip to content

Commit d2a90c6

Browse files
committed
compiler: support Method and MethodByName
Add reflective method lookup and method iteration metadata, preserving complete method sets whenever reflection needs them.
1 parent 7f1f99b commit d2a90c6

10 files changed

Lines changed: 476 additions & 90 deletions

File tree

compiler/interface.go

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,13 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
225225
}
226226
methods = append(methods, fn)
227227
}
228+
methodEntryType := types.NewStruct([]*types.Var{
229+
types.NewVar(token.NoPos, nil, "signature", types.Typ[types.UnsafePointer]),
230+
types.NewVar(token.NoPos, nil, "name", types.Typ[types.UnsafePointer]),
231+
}, nil)
228232
methodSetType := types.NewStruct([]*types.Var{
229233
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
230-
types.NewVar(token.NoPos, nil, "methods", types.NewArray(types.Typ[types.UnsafePointer], int64(len(methods)))),
234+
types.NewVar(token.NoPos, nil, "methods", types.NewArray(methodEntryType, int64(len(methods)))),
231235
}, nil)
232236
methodSetValue := c.getMethodSetValue(methods)
233237
switch typ := typ.(type) {
@@ -1189,12 +1193,15 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
11891193
}
11901194

11911195
// getMethodSetValue creates the method set struct value for a list of methods.
1192-
// The struct contains a length and a sorted array of method signature pointers.
1196+
// The struct contains a length and a sorted array of {signature, name} entries.
1197+
// Each entry pairs a method signature pointer (for Implements comparison) with
1198+
// a pointer to the method's null-terminated name string.
11931199
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
11941200
// Create a sorted list of method signature global names.
11951201
type methodRef struct {
1196-
name string
1197-
value llvm.Value
1202+
sigGlobalName string
1203+
methodName string
1204+
sigValue llvm.Value
11981205
}
11991206
var refs []methodRef
12001207
for _, method := range methods {
@@ -1225,23 +1232,44 @@ func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
12251232
value.AddMetadata(0, diglobal)
12261233
}
12271234
}
1228-
refs = append(refs, methodRef{globalName, value})
1235+
refs = append(refs, methodRef{globalName, name, value})
12291236
}
12301237
sort.Slice(refs, func(i, j int) bool {
1231-
return refs[i].name < refs[j].name
1238+
return refs[i].sigGlobalName < refs[j].sigGlobalName
12321239
})
12331240

1234-
var values []llvm.Value
1241+
pairType := c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
1242+
var pairs []llvm.Value
12351243
for _, ref := range refs {
1236-
values = append(values, ref.value)
1244+
nameGlobal := c.getMethodNameGlobal(ref.methodName)
1245+
pair := c.ctx.ConstStruct([]llvm.Value{ref.sigValue, nameGlobal}, false)
1246+
pairs = append(pairs, pair)
12371247
}
12381248

12391249
return c.ctx.ConstStruct([]llvm.Value{
1240-
llvm.ConstInt(c.uintptrType, uint64(len(values)), false),
1241-
llvm.ConstArray(c.dataPtrType, values),
1250+
llvm.ConstInt(c.uintptrType, uint64(len(pairs)), false),
1251+
llvm.ConstArray(pairType, pairs),
12421252
}, false)
12431253
}
12441254

1255+
// getMethodNameGlobal returns a global containing the null-terminated method
1256+
// name string, creating it if needed.
1257+
func (c *compilerContext) getMethodNameGlobal(name string) llvm.Value {
1258+
globalName := "reflect/types.methodname:" + name
1259+
g := c.mod.NamedGlobal(globalName)
1260+
if !g.IsNil() {
1261+
return g
1262+
}
1263+
nameBytes := c.ctx.ConstString(name+"\x00", false)
1264+
g = llvm.AddGlobal(c.mod, nameBytes.Type(), globalName)
1265+
g.SetInitializer(nameBytes)
1266+
g.SetGlobalConstant(true)
1267+
g.SetLinkage(llvm.LinkOnceODRLinkage)
1268+
g.SetAlignment(1)
1269+
g.SetUnnamedAddr(true)
1270+
return g
1271+
}
1272+
12451273
// getInvokeFunction returns the thunk to call the given interface method. The
12461274
// thunk is declared, not defined: it will be defined by the interface lowering
12471275
// pass.

compiler/testdata/go1.27.ll

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ target triple = "wasm32-unknown-wasi"
77
%runtime._interface = type { ptr, ptr }
88

99
@"reflect/types.signature:Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
10-
@"reflect/types.type:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] } { ptr @"named:main.genericMethod$methodset", i8 122, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, { i32, [1 x ptr] } }, ptr @"reflect/types.type:pointer:named:main.genericMethod", i32 0, i32 1), ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] }, [19 x i8] c"main.genericMethod\00" }, align 4
10+
@"reflect/types.methodname:Regular" = linkonce_odr unnamed_addr constant [8 x i8] c"Regular\00", align 1
11+
@"reflect/types.type:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [19 x i8] } { ptr @"named:main.genericMethod$methodset", i8 122, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, { i32, [1 x { ptr, ptr }] } }, ptr @"reflect/types.type:pointer:named:main.genericMethod", i32 0, i32 1), ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}", ptr @"reflect/types.methodname:Regular" }] }, [19 x i8] c"main.genericMethod\00" }, align 4
1112
@"reflect/types.type.pkgpath:main" = linkonce_odr unnamed_addr constant [5 x i8] c"main\00", align 1
12-
@"reflect/types.type:pointer:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, { i32, [1 x ptr] } } { ptr @"pointer:named:main.genericMethod$methodset", i8 -43, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] } }, align 4
13+
@"reflect/types.type:pointer:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, { i32, [1 x { ptr, ptr }] } } { ptr @"pointer:named:main.genericMethod$methodset", i8 -43, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}", ptr @"reflect/types.methodname:Regular" }] } }, align 4
1314
@"reflect/methods.Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
1415
@"main$string" = internal unnamed_addr constant [18 x i8] c"main.genericMethod", align 1
1516
@"main$string.1" = internal unnamed_addr constant [7 x i8] c"Regular", align 1
@@ -21,7 +22,7 @@ target triple = "wasm32-unknown-wasi"
2122
@"reflect/types.type:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [23 x i8] } { i8 122, i16 0, ptr @"reflect/types.type:pointer:named:main.onlyGenericMethod", ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", [23 x i8] c"main.onlyGenericMethod\00" }, align 4
2223
@"reflect/types.type:pointer:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.onlyGenericMethod" }, align 4
2324

24-
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
25+
declare void @runtime.trackPointer(ptr readonly captures(none), ptr, ptr) #0
2526

2627
; Function Attrs: nounwind
2728
define hidden void @main.init(ptr %context) unnamed_addr #1 {
@@ -43,7 +44,7 @@ entry:
4344
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
4445
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:named:main.onlyGenericMethod", ptr nonnull %stackalloc, ptr undef) #2
4546
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
46-
ret { %runtime._interface, %runtime._interface } { %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), ptr null }, %runtime._interface { ptr @"reflect/types.type:named:main.onlyGenericMethod", ptr null } }
47+
ret { %runtime._interface, %runtime._interface } { %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), ptr null }, %runtime._interface { ptr @"reflect/types.type:named:main.onlyGenericMethod", ptr null } }
4748
}
4849

4950
; Function Attrs: nounwind

compiler/testdata/interface.ll

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ target triple = "wasm32-unknown-wasi"
1010
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
1111
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
1212
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
13-
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 -32767, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
13+
@"reflect/types.methodname:Error" = linkonce_odr unnamed_addr constant [6 x i8] c"Error\00", align 1
14+
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [7 x i8] } { i8 116, i16 -32767, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Error:func:{}{basic:string}", ptr @"reflect/types.methodname:Error" }] }, [7 x i8] c".error\00" }, align 4
1415
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
15-
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
16+
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x { ptr, ptr }] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Error:func:{}{basic:string}", ptr @"reflect/types.methodname:Error" }] } }, align 4
1617
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
1718
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
1819
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
19-
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
20+
@"reflect/types.methodname:String" = linkonce_odr unnamed_addr constant [7 x i8] c"String\00", align 1
21+
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x { ptr, ptr }] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:String:func:{}{basic:string}", ptr @"reflect/types.methodname:String" }] } }, align 4
2022
@"reflect/types.typeid:basic:int" = external constant i8
2123

2224
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0

src/internal/reflectlite/type.go

Lines changed: 165 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
264272
type 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
924932
func 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.
10371189
func 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

Comments
 (0)