go.ketch.com/lib/goja@v0.0.1/object_goarray_reflect.go (about)

     1  package goja
     2  
     3  import (
     4  	"reflect"
     5  	"strconv"
     6  
     7  	"go.ketch.com/lib/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.updateLen()
    64  	o.baseObject._put("length", &o.lengthProp)
    65  }
    66  
    67  func (o *objectGoArrayReflect) init() {
    68  	o._init()
    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  		ownProp = &o.lengthProp
   118  	} else {
   119  		ownProp = o.objectGoReflect.getOwnPropStr(name)
   120  	}
   121  	return o.getStrWithOwnProp(ownProp, name, receiver)
   122  }
   123  
   124  func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value {
   125  	if idx := strToGoIdx(name); idx >= 0 {
   126  		if idx < o.fieldsValue.Len() {
   127  			return &valueProperty{
   128  				value:      o._getIdx(idx),
   129  				writable:   true,
   130  				enumerable: true,
   131  			}
   132  		}
   133  		return nil
   134  	}
   135  	if name == "length" {
   136  		return &o.lengthProp
   137  	}
   138  	return o.objectGoReflect.getOwnPropStr(name)
   139  }
   140  
   141  func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value {
   142  	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
   143  		return &valueProperty{
   144  			value:      o._getIdx(idx),
   145  			writable:   true,
   146  			enumerable: true,
   147  		}
   148  	}
   149  	return nil
   150  }
   151  
   152  func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool {
   153  	cached := o.valueCache.get(idx)
   154  	if cached != nil {
   155  		copyReflectValueWrapper(cached)
   156  	}
   157  
   158  	rv := o.fieldsValue.Index(idx)
   159  	err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{})
   160  	if err != nil {
   161  		if cached != nil {
   162  			cached.setReflectValue(rv)
   163  		}
   164  		o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err)
   165  		return false
   166  	}
   167  	if cached != nil {
   168  		o.valueCache[idx] = nil
   169  	}
   170  	return true
   171  }
   172  
   173  func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool {
   174  	if i := toIntStrict(int64(idx)); i >= 0 {
   175  		if i >= o.fieldsValue.Len() {
   176  			if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok {
   177  				return res
   178  			}
   179  		}
   180  		return o.putIdx(i, val, throw)
   181  	} else {
   182  		name := idx.string()
   183  		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
   184  			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
   185  			return false
   186  		} else {
   187  			return res
   188  		}
   189  	}
   190  }
   191  
   192  func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
   193  	if idx := strToGoIdx(name); idx >= 0 {
   194  		if idx >= o.fieldsValue.Len() {
   195  			if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok {
   196  				return res
   197  			}
   198  		}
   199  		return o.putIdx(idx, val, throw)
   200  	} else {
   201  		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
   202  			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
   203  			return false
   204  		} else {
   205  			return res
   206  		}
   207  	}
   208  }
   209  
   210  func (o *objectGoArrayReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
   211  	return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw)
   212  }
   213  
   214  func (o *objectGoArrayReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
   215  	return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw)
   216  }
   217  
   218  func (o *objectGoArrayReflect) hasOwnPropertyIdx(idx valueInt) bool {
   219  	return o._hasIdx(idx)
   220  }
   221  
   222  func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool {
   223  	if o._hasStr(name) || name == "length" {
   224  		return true
   225  	}
   226  	return o.objectGoReflect._has(name.String())
   227  }
   228  
   229  func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
   230  	if i := toIntStrict(int64(idx)); i >= 0 {
   231  		if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) {
   232  			return false
   233  		}
   234  		val := descr.Value
   235  		if val == nil {
   236  			val = _undefined
   237  		}
   238  		return o.putIdx(i, val, throw)
   239  	}
   240  	o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx)
   241  	return false
   242  }
   243  
   244  func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   245  	if idx := strToGoIdx(name); idx >= 0 {
   246  		if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
   247  			return false
   248  		}
   249  		val := descr.Value
   250  		if val == nil {
   251  			val = _undefined
   252  		}
   253  		return o.putIdx(idx, val, throw)
   254  	}
   255  	o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name)
   256  	return false
   257  }
   258  
   259  func (o *objectGoArrayReflect) toPrimitive() Value {
   260  	return o.toPrimitiveString()
   261  }
   262  
   263  func (o *objectGoArrayReflect) _deleteIdx(idx int) {
   264  	if idx < o.fieldsValue.Len() {
   265  		if cv := o.valueCache.get(idx); cv != nil {
   266  			copyReflectValueWrapper(cv)
   267  			o.valueCache[idx] = nil
   268  		}
   269  
   270  		o.fieldsValue.Index(idx).Set(reflect.Zero(o.fieldsValue.Type().Elem()))
   271  	}
   272  }
   273  
   274  func (o *objectGoArrayReflect) deleteStr(name unistring.String, throw bool) bool {
   275  	if idx := strToGoIdx(name); idx >= 0 {
   276  		o._deleteIdx(idx)
   277  		return true
   278  	}
   279  
   280  	return o.objectGoReflect.deleteStr(name, throw)
   281  }
   282  
   283  func (o *objectGoArrayReflect) deleteIdx(i valueInt, throw bool) bool {
   284  	idx := toIntStrict(int64(i))
   285  	if idx >= 0 {
   286  		o._deleteIdx(idx)
   287  	}
   288  	return true
   289  }
   290  
   291  type goArrayReflectPropIter struct {
   292  	o          *objectGoArrayReflect
   293  	idx, limit int
   294  }
   295  
   296  func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) {
   297  	if i.idx < i.limit && i.idx < i.o.fieldsValue.Len() {
   298  		name := strconv.Itoa(i.idx)
   299  		i.idx++
   300  		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
   301  	}
   302  
   303  	return i.o.objectGoReflect.iterateStringKeys()()
   304  }
   305  
   306  func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value {
   307  	for i := 0; i < o.fieldsValue.Len(); i++ {
   308  		accum = append(accum, asciiString(strconv.Itoa(i)))
   309  	}
   310  
   311  	return o.objectGoReflect.stringKeys(all, accum)
   312  }
   313  
   314  func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc {
   315  	return (&goArrayReflectPropIter{
   316  		o:     o,
   317  		limit: o.fieldsValue.Len(),
   318  	}).next
   319  }
   320  
   321  func (o *objectGoArrayReflect) sortLen() int {
   322  	return o.fieldsValue.Len()
   323  }
   324  
   325  func (o *objectGoArrayReflect) sortGet(i int) Value {
   326  	return o.getIdx(valueInt(i), nil)
   327  }
   328  
   329  func (o *objectGoArrayReflect) swap(i int, j int) {
   330  	vi := o.fieldsValue.Index(i)
   331  	vj := o.fieldsValue.Index(j)
   332  	tmp := reflect.New(o.fieldsValue.Type().Elem()).Elem()
   333  	tmp.Set(vi)
   334  	vi.Set(vj)
   335  	vj.Set(tmp)
   336  
   337  	cachedI := o.valueCache.get(i)
   338  	cachedJ := o.valueCache.get(j)
   339  	if cachedI != nil {
   340  		cachedI.setReflectValue(vj)
   341  		o.valueCache.put(j, cachedI)
   342  	} else {
   343  		if j < len(o.valueCache) {
   344  			o.valueCache[j] = nil
   345  		}
   346  	}
   347  
   348  	if cachedJ != nil {
   349  		cachedJ.setReflectValue(vi)
   350  		o.valueCache.put(i, cachedJ)
   351  	} else {
   352  		if i < len(o.valueCache) {
   353  			o.valueCache[i] = nil
   354  		}
   355  	}
   356  }