github.com/zeebo/goof@v0.0.0-20230907150950-e9457bc94477/reflect_helpers.go (about) 1 package goof 2 3 import ( 4 "fmt" 5 "path" 6 "reflect" 7 "strings" 8 "unsafe" 9 ) 10 11 func ifaces(values []reflect.Value) []interface{} { 12 out := make([]interface{}, 0, len(values)) 13 for _, value := range values { 14 out = append(out, value.Interface()) 15 } 16 return out 17 } 18 19 func reflectCanBeNil(rtyp reflect.Type) bool { 20 switch rtyp.Kind() { 21 case reflect.Interface, 22 reflect.Ptr, 23 reflect.Map, 24 reflect.Chan, 25 reflect.Slice: 26 return true 27 } 28 return false 29 } 30 31 func typesByString(types []reflect.Type) sortTypesByString { 32 cache := make([]string, 0, len(types)) 33 for _, typ := range types { 34 cache = append(cache, typ.String()) 35 } 36 return sortTypesByString{ 37 types: types, 38 cache: cache, 39 } 40 } 41 42 type sortTypesByString struct { 43 types []reflect.Type 44 cache []string 45 } 46 47 func (s sortTypesByString) Len() int { return len(s.types) } 48 49 func (s sortTypesByString) Less(i, j int) bool { 50 return s.cache[i] < s.cache[j] 51 } 52 53 func (s sortTypesByString) Swap(i, j int) { 54 s.cache[i], s.cache[j] = s.cache[j], s.cache[i] 55 s.types[i], s.types[j] = s.types[j], s.types[i] 56 } 57 58 var ( 59 unsafePointerType = reflect.TypeOf((*unsafe.Pointer)(nil)).Elem() 60 ) 61 62 // dwarfName does a best effort to return the dwarf entry name for the reflect 63 // type so that we can map between them. here's hoping it doesn't do it wrong 64 func dwarfName(rtyp reflect.Type) (out string) { 65 pkg_path := rtyp.PkgPath() 66 name := rtyp.Name() 67 68 switch { 69 // this type's PkgPath returns "" instead of "unsafe". hah. 70 case rtyp == unsafePointerType: 71 return "unsafe.Pointer" 72 73 case pkg_path != "": 74 // this is crazy, but sometimes a dot is encoded as %2e, but only when 75 // it's in the last path component. i wonder if this is sufficient. 76 if strings.Contains(pkg_path, "/") { 77 dir := path.Dir(pkg_path) 78 base := strings.Replace(path.Base(pkg_path), ".", "%2e", -1) 79 pkg_path = dir + "/" + base 80 } 81 82 return pkg_path + "." + name 83 84 case name != "": 85 return name 86 87 default: 88 switch rtyp.Kind() { 89 case reflect.Ptr: 90 return "*" + dwarfName(rtyp.Elem()) 91 92 case reflect.Slice: 93 return "[]" + dwarfName(rtyp.Elem()) 94 95 case reflect.Array: 96 return fmt.Sprintf("[%d]%s", 97 rtyp.Len(), 98 dwarfName(rtyp.Elem())) 99 100 case reflect.Map: 101 return fmt.Sprintf("map[%s]%s", 102 dwarfName(rtyp.Key()), 103 dwarfName(rtyp.Elem())) 104 105 case reflect.Chan: 106 prefix := "chan" 107 switch rtyp.ChanDir() { 108 case reflect.SendDir: 109 prefix = "chan<-" 110 case reflect.RecvDir: 111 prefix = "<-chan" 112 } 113 return fmt.Sprintf("%s %s", 114 prefix, 115 dwarfName(rtyp.Elem())) 116 117 // TODO: func, struct 118 119 default: 120 // oh well. this sometimes works. 121 return rtyp.String() 122 } 123 } 124 }