github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/type.go (about) 1 package goloader 2 3 import ( 4 "cmd/objfile/goobj" 5 "cmd/objfile/obj" 6 "cmd/objfile/objabi" 7 "fmt" 8 "reflect" 9 "runtime" 10 "strings" 11 "unsafe" 12 ) 13 14 type tflag uint8 15 16 // See runtime/type.go _typePair 17 type _typePair struct { 18 t1 *_type 19 t2 *_type 20 } 21 22 // See reflect/value.go emptyInterface 23 type emptyInterface struct { 24 _type *_type 25 data unsafe.Pointer 26 } 27 28 type nonEmptyInterface struct { 29 // see ../runtime/iface.go:/Itab 30 itab *itab 31 word unsafe.Pointer 32 } 33 34 func efaceOf(ep *interface{}) *emptyInterface { 35 return (*emptyInterface)(unsafe.Pointer(ep)) 36 } 37 38 // See reflect/value.go sliceHeader 39 type sliceHeader struct { 40 Data uintptr 41 Len int 42 Cap int 43 } 44 45 // Method on non-interface type 46 type method struct { 47 name nameOff // name of method 48 mtyp typeOff // method type (without receiver) 49 ifn textOff // fn used in interface call (one-word receiver) 50 tfn textOff // fn used for normal method call 51 } 52 53 type imethod struct { 54 name nameOff 55 ityp typeOff 56 } 57 58 type interfacetype struct { 59 typ _type 60 pkgpath name 61 mhdr []imethod 62 } 63 64 type name struct { 65 bytes *byte 66 } 67 68 func (t *_type) uncommon() *uncommonType { return _uncommon(t) } 69 func (t *_type) nameOff(off nameOff) name { return _nameOff(t, off) } 70 func (t *_type) typeOff(off typeOff) *_type { return _typeOff(t, off) } 71 func (n name) name() string { return _name(n) } 72 func (n name) pkgPath() string { return _pkgPath(n) } 73 func (n name) isExported() bool { return _isExported(n) } 74 func (t *uncommonType) methods() []method { return _methods(t) } 75 func (t *_type) Kind() reflect.Kind { return _Kind(t) } 76 func (t *_type) Elem() *_type { return fromRType(_Elem(t)) } 77 78 // This replaces local package names with import paths, including where the package name doesn't match the last part of the import path e.g. 79 // 80 // import "github.com/org/somepackage/v4" + somepackage.SomeStruct 81 // => github.com/org/somepackage/v4.SomeStruct 82 func resolveFullyQualifiedSymbolName(t *_type) string { 83 typ := AsRType(t) 84 // go.shape is a special builtin package whose name shouldn't be escaped 85 pkgPath := unescapeGoShapePkg(objabi.PathToPrefix(t.PkgPath())) 86 87 name := typ.Name() 88 if name == "" { 89 name = nameFromTypeString(t) 90 } 91 var maybeStar string 92 if typ.String()[0] == '*' { 93 maybeStar = "*" 94 } 95 if pkgPath != "" && name != "" { 96 if strings.HasPrefix(pkgPath, "go.shape") { // go.shape Name()s don't necessarily match the String() 97 return typ.String() 98 } 99 return maybeStar + pkgPath + "." + name 100 } 101 switch t.Kind() { 102 case reflect.Ptr: 103 return "*" + resolveFullyQualifiedSymbolName(fromRType(typ.Elem())) 104 case reflect.Struct: 105 if typ.NumField() == 0 { 106 return typ.String() 107 } 108 fields := make([]string, typ.NumField()) 109 for i := 0; i < typ.NumField(); i++ { 110 fieldName := typ.Field(i).Name + " " 111 if typ.Field(i).Anonymous { 112 fieldName = "" 113 } 114 fieldPkgPath := "" 115 if typ.Field(i).PkgPath != "" && !typ.Field(i).Anonymous { 116 fieldPkgPath = unescapeGoShapePkg(objabi.PathToPrefix(typ.Field(i).PkgPath)) + "." 117 } 118 fieldStructTag := "" 119 if typ.Field(i).Tag != "" { 120 fieldStructTag = fmt.Sprintf(" %q", string(typ.Field(i).Tag)) 121 } 122 fields[i] = fmt.Sprintf("%s%s%s%s", fieldPkgPath, fieldName, resolveFullyQualifiedSymbolName(fromRType(typ.Field(i).Type)), fieldStructTag) 123 } 124 return fmt.Sprintf("struct { %s }", strings.Join(fields, "; ")) 125 case reflect.Map: 126 return fmt.Sprintf("map[%s]%s", resolveFullyQualifiedSymbolName(fromRType(typ.Key())), resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))) 127 case reflect.Chan: 128 switch reflect.ChanDir(typ.ChanDir()) { 129 case reflect.BothDir: 130 return fmt.Sprintf("chan %s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))) 131 case reflect.RecvDir: 132 return fmt.Sprintf("<-chan %s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))) 133 case reflect.SendDir: 134 return fmt.Sprintf("chan<- %s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))) 135 } 136 case reflect.Slice: 137 return fmt.Sprintf("[]%s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))) 138 case reflect.Array: 139 return fmt.Sprintf("[%d]%s", typ.Len(), resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))) 140 case reflect.Func: 141 ins := make([]string, typ.NumIn()) 142 outs := make([]string, typ.NumOut()) 143 for i := 0; i < typ.NumIn(); i++ { 144 ins[i] = resolveFullyQualifiedSymbolName(fromRType(typ.In(i))) 145 if i == typ.NumIn()-1 && typ.IsVariadic() { 146 ins[i] = "..." + resolveFullyQualifiedSymbolName(fromRType(typ.In(i).Elem())) 147 } 148 } 149 for i := 0; i < typ.NumOut(); i++ { 150 outs[i] = resolveFullyQualifiedSymbolName(fromRType(typ.Out(i))) 151 } 152 funcName := "func(" + strings.Join(ins, ", ") + ")" 153 if len(outs) > 0 { 154 funcName += " " 155 } 156 if len(outs) > 1 { 157 funcName += "(" 158 } 159 funcName += strings.Join(outs, ", ") 160 if len(outs) > 1 { 161 funcName += ")" 162 } 163 return funcName 164 case reflect.Interface: 165 if goobj.BuiltinIdx(TypePrefix+typ.String(), int(obj.ABI0)) != -1 { 166 // must be a builtin, 167 return typ.String() 168 } 169 if typ.NumMethod() == 0 { 170 return typ.String() 171 } 172 methods := make([]string, typ.NumMethod()) 173 ifaceT := (*interfacetype)(unsafe.Pointer(t)) 174 175 for i := 0; i < typ.NumMethod(); i++ { 176 methodType := _typeOff(t, ifaceT.mhdr[i].ityp) 177 methodName := _nameOff(t, ifaceT.mhdr[i].name).name() 178 methods[i] = fmt.Sprintf("%s(%s", methodName, strings.TrimPrefix(resolveFullyQualifiedSymbolName(methodType), "func(")) 179 } 180 reflect.TypeOf(0) 181 return fmt.Sprintf("interface { %s }", strings.Join(methods, "; ")) 182 default: 183 if goobj.BuiltinIdx(TypePrefix+typ.String(), int(obj.ABI0)) != -1 { 184 // must be a builtin, 185 return typ.String() 186 } 187 switch typ.String() { 188 case "int", "uint", "struct {}", "interface {}": 189 return typ.String() 190 } 191 panic("unexpected builtin type: " + typ.String()) 192 } 193 return "" 194 } 195 196 func nameFromTypeString(t *_type) string { 197 typ := AsRType(t) 198 s := typ.String() 199 i := len(s) - 1 200 sqBrackets := 0 201 for i >= 0 && (s[i] != '.' || sqBrackets != 0) { 202 switch s[i] { 203 case ']': 204 sqBrackets++ 205 case '[': 206 sqBrackets-- 207 } 208 i-- 209 } 210 return s[i+1:] 211 } 212 213 func fullyQualifiedMethodName(t *_type, method method) string { 214 methodName := t.nameOff(method.name).name() 215 216 // t.PkgPath() will always return a path, whereas AsRType(t).PkgPath() will only return if flag TNamed is set 217 pkgPath := unescapeGoShapePkg(objabi.PathToPrefix(t.PkgPath())) 218 name := nameFromTypeString(t) 219 if name == "" { 220 panic("method on type with no name") 221 } 222 if t.Kind() == reflect.Pointer { 223 return pkgPath + ".(*" + name + ")." + methodName 224 } 225 return pkgPath + "." + name + "." + methodName 226 227 } 228 229 func unescapeGoShapePkg(pkgPath string) string { 230 if pkgPath == "go%2eshape" { 231 return "go.shape" 232 } 233 return pkgPath 234 } 235 236 func symbolIsVariant(name string) (string, bool) { 237 const dot = "·" 238 const noAlgPrefix = TypePrefix + "noalg." 239 if strings.HasPrefix(name, TypePrefix+"struct {") || strings.HasPrefix(name, TypePrefix+"*struct {") { 240 // Anonymous structs might embed variant types, so these will need parsing first 241 ptr := false 242 if strings.HasPrefix(name, TypePrefix+"*struct {") { 243 ptr = true 244 } 245 fieldsStr := strings.TrimPrefix(name, TypePrefix+"struct { ") 246 fieldsStr = strings.TrimPrefix(name, TypePrefix+"*struct { ") 247 fieldsStr = strings.TrimSuffix(fieldsStr, " }") 248 fields := strings.Split(fieldsStr, "; ") 249 isVariant := false 250 for j, field := range fields { 251 var typeName string 252 var typeNameIndex int 253 fieldTypeTag := strings.SplitN(field, " ", 3) 254 // could be anonymous, or tagless, or both - we want to operate on the type 255 switch len(fieldTypeTag) { 256 case 1: 257 // Anonymous, tagless - just a type 258 typeName = fieldTypeTag[0] 259 case 2: 260 // could be a name + type, or type + tag 261 if strings.HasPrefix(fieldTypeTag[1], "\"") || strings.HasPrefix(fieldTypeTag[1], "`") { 262 // type + tag 263 typeName = fieldTypeTag[0] 264 } else { 265 // name + type 266 typeName = fieldTypeTag[1] 267 typeNameIndex = 1 268 } 269 case 3: 270 // Name + type + tag 271 typeName = fieldTypeTag[1] 272 typeNameIndex = 1 273 } 274 i := len(typeName) 275 for i > 0 && typeName[i-1] >= '0' && typeName[i-1] <= '9' { 276 i-- 277 } 278 if i >= len(dot) && typeName[i-len(dot):i] == dot { 279 isVariant = true 280 fieldTypeTag[typeNameIndex] = typeName[:i-len(dot)] 281 fields[j] = strings.Join(fieldTypeTag, " ") 282 } 283 } 284 if isVariant { 285 if ptr { 286 return TypePrefix + "*struct { " + strings.Join(fields, "; ") + " }", true 287 } 288 return TypePrefix + "struct { " + strings.Join(fields, "; ") + " }", true 289 290 } 291 return "", false 292 } else { 293 // need to double check for function scoped types which get a ·N suffix added, and also type.noalg.* variants 294 i := len(name) 295 for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' { 296 i-- 297 } 298 if i >= len(dot) && name[i-len(dot):i] == dot { 299 return name[:i-len(dot)], true 300 } else if strings.HasPrefix(name, noAlgPrefix) { 301 return TypePrefix + strings.TrimPrefix(name, noAlgPrefix), true 302 } 303 return "", false 304 } 305 } 306 307 func funcPkgPath(funcName string) string { 308 funcName = strings.TrimPrefix(funcName, TypeDoubleDotPrefix+"eq.") 309 // Anonymous struct methods can't have a package 310 if strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"struct {") || strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"(*struct {") { 311 return "" 312 } 313 lastSlash := strings.LastIndexByte(funcName, '/') 314 if lastSlash == -1 { 315 lastSlash = 0 316 } 317 // Generic dictionaries 318 firstDict := strings.Index(funcName, "..dict") 319 if firstDict > 0 { 320 return funcName[:firstDict] 321 } else { 322 // Methods on structs embedding structs from other packages look funny, e.g.: 323 // regexp.(*onePassInst).regexp/syntax.op 324 firstBracket := strings.LastIndex(funcName, ".(") 325 if firstBracket > 0 && lastSlash > firstBracket { 326 lastSlash = firstBracket 327 } 328 } 329 dot := lastSlash 330 for ; dot < len(funcName) && funcName[dot] != '.' && funcName[dot] != '(' && funcName[dot] != '['; dot++ { 331 } 332 pkgPath := funcName[:dot] 333 return strings.TrimPrefix(strings.TrimPrefix(pkgPath, TypePrefix+".eq."), "[...]") 334 } 335 336 func (t *_type) PkgPath() string { 337 ut := t.uncommon() 338 if ut == nil { 339 return EmptyString 340 } 341 return t.nameOff(ut.pkgpath).name() 342 } 343 344 func RegTypes(symPtr map[string]uintptr, interfaces ...interface{}) { 345 for _, inter := range interfaces { 346 v := reflect.ValueOf(inter) 347 regType(symPtr, v) 348 if v.Kind() == reflect.Ptr { 349 regType(symPtr, v.Elem()) 350 } 351 } 352 } 353 354 func regType(symPtr map[string]uintptr, v reflect.Value) { 355 inter := v.Interface() 356 if v.Kind() == reflect.Func && getFunctionPtr(inter) != 0 { 357 symPtr[runtime.FuncForPC(v.Pointer()).Name()] = getFunctionPtr(inter) 358 } else { 359 header := (*emptyInterface)(unsafe.Pointer(&inter)) 360 t := header._type 361 registerType(t, symPtr, map[string]struct{}{}) 362 } 363 } 364 365 func buildModuleTypeHash(module *moduledata, typeHash map[uint32][]*_type) { 366 for _, tl := range module.typelinks { 367 var t *_type 368 t = (*_type)(adduintptr(module.types, int(tl))) 369 registerTypeHash(t, typeHash) 370 } 371 } 372 373 func registerTypeHash(t *_type, typeHash map[uint32][]*_type) { 374 if t.Kind() == reflect.Invalid { 375 panic("Unexpected invalid kind during registration!") 376 } 377 378 tlist := typeHash[t.hash] 379 for _, tcur := range tlist { 380 if tcur == t { 381 return 382 } 383 } 384 typeHash[t.hash] = append(tlist, t) 385 386 switch t.Kind() { 387 case reflect.Ptr, reflect.Chan, reflect.Array, reflect.Slice: 388 // Indirect pointers + elems 389 element := t.Elem() 390 registerTypeHash(element, typeHash) 391 case reflect.Func: 392 typ := AsType(t) 393 for i := 0; i < typ.NumIn(); i++ { 394 registerTypeHash(toType(typ.In(i)), typeHash) 395 } 396 for i := 0; i < typ.NumOut(); i++ { 397 registerTypeHash(toType(typ.Out(i)), typeHash) 398 } 399 case reflect.Struct: 400 typ := AsType(t) 401 for i := 0; i < typ.NumField(); i++ { 402 registerTypeHash(toType(typ.Field(i).Type), typeHash) 403 } 404 case reflect.Map: 405 mt := (*mapType)(unsafe.Pointer(t)) 406 registerTypeHash(mt.key, typeHash) 407 registerTypeHash(mt.elem, typeHash) 408 case reflect.Bool, reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64, reflect.Int32, reflect.Uint32, reflect.Int16, reflect.Uint16, reflect.Int8, reflect.Uint8, reflect.Float64, reflect.Float32, reflect.String, reflect.UnsafePointer, reflect.Uintptr, reflect.Complex64, reflect.Complex128, reflect.Interface: 409 // Already added above 410 default: 411 panic(fmt.Sprintf("registerTypeHash found unexpected type (kind %s): ", t.Kind())) 412 } 413 }