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

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