github.com/goplus/reflectx@v1.2.2/methodof_js.go (about)

     1  //go:build js && !wasm
     2  // +build js,!wasm
     3  
     4  package reflectx
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"sort"
    10  	"strings"
    11  	"unsafe"
    12  
    13  	"github.com/gopherjs/gopherjs/js"
    14  )
    15  
    16  // icall stat
    17  func IcallStat() (capacity int, allocate int, aviable int) {
    18  	return 0, 0, 0
    19  }
    20  
    21  func (ctx *Context) Reset() {
    22  	ctx.nAllocateError = 0
    23  	ctx.embedLookupCache = make(map[reflect.Type]reflect.Type)
    24  	ctx.structLookupCache = make(map[string][]reflect.Type)
    25  	ctx.interfceLookupCache = make(map[string]reflect.Type)
    26  }
    27  
    28  func resetAll() {
    29  	typMethodMap = make(map[reflect.Type]bool)
    30  }
    31  
    32  func (ctx *Context) IcallAlloc() int {
    33  	return 0
    34  }
    35  
    36  func isMethod(typ reflect.Type) bool {
    37  	return typMethodMap[typ]
    38  }
    39  
    40  type MethodProvider interface {
    41  	Remove(index []int) // remove method info
    42  	Clear()             // clear all methods
    43  }
    44  
    45  func MethodByIndex(typ reflect.Type, index int) reflect.Method {
    46  	m := MethodX(typ, index)
    47  	if isMethod(typ) {
    48  		m.Func = reflect.MakeFunc(m.Type, func(args []reflect.Value) []reflect.Value {
    49  			recv := args[0].MethodByName(m.Name)
    50  			if m.Type.IsVariadic() {
    51  				return recv.CallSlice(args[1:])
    52  			} else {
    53  				return recv.Call(args[1:])
    54  			}
    55  		})
    56  	}
    57  	return m
    58  }
    59  
    60  func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) {
    61  	m, ok = MethodByNameX(typ, name)
    62  	if !ok {
    63  		return
    64  	}
    65  	if isMethod(typ) {
    66  		m.Func = reflect.MakeFunc(m.Type, func(args []reflect.Value) []reflect.Value {
    67  			recv := args[0].MethodByName(name)
    68  			if m.Type.IsVariadic() {
    69  				return recv.CallSlice(args[1:])
    70  			} else {
    71  				return recv.Call(args[1:])
    72  			}
    73  		})
    74  	}
    75  	return
    76  }
    77  
    78  var (
    79  	typMethodMap = make(map[reflect.Type]bool)
    80  )
    81  
    82  func newMethodSet(styp reflect.Type, maxmfunc, maxpfunc int) reflect.Type {
    83  	rt, _ := newType(styp.PkgPath(), styp.Name(), styp, maxmfunc, 0)
    84  	setTypeName(rt, styp.PkgPath(), styp.Name())
    85  	typ := toType(rt)
    86  	jstyp := jsType(rt)
    87  	jstyp.Set("methodSetCache", nil)
    88  	ptyp := reflect.PtrTo(typ)
    89  	prt := totype(ptyp)
    90  	resetUncommonType(prt, maxpfunc, 0)
    91  	pjstyp := jsType(prt)
    92  	pjstyp.Set("methodSetCache", nil)
    93  	return typ
    94  }
    95  
    96  func resizeMethod(typ reflect.Type, mcount int, xcount int) error {
    97  	rt := totype(typ)
    98  	ut := toUncommonType(rt)
    99  	if ut == nil {
   100  		return fmt.Errorf("not found uncommonType of %v", typ)
   101  	}
   102  	if uint16(mcount) > ut.mcount {
   103  		return fmt.Errorf("too many methods of %v", typ)
   104  	}
   105  	ut.xcount = uint16(xcount)
   106  	return nil
   107  }
   108  
   109  func (ctx *Context) setMethodSet(typ reflect.Type, methods []Method) error {
   110  	sort.Slice(methods, func(i, j int) bool {
   111  		n := strings.Compare(methods[i].Name, methods[j].Name)
   112  		if n == 0 && methods[i].Type == methods[j].Type {
   113  			panic(fmt.Sprintf("method redeclared: %v", methods[j].Name))
   114  		}
   115  		return n < 0
   116  	})
   117  
   118  	var mcount, pcount int
   119  	var xcount, pxcount int
   120  	pcount = len(methods)
   121  	for _, m := range methods {
   122  		isexport := methodIsExported(m.Name)
   123  		if isexport {
   124  			pxcount++
   125  		}
   126  		if !m.Pointer {
   127  			if isexport {
   128  				xcount++
   129  			}
   130  			mcount++
   131  		}
   132  	}
   133  
   134  	ptyp := reflect.PtrTo(typ)
   135  	if err := resizeMethod(typ, mcount, xcount); err != nil {
   136  		return err
   137  	}
   138  	if err := resizeMethod(ptyp, pcount, pxcount); err != nil {
   139  		return err
   140  	}
   141  	rt := totype(typ)
   142  	prt := totype(ptyp)
   143  
   144  	ums := toUncommonType(rt)._methods
   145  
   146  	jstyp := jsType(rt)
   147  	jstyp.Set("methodSetCache", nil)
   148  	jsms := jstyp.Get("methods")
   149  	jsproto := jstyp.Get("prototype")
   150  	jsmscache := js.Global.Get("Array").New()
   151  
   152  	pums := toUncommonType(prt)._methods
   153  	pjstyp := jsType(prt)
   154  	pjstyp.Set("methodSetCache", nil)
   155  	pjsms := pjstyp.Get("methods")
   156  	pjsproto := pjstyp.Get("prototype")
   157  	pjsmscache := js.Global.Get("Array").New()
   158  
   159  	index := -1
   160  	pindex := -1
   161  	for i, m := range methods {
   162  		in, out, ntyp, _, _ := parserMethodType(m.Type, nil)
   163  		var ftyp reflect.Type
   164  		if m.Pointer {
   165  			ftyp = reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, m.Type.IsVariadic())
   166  			pindex++
   167  		} else {
   168  			ftyp = reflect.FuncOf(append([]reflect.Type{typ}, in...), out, m.Type.IsVariadic())
   169  			index++
   170  		}
   171  		fn := js.Global.Get("Object").New()
   172  		fn.Set("pkg", "")
   173  		fn.Set("name", js.InternalObject(m.Name))
   174  		fn.Set("prop", js.InternalObject(m.Name))
   175  		fn.Set("typ", jsType(totype(ntyp)))
   176  		if m.Pointer {
   177  			pjsms.SetIndex(pindex, fn)
   178  		} else {
   179  			jsms.SetIndex(index, fn)
   180  			jsmscache.SetIndex(index, fn)
   181  		}
   182  		pjsmscache.SetIndex(i, fn)
   183  
   184  		isexport := methodIsExported(m.Name)
   185  		nm := newNameEx(m.Name, "", isexport, !isexport)
   186  		if !isexport {
   187  			fn.Set("pkg", m.PkgPath)
   188  			nm.setPkgPath(m.PkgPath)
   189  		}
   190  		mname := resolveReflectName(nm)
   191  		mtyp := resolveReflectType(totype(ntyp))
   192  		pums[i].name = mname
   193  		pums[i].mtyp = mtyp
   194  		if !m.Pointer {
   195  			ums[index].name = mname
   196  			ums[index].mtyp = mtyp
   197  		}
   198  		dfn := reflect.MakeFunc(ftyp, m.Func)
   199  		tfn := tovalue(&dfn)
   200  		nargs := ftyp.NumIn()
   201  		if m.Pointer {
   202  			pjsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   203  				iargs := make([]interface{}, nargs, nargs)
   204  				iargs[0] = this
   205  				for i, arg := range args {
   206  					iargs[i+1] = arg
   207  				}
   208  				return js.InternalObject(tfn.ptr).Invoke(iargs...)
   209  			}))
   210  		} else {
   211  			pjsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   212  				iargs := make([]interface{}, nargs, nargs)
   213  				iargs[0] = *(**js.Object)(unsafe.Pointer(this))
   214  				for i, arg := range args {
   215  					iargs[i+1] = arg
   216  				}
   217  				return js.InternalObject(tfn.ptr).Invoke(iargs...)
   218  			}))
   219  		}
   220  		jsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   221  			iargs := make([]interface{}, nargs, nargs)
   222  			iargs[0] = this.Get("$val")
   223  			for i, arg := range args {
   224  				iargs[i+1] = arg
   225  			}
   226  			return js.InternalObject(tfn.ptr).Invoke(iargs...)
   227  		}))
   228  	}
   229  	jstyp.Set("methodSetCache", jsmscache)
   230  	pjstyp.Set("methodSetCache", pjsmscache)
   231  
   232  	typMethodMap[typ] = true
   233  	return nil
   234  }