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  }