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

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