github.com/goplusjs/reflectx@v0.5.4/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/goplusjs/gopherjs/js"
    14  )
    15  
    16  func New(typ reflect.Type) reflect.Value {
    17  	return reflect.New(typ)
    18  }
    19  
    20  func Interface(v reflect.Value) interface{} {
    21  	return v.Interface()
    22  }
    23  
    24  func isMethod(typ reflect.Type) bool {
    25  	return typMethodMap[typ]
    26  }
    27  
    28  func MethodByIndex(typ reflect.Type, index int) reflect.Method {
    29  	m := MethodX(typ, index)
    30  	if isMethod(typ) {
    31  		m.Func = reflect.MakeFunc(m.Type, func(args []reflect.Value) []reflect.Value {
    32  			recv := args[0].MethodByName(m.Name)
    33  			if m.Type.IsVariadic() {
    34  				return recv.CallSlice(args[1:])
    35  			} else {
    36  				return recv.Call(args[1:])
    37  			}
    38  		})
    39  	}
    40  	return m
    41  }
    42  
    43  func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) {
    44  	m, ok = MethodByNameX(typ, name)
    45  	if !ok {
    46  		return
    47  	}
    48  	if isMethod(typ) {
    49  		m.Func = reflect.MakeFunc(m.Type, func(args []reflect.Value) []reflect.Value {
    50  			recv := args[0].MethodByName(name)
    51  			if m.Type.IsVariadic() {
    52  				return recv.CallSlice(args[1:])
    53  			} else {
    54  				return recv.Call(args[1:])
    55  			}
    56  		})
    57  	}
    58  	return
    59  }
    60  
    61  var (
    62  	typMethodMap = make(map[reflect.Type]bool)
    63  )
    64  
    65  func resetTypeList() {
    66  	typMethodMap = make(map[reflect.Type]bool)
    67  }
    68  
    69  func newMethodSet(styp reflect.Type, maxmfunc, maxpfunc int) reflect.Type {
    70  	rt, _ := newType(styp.PkgPath(), styp.Name(), styp, maxmfunc, 0)
    71  	setTypeName(rt, styp.PkgPath(), styp.Name())
    72  	typ := toType(rt)
    73  	jstyp := jsType(rt)
    74  	jstyp.Set("methodSetCache", nil)
    75  	ptyp := reflect.PtrTo(typ)
    76  	prt := totype(ptyp)
    77  	resetUncommonType(prt, maxpfunc, 0)
    78  	pjstyp := jsType(prt)
    79  	pjstyp.Set("methodSetCache", nil)
    80  	if nt, ok := ntypeMap[styp]; ok {
    81  		ntypeMap[typ] = &Named{Name: nt.Name, PkgPath: nt.PkgPath, Type: typ, From: nt.From, Kind: nt.Kind}
    82  	}
    83  	return typ
    84  }
    85  
    86  func resizeMethod(typ reflect.Type, count int) error {
    87  	rt := totype(typ)
    88  	ut := toUncommonType(rt)
    89  	if ut == nil {
    90  		return fmt.Errorf("not found uncommonType of %v", typ)
    91  	}
    92  	if uint16(count) > ut.mcount {
    93  		return fmt.Errorf("too many methods of %v", typ)
    94  	}
    95  	ut.xcount = uint16(count)
    96  	return nil
    97  }
    98  
    99  func setMethodSet(typ reflect.Type, methods []Method) error {
   100  	sort.Slice(methods, func(i, j int) bool {
   101  		n := strings.Compare(methods[i].Name, methods[j].Name)
   102  		if n == 0 && methods[i].Type == methods[j].Type {
   103  			panic(fmt.Sprintf("method redeclared: %v", methods[j].Name))
   104  		}
   105  		return n < 0
   106  	})
   107  	isPointer := func(m Method) bool {
   108  		return m.Pointer
   109  	}
   110  	var mcount, pcount int
   111  	pcount = len(methods)
   112  	for _, m := range methods {
   113  		if !isPointer(m) {
   114  			mcount++
   115  		}
   116  	}
   117  
   118  	ptyp := reflect.PtrTo(typ)
   119  	if err := resizeMethod(typ, mcount); err != nil {
   120  		return err
   121  	}
   122  	if err := resizeMethod(ptyp, pcount); err != nil {
   123  		return err
   124  	}
   125  	rt := totype(typ)
   126  	prt := totype(ptyp)
   127  
   128  	ums := toUncommonType(rt)._methods
   129  
   130  	jstyp := jsType(rt)
   131  	jstyp.Set("methodSetCache", nil)
   132  	jsms := jstyp.Get("methods")
   133  	jsproto := jstyp.Get("prototype")
   134  	jsmscache := js.Global.Get("Array").New()
   135  
   136  	pums := toUncommonType(prt)._methods
   137  	pjstyp := jsType(prt)
   138  	pjstyp.Set("methodSetCache", nil)
   139  	pjsms := pjstyp.Get("methods")
   140  	pjsproto := pjstyp.Get("prototype")
   141  	pjsmscache := js.Global.Get("Array").New()
   142  
   143  	index := -1
   144  	pindex := -1
   145  	for i, m := range methods {
   146  		in, out, ntyp, _, _ := parserMethodType(m.Type, nil)
   147  		var ftyp reflect.Type
   148  		if m.Pointer {
   149  			ftyp = reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, m.Type.IsVariadic())
   150  			pindex++
   151  		} else {
   152  			ftyp = reflect.FuncOf(append([]reflect.Type{typ}, in...), out, m.Type.IsVariadic())
   153  			index++
   154  		}
   155  		fn := js.Global.Get("Object").New()
   156  		fn.Set("pkg", "")
   157  		fn.Set("name", js.InternalObject(m.Name))
   158  		fn.Set("prop", js.InternalObject(m.Name))
   159  		fn.Set("typ", jsType(totype(ntyp)))
   160  		if m.Pointer {
   161  			pjsms.SetIndex(pindex, fn)
   162  		} else {
   163  			jsms.SetIndex(index, fn)
   164  			jsmscache.SetIndex(index, fn)
   165  		}
   166  		pjsmscache.SetIndex(i, fn)
   167  
   168  		mname := resolveReflectName(newName(m.Name, "", true))
   169  		mtyp := resolveReflectType(totype(ntyp))
   170  		pums[i].name = mname
   171  		pums[i].mtyp = mtyp
   172  		if !m.Pointer {
   173  			ums[index].name = mname
   174  			ums[index].mtyp = mtyp
   175  		}
   176  		dfn := reflect.MakeFunc(ftyp, m.Func)
   177  		tfn := tovalue(&dfn)
   178  		nargs := ftyp.NumIn()
   179  		if m.Pointer {
   180  			pjsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   181  				iargs := make([]interface{}, nargs, nargs)
   182  				iargs[0] = this
   183  				for i, arg := range args {
   184  					iargs[i+1] = arg
   185  				}
   186  				return js.InternalObject(tfn.ptr).Invoke(iargs...)
   187  			}))
   188  		} else {
   189  			pjsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   190  				iargs := make([]interface{}, nargs, nargs)
   191  				iargs[0] = *(**js.Object)(unsafe.Pointer(this))
   192  				for i, arg := range args {
   193  					iargs[i+1] = arg
   194  				}
   195  				return js.InternalObject(tfn.ptr).Invoke(iargs...)
   196  			}))
   197  		}
   198  		jsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   199  			iargs := make([]interface{}, nargs, nargs)
   200  			iargs[0] = this.Get("$val")
   201  			for i, arg := range args {
   202  				iargs[i+1] = arg
   203  			}
   204  			return js.InternalObject(tfn.ptr).Invoke(iargs...)
   205  		}))
   206  	}
   207  	jstyp.Set("methodSetCache", jsmscache)
   208  	pjstyp.Set("methodSetCache", pjsmscache)
   209  
   210  	typMethodMap[typ] = true
   211  	return nil
   212  }
   213  
   214  // func methodOf(styp reflect.Type, methods []Method) reflect.Type {
   215  // 	sort.Slice(methods, func(i, j int) bool {
   216  // 		n := strings.Compare(methods[i].Name, methods[j].Name)
   217  // 		if n == 0 && methods[i].Type == methods[j].Type {
   218  // 			panic(fmt.Sprintf("method redeclared: %v", methods[j].Name))
   219  // 		}
   220  // 		return n < 0
   221  // 	})
   222  // 	isPointer := func(m Method) bool {
   223  // 		return m.Pointer
   224  // 	}
   225  // 	var mcount, pcount int
   226  // 	pcount = len(methods)
   227  // 	for _, m := range methods {
   228  // 		if !isPointer(m) {
   229  // 			mcount++
   230  // 		}
   231  // 	}
   232  // 	orgtyp := styp
   233  // 	rt, ums := newType(styp.PkgPath(), styp.Name(), styp, mcount, mcount)
   234  // 	setTypeName(rt, styp.PkgPath(), styp.Name())
   235  
   236  // 	typ := toType(rt)
   237  // 	jstyp := jsType(rt)
   238  // 	jstyp.Set("methodSetCache", nil)
   239  // 	jsms := jstyp.Get("methods")
   240  // 	jsproto := jstyp.Get("prototype")
   241  // 	jsmscache := js.Global.Get("Array").New()
   242  
   243  // 	ptyp := reflect.PtrTo(typ)
   244  // 	prt := totype(ptyp)
   245  // 	pums := resetUncommonType(prt, pcount, pcount)._methods
   246  // 	pjstyp := jsType(prt)
   247  // 	pjstyp.Set("methodSetCache", nil)
   248  // 	pjsms := pjstyp.Get("methods")
   249  // 	pjsproto := pjstyp.Get("prototype")
   250  // 	pjsmscache := js.Global.Get("Array").New()
   251  
   252  // 	index := -1
   253  // 	pindex := -1
   254  // 	for i, m := range methods {
   255  // 		in, out, ntyp, _, _ := toRealType(typ, orgtyp, m.Type)
   256  // 		var ftyp reflect.Type
   257  // 		if m.Pointer {
   258  // 			ftyp = reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, m.Type.IsVariadic())
   259  // 			pindex++
   260  // 		} else {
   261  // 			ftyp = reflect.FuncOf(append([]reflect.Type{typ}, in...), out, m.Type.IsVariadic())
   262  // 			index++
   263  // 		}
   264  // 		fn := js.Global.Get("Object").New()
   265  // 		fn.Set("pkg", "")
   266  // 		fn.Set("name", js.InternalObject(m.Name))
   267  // 		fn.Set("prop", js.InternalObject(m.Name))
   268  // 		fn.Set("typ", jsType(totype(ntyp)))
   269  // 		if m.Pointer {
   270  // 			pjsms.SetIndex(pindex, fn)
   271  // 		} else {
   272  // 			jsms.SetIndex(index, fn)
   273  // 			jsmscache.SetIndex(index, fn)
   274  // 		}
   275  // 		pjsmscache.SetIndex(i, fn)
   276  
   277  // 		mname := resolveReflectName(newName(m.Name, "", true))
   278  // 		mtyp := resolveReflectType(totype(ntyp))
   279  // 		pums[i].name = mname
   280  // 		pums[i].mtyp = mtyp
   281  // 		if !m.Pointer {
   282  // 			ums[index].name = mname
   283  // 			ums[index].mtyp = mtyp
   284  // 		}
   285  // 		dfn := reflect.MakeFunc(ftyp, m.Func)
   286  // 		tfn := tovalue(&dfn)
   287  // 		nargs := ftyp.NumIn()
   288  // 		if m.Pointer {
   289  // 			pjsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   290  // 				iargs := make([]interface{}, nargs, nargs)
   291  // 				iargs[0] = this
   292  // 				for i, arg := range args {
   293  // 					iargs[i+1] = arg
   294  // 				}
   295  // 				return js.InternalObject(tfn.ptr).Invoke(iargs...)
   296  // 			}))
   297  // 		} else {
   298  // 			pjsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   299  // 				iargs := make([]interface{}, nargs, nargs)
   300  // 				iargs[0] = *(**js.Object)(unsafe.Pointer(this))
   301  // 				for i, arg := range args {
   302  // 					iargs[i+1] = arg
   303  // 				}
   304  // 				return js.InternalObject(tfn.ptr).Invoke(iargs...)
   305  // 			}))
   306  // 		}
   307  // 		jsproto.Set(m.Name, js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
   308  // 			iargs := make([]interface{}, nargs, nargs)
   309  // 			iargs[0] = this.Get("$val")
   310  // 			for i, arg := range args {
   311  // 				iargs[i+1] = arg
   312  // 			}
   313  // 			return js.InternalObject(tfn.ptr).Invoke(iargs...)
   314  // 		}))
   315  // 	}
   316  // 	jstyp.Set("methodSetCache", jsmscache)
   317  // 	pjstyp.Set("methodSetCache", pjsmscache)
   318  
   319  // 	typMethodMap[typ] = true
   320  // 	return typ
   321  // }