github.com/goplus/reflectx@v1.2.2/methodof.go (about) 1 //go:build !js || (js && wasm) 2 // +build !js js,wasm 3 4 package reflectx 5 6 import ( 7 "fmt" 8 "log" 9 "reflect" 10 "sort" 11 "strings" 12 "unsafe" 13 14 "github.com/goplus/reflectx/abi" 15 _ "github.com/goplus/reflectx/internal/icall512" 16 ) 17 18 // icall stat 19 func IcallStat() (capacity int, allocate int, aviable int) { 20 mps := abi.Default 21 return mps.Cap(), mps.Used(), mps.Available() 22 } 23 24 func resetAll() { 25 abi.Default.Clear() 26 } 27 28 func (ctx *Context) Reset() { 29 for mp, list := range ctx.methodIndexList { 30 mp.Remove(list) 31 } 32 ctx.nAllocateError = 0 33 ctx.embedLookupCache = make(map[reflect.Type]reflect.Type) 34 ctx.structLookupCache = make(map[string][]reflect.Type) 35 ctx.interfceLookupCache = make(map[string]reflect.Type) 36 ctx.methodIndexList = make(map[abi.MethodProvider][]int) 37 } 38 39 func (ctx *Context) IcallAlloc() int { 40 n := 0 41 for _, list := range ctx.methodIndexList { 42 n += len(list) 43 } 44 return n 45 } 46 47 func methodInfoText(info *abi.MethodInfo) string { 48 if info.Pointer { 49 return "(*" + info.Type.String() + ")." + info.Name 50 } 51 return info.Type.String() + "." + info.Name 52 } 53 54 // register method info 55 func (ctx *Context) registerMethod(info *abi.MethodInfo) (ifn unsafe.Pointer, allocated bool) { 56 for _, mp := range abi.Default.List() { 57 if mp.Available() == 0 { 58 continue 59 } 60 ifn, index := mp.Insert(info) 61 if index == -1 { 62 break 63 } 64 ctx.methodIndexList[mp] = append(ctx.methodIndexList[mp], index) 65 return ifn, true 66 } 67 ctx.nAllocateError++ 68 return nil, false 69 } 70 71 func isMethod(typ reflect.Type) (ok bool) { 72 return totype(typ).tflag&tflagUserMethod != 0 73 } 74 75 type MethodInfo struct { 76 Name string 77 Func reflect.Value 78 Type reflect.Type 79 InTyp reflect.Type 80 OutTyp reflect.Type 81 InSize uintptr 82 OutSize uintptr 83 Pointer bool 84 Indirect bool 85 Variadic bool 86 OnePtr bool 87 } 88 89 func MethodByIndex(typ reflect.Type, index int) reflect.Method { 90 return totype(typ).MethodX(index) 91 } 92 93 func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) { 94 m, ok = totype(typ).MethodByNameX(name) 95 return 96 } 97 98 func resizeMethod(typ reflect.Type, mcount int, xcount int) error { 99 rt := totype(typ) 100 ut := toUncommonType(rt) 101 if ut == nil { 102 return fmt.Errorf("not found uncommonType of %v", typ) 103 } 104 if uint16(mcount) > ut.mcount { 105 return fmt.Errorf("too many methods of %v", typ) 106 } 107 ut.xcount = uint16(xcount) 108 return nil 109 } 110 111 func createMethod(typ reflect.Type, ptyp reflect.Type, m Method, index int) (mfn reflect.Value, inTyp, outTyp reflect.Type, mtyp typeOff, tfn, ptfn textOff) { 112 var in []reflect.Type 113 var out []reflect.Type 114 var ntyp reflect.Type 115 in, out, ntyp, inTyp, outTyp = parserMethodType(m.Type, nil) 116 mtyp = resolveReflectType(totype(ntyp)) 117 var ftyp reflect.Type 118 if m.Pointer { 119 ftyp = reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, m.Type.IsVariadic()) 120 } else { 121 ftyp = reflect.FuncOf(append([]reflect.Type{typ}, in...), out, m.Type.IsVariadic()) 122 } 123 124 mfn = reflect.MakeFunc(ftyp, m.Func) 125 ptr := tovalue(&mfn).ptr 126 127 tfn = resolveReflectText(unsafe.Pointer(ptr)) 128 if !m.Pointer { 129 variadic := m.Type.IsVariadic() 130 ctyp := reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, variadic) 131 var cv reflect.Value 132 if variadic { 133 cv = reflect.MakeFunc(ctyp, func(args []reflect.Value) (results []reflect.Value) { 134 return args[0].Elem().Method(index).CallSlice(args[1:]) 135 }) 136 } else { 137 cv = reflect.MakeFunc(ctyp, func(args []reflect.Value) (results []reflect.Value) { 138 return args[0].Elem().Method(index).Call(args[1:]) 139 }) 140 } 141 ptfn = resolveReflectText(tovalue(&cv).ptr) 142 } else { 143 ptfn = tfn 144 } 145 return 146 } 147 148 func (ctx *Context) setMethodSet(typ reflect.Type, methods []Method) error { 149 sort.Slice(methods, func(i, j int) bool { 150 n := strings.Compare(methods[i].Name, methods[j].Name) 151 if n == 0 && methods[i].PkgPath == methods[j].PkgPath { 152 panic(fmt.Sprintf("method redeclared: %v", methods[j].Name)) 153 } 154 return n < 0 155 }) 156 var mcount, pcount int 157 var xcount, pxcount int 158 pcount = len(methods) 159 var mlist []string 160 for _, m := range methods { 161 isexport := methodIsExported(m.Name) 162 if isexport { 163 pxcount++ 164 } 165 if !m.Pointer { 166 if isexport { 167 xcount++ 168 } 169 mlist = append(mlist, m.Name) 170 mcount++ 171 } 172 } 173 ptyp := reflect.PtrTo(typ) 174 if err := resizeMethod(typ, mcount, xcount); err != nil { 175 return err 176 } 177 if err := resizeMethod(ptyp, pcount, pxcount); err != nil { 178 return err 179 } 180 rt := totype(typ) 181 prt := totype(ptyp) 182 183 ms := rt.methods() 184 pms := prt.methods() 185 186 var onePtr bool 187 switch typ.Kind() { 188 case reflect.Func, reflect.Chan, reflect.Map: 189 onePtr = true 190 case reflect.Struct: 191 onePtr = typ.NumField() == 1 && typ.Field(0).Type.Kind() == reflect.Ptr 192 } 193 var index int 194 for i, m := range methods { 195 isexport := methodIsExported(m.Name) 196 nm := newNameEx(m.Name, "", isexport, !isexport) 197 if !isexport { 198 nm.setPkgPath(m.PkgPath) 199 } 200 mname := resolveReflectName(nm) 201 mfn, inTyp, outTyp, mtyp, tfn, ptfn := createMethod(typ, ptyp, m, index) 202 isz := argsTypeSize(inTyp, true) 203 osz := argsTypeSize(outTyp, false) 204 pinfo := &abi.MethodInfo{ 205 Name: m.Name, 206 Type: typ, 207 Func: mfn, 208 InTyp: inTyp, 209 OutTyp: outTyp, 210 InSize: isz, 211 OutSize: osz, 212 Pointer: true, 213 Indirect: !m.Pointer, 214 Variadic: m.Type.IsVariadic(), 215 OnePtr: onePtr, 216 } 217 pifn, _ := ctx.registerMethod(pinfo) 218 pms[i].name = mname 219 pms[i].mtyp = mtyp 220 pms[i].tfn = ptfn 221 pms[i].ifn = resolveReflectText(pifn) 222 223 if !m.Pointer { 224 info := &abi.MethodInfo{ 225 Name: m.Name, 226 Type: typ, 227 Func: mfn, 228 InTyp: inTyp, 229 OutTyp: outTyp, 230 InSize: isz, 231 OutSize: osz, 232 Variadic: m.Type.IsVariadic(), 233 OnePtr: onePtr, 234 } 235 ifn, _ := ctx.registerMethod(info) 236 ms[index].name = mname 237 ms[index].mtyp = mtyp 238 ms[index].tfn = tfn 239 ms[index].ifn = resolveReflectText(ifn) 240 index++ 241 } 242 } 243 rt.tflag |= tflagUserMethod 244 prt.tflag |= tflagUserMethod 245 246 if ctx.nAllocateError != 0 { 247 ncap := abi.Default.Cap() 248 err := &AllocError{ 249 Typ: typ, 250 Cap: ncap, 251 Req: ncap + ctx.nAllocateError, 252 } 253 if !DisableAllocateWarning { 254 log.Printf("warning, %v, import _ %q\n", err, "github.com/goplus/reflectx/icall/icall[N]") 255 } 256 return err 257 } 258 return nil 259 } 260 261 func newMethodSet(styp reflect.Type, maxmfunc, maxpfunc int) reflect.Type { 262 rt, _ := newType("", "", styp, maxmfunc, 0) 263 prt, _ := newType("", "", reflect.PtrTo(styp), maxpfunc, 0) 264 rt.ptrToThis = resolveReflectType(prt) 265 (*ptrType)(unsafe.Pointer(prt)).elem = rt 266 setTypeName(rt, styp.PkgPath(), styp.Name()) 267 prt.uncommon().pkgPath = resolveReflectName(newName(styp.PkgPath(), "", false)) 268 return toType(rt) 269 } 270 271 const ( 272 uintptrAligin = unsafe.Sizeof(uintptr(0)) 273 ) 274 275 func argsTypeSize(typ reflect.Type, offset bool) (off uintptr) { 276 numIn := typ.NumField() 277 if numIn == 0 { 278 return 0 279 } 280 for i := 0; i < numIn; i++ { 281 t := typ.Field(i).Type 282 targ := totype(t) 283 a := uintptr(targ.align) 284 off = (off + a - 1) &^ (a - 1) 285 n := targ.size 286 if n == 0 { 287 continue 288 } 289 off += n 290 } 291 if offset { 292 off = (off + uintptrAligin - 1) &^ (uintptrAligin - 1) 293 if off == 0 { 294 return uintptrAligin 295 } 296 } 297 return 298 }