github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goarray_reflect.go (about)

     1  package goja
     2  
     3  import (
     4  	"reflect"
     5  	"strconv"
     6  
     7  	"github.com/nuvolaris/goja/unistring"
     8  )
     9  
    10  type objectGoArrayReflect struct {
    11  	objectGoReflect
    12  	lengthProp valueProperty
    13  
    14  	valueCache valueArrayCache
    15  
    16  	putIdx func(idx int, v Value, throw bool) bool
    17  }
    18  
    19  type valueArrayCache []reflectValueWrapper
    20  
    21  func (c *valueArrayCache) get(idx int) reflectValueWrapper {
    22  	if idx < len(*c) {
    23  		return (*c)[idx]
    24  	}
    25  	return nil
    26  }
    27  
    28  func (c *valueArrayCache) grow(newlen int) {
    29  	oldcap := cap(*c)
    30  	if oldcap < newlen {
    31  		a := make([]reflectValueWrapper, newlen, growCap(newlen, len(*c), oldcap))
    32  		copy(a, *c)
    33  		*c = a
    34  	} else {
    35  		*c = (*c)[:newlen]
    36  	}
    37  }
    38  
    39  func (c *valueArrayCache) put(idx int, w reflectValueWrapper) {
    40  	if len(*c) <= idx {
    41  		c.grow(idx + 1)
    42  	}
    43  	(*c)[idx] = w
    44  }
    45  
    46  func (c *valueArrayCache) shrink(newlen int) {
    47  	if len(*c) > newlen {
    48  		tail := (*c)[newlen:]
    49  		for i, item := range tail {
    50  			if item != nil {
    51  				copyReflectValueWrapper(item)
    52  				tail[i] = nil
    53  			}
    54  		}
    55  		*c = (*c)[:newlen]
    56  	}
    57  }
    58  
    59  func (o *objectGoArrayReflect) _init() {
    60  	o.objectGoReflect.init()
    61  	o.class = classArray
    62  	o.prototype = o.val.runtime.global.ArrayPrototype
    63  	o.baseObject._put("length", &o.lengthProp)
    64  }
    65  
    66  func (o *objectGoArrayReflect) init() {
    67  	o._init()
    68  	o.updateLen()
    69  	o.putIdx = o._putIdx
    70  }
    71  
    72  func (o *objectGoArrayReflect) updateLen() {
    73  	o.lengthProp.value = intToValue(int64(o.fieldsValue.Len()))
    74  }
    75  
    76  func (o *objectGoArrayReflect) _hasIdx(idx valueInt) bool {
    77  	if idx := int64(idx); idx >= 0 && idx < int64(o.fieldsValue.Len()) {
    78  		return true
    79  	}
    80  	return false
    81  }
    82  
    83  func (o *objectGoArrayReflect) _hasStr(name unistring.String) bool {
    84  	if idx := strToIdx64(name); idx >= 0 && idx < int64(o.fieldsValue.Len()) {
    85  		return true
    86  	}
    87  	return false
    88  }
    89  
    90  func (o *objectGoArrayReflect) _getIdx(idx int) Value {
    91  	if v := o.valueCache.get(idx); v != nil {
    92  		return v.esValue()
    93  	}
    94  
    95  	v := o.fieldsValue.Index(idx)
    96  
    97  	res, w := o.elemToValue(v)
    98  	if w != nil {
    99  		o.valueCache.put(idx, w)
   100  	}
   101  
   102  	return res
   103  }
   104  
   105  func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value {
   106  	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
   107  		return o._getIdx(idx)
   108  	}
   109  	return o.objectGoReflect.getStr(idx.string(), receiver)
   110  }
   111  
   112  func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Value {
   113  	var ownProp Value
   114  	if idx := strToGoIdx(name); idx >= 0 && idx < o.fieldsValue.Len() {
   115  		ownProp = o._getIdx(idx)
   116  	} else if name == "length" {
   117  		if o.fieldsValue.Kind() == reflect.Slice {
   118  			o.updateLen()
   119  		}
   120  		ownProp = &o.lengthProp
   121  	} else {
   122  		ownProp = o.objectGoReflect.getOwnPropStr(name)
   123  	}
   124  	return o.getStrWithOwnProp(ownProp, name, receiver)
   125  }
   126  
   127  func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value {
   128  	if idx := strToGoIdx(name); idx >= 0 {
   129  		if idx < o.fieldsValue.Len() {
   130  			return &valueProperty{
   131  				value:      o._getIdx(idx),
   132  				writable:   true,
   133  				enumerable: true,
   134  			}
   135  		}
   136  		return nil
   137  	}
   138  	if name == "length" {
   139  		if o.fieldsValue.Kind() == reflect.Slice {
   140  			o.updateLen()
   141  		}
   142  		return &o.lengthProp
   143  	}
   144  	return o.objectGoReflect.getOwnPropStr(name)
   145  }
   146  
   147  func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value {
   148  	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
   149  		return &valueProperty{
   150  			value:      o._getIdx(idx),
   151  			writable:   true,
   152  			enumerable: true,
   153  		}
   154  	}
   155  	return nil
   156  }
   157  
   158  func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool {
   159  	cached := o.valueCache.get(idx)
   160  	if cached != nil {
   161  		copyReflectValueWrapper(cached)
   162  	}
   163  
   164  	rv := o.fieldsValue.Index(idx)
   165  	err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{})
   166  	if err != nil {
   167  		if cached != nil {
   168  			cached.setReflectValue(rv)
   169  		}
   170  		o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err)
   171  		return false
   172  	}
   173  	if cached != nil {
   174  		o.valueCache[idx] = nil
   175  	}
   176  	return true
   177  }
   178  
   179  func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool {
   180  	if i := toIntStrict(int64(idx)); i >= 0 {
   181  		if i >= o.fieldsValue.Len() {
   182  			if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok {
   183  				return res
   184  			}
   185  		}
   186  		return o.putIdx(i, val, throw)
   187  	} else {
   188  		name := idx.string()
   189  		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
   190  			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
   191  			return false
   192  		} else {
   193  			return res
   194  		}
   195  	}
   196  }
   197  
   198  func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
   199  	if idx := strToGoIdx(name); idx >= 0 {
   200  		if idx >= o.fieldsValue.Len() {
   201  			if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok {
   202  				return res
   203  			}
   204  		}
   205  		return o.putIdx(idx, val, throw)
   206  	} else {
   207  		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
   208  			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
   209  			return false
   210  		} else {
   211  			return res
   212  		}
   213  	}
   214  }
   215  
   216  func (o *objectGoArrayReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
   217  	return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw)
   218  }
   219  
   220  func (o *objectGoArrayReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
   221  	return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw)
   222  }
   223  
   224  func (o *objectGoArrayReflect) hasOwnPropertyIdx(idx valueInt) bool {
   225  	return o._hasIdx(idx)
   226  }
   227  
   228  func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool {
   229  	if o._hasStr(name) || name == "length" {
   230  		return true
   231  	}
   232  	return o.objectGoReflect.hasOwnPropertyStr(name)
   233  }
   234  
   235  func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
   236  	if i := toIntStrict(int64(idx)); i >= 0 {
   237  		if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) {
   238  			return false
   239  		}
   240  		val := descr.Value
   241  		if val == nil {
   242  			val = _undefined
   243  		}
   244  		return o.putIdx(i, val, throw)
   245  	}
   246  	o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx)
   247  	return false
   248  }
   249  
   250  func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   251  	if idx := strToGoIdx(name); idx >= 0 {
   252  		if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
   253  			return false
   254  		}
   255  		val := descr.Value
   256  		if val == nil {
   257  			val = _undefined
   258  		}
   259  		return o.putIdx(idx, val, throw)
   260  	}
   261  	o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name)
   262  	return false
   263  }
   264  
   265  func (o *objectGoArrayReflect) _deleteIdx(idx int) {
   266  	if idx < o.fieldsValue.Len() {
   267  		if cv := o.valueCache.get(idx); cv != nil {
   268  			copyReflectValueWrapper(cv)
   269  			o.valueCache[idx] = nil
   270  		}
   271  
   272  		o.fieldsValue.Index(idx).Set(reflect.Zero(o.fieldsValue.Type().Elem()))
   273  	}
   274  }
   275  
   276  func (o *objectGoArrayReflect) deleteStr(name unistring.String, throw bool) bool {
   277  	if idx := strToGoIdx(name); idx >= 0 {
   278  		o._deleteIdx(idx)
   279  		return true
   280  	}
   281  
   282  	return o.objectGoReflect.deleteStr(name, throw)
   283  }
   284  
   285  func (o *objectGoArrayReflect) deleteIdx(i valueInt, throw bool) bool {
   286  	idx := toIntStrict(int64(i))
   287  	if idx >= 0 {
   288  		o._deleteIdx(idx)
   289  	}
   290  	return true
   291  }
   292  
   293  type goArrayReflectPropIter struct {
   294  	o          *objectGoArrayReflect
   295  	idx, limit int
   296  }
   297  
   298  func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) {
   299  	if i.idx < i.limit && i.idx < i.o.fieldsValue.Len() {
   300  		name := strconv.Itoa(i.idx)
   301  		i.idx++
   302  		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
   303  	}
   304  
   305  	return i.o.objectGoReflect.iterateStringKeys()()
   306  }
   307  
   308  func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value {
   309  	for i := 0; i < o.fieldsValue.Len(); i++ {
   310  		accum = append(accum, asciiString(strconv.Itoa(i)))
   311  	}
   312  
   313  	return o.objectGoReflect.stringKeys(all, accum)
   314  }
   315  
   316  func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc {
   317  	return (&goArrayReflectPropIter{
   318  		o:     o,
   319  		limit: o.fieldsValue.Len(),
   320  	}).next
   321  }
   322  
   323  func (o *objectGoArrayReflect) sortLen() int {
   324  	return o.fieldsValue.Len()
   325  }
   326  
   327  func (o *objectGoArrayReflect) sortGet(i int) Value {
   328  	return o.getIdx(valueInt(i), nil)
   329  }
   330  
   331  func (o *objectGoArrayReflect) swap(i int, j int) {
   332  	vi := o.fieldsValue.Index(i)
   333  	vj := o.fieldsValue.Index(j)
   334  	tmp := reflect.New(o.fieldsValue.Type().Elem()).Elem()
   335  	tmp.Set(vi)
   336  	vi.Set(vj)
   337  	vj.Set(tmp)
   338  
   339  	cachedI := o.valueCache.get(i)
   340  	cachedJ := o.valueCache.get(j)
   341  	if cachedI != nil {
   342  		cachedI.setReflectValue(vj)
   343  		o.valueCache.put(j, cachedI)
   344  	} else {
   345  		if j < len(o.valueCache) {
   346  			o.valueCache[j] = nil
   347  		}
   348  	}
   349  
   350  	if cachedJ != nil {
   351  		cachedJ.setReflectValue(vi)
   352  		o.valueCache.put(i, cachedJ)
   353  	} else {
   354  		if i < len(o.valueCache) {
   355  			o.valueCache[i] = nil
   356  		}
   357  	}
   358  }