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

     1  package goja
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"math/bits"
     7  	"reflect"
     8  	"sort"
     9  	"strconv"
    10  
    11  	"github.com/nuvolaris/goja/unistring"
    12  )
    13  
    14  type sparseArrayItem struct {
    15  	idx   uint32
    16  	value Value
    17  }
    18  
    19  type sparseArrayObject struct {
    20  	baseObject
    21  	items          []sparseArrayItem
    22  	length         uint32
    23  	propValueCount int
    24  	lengthProp     valueProperty
    25  }
    26  
    27  func (a *sparseArrayObject) findIdx(idx uint32) int {
    28  	return sort.Search(len(a.items), func(i int) bool {
    29  		return a.items[i].idx >= idx
    30  	})
    31  }
    32  
    33  func (a *sparseArrayObject) _setLengthInt(l uint32, throw bool) bool {
    34  	ret := true
    35  	if l <= a.length {
    36  		if a.propValueCount > 0 {
    37  			// Slow path
    38  			for i := len(a.items) - 1; i >= 0; i-- {
    39  				item := a.items[i]
    40  				if item.idx <= l {
    41  					break
    42  				}
    43  				if prop, ok := item.value.(*valueProperty); ok {
    44  					if !prop.configurable {
    45  						l = item.idx + 1
    46  						ret = false
    47  						break
    48  					}
    49  					a.propValueCount--
    50  				}
    51  			}
    52  		}
    53  	}
    54  
    55  	idx := a.findIdx(l)
    56  
    57  	aa := a.items[idx:]
    58  	for i := range aa {
    59  		aa[i].value = nil
    60  	}
    61  	a.items = a.items[:idx]
    62  	a.length = l
    63  	if !ret {
    64  		a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
    65  	}
    66  	return ret
    67  }
    68  
    69  func (a *sparseArrayObject) setLengthInt(l uint32, throw bool) bool {
    70  	if l == a.length {
    71  		return true
    72  	}
    73  	if !a.lengthProp.writable {
    74  		a.val.runtime.typeErrorResult(throw, "length is not writable")
    75  		return false
    76  	}
    77  	return a._setLengthInt(l, throw)
    78  }
    79  
    80  func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
    81  	if !a.lengthProp.writable {
    82  		a.val.runtime.typeErrorResult(throw, "length is not writable")
    83  		return false
    84  	}
    85  	return a._setLengthInt(v, throw)
    86  }
    87  
    88  func (a *sparseArrayObject) _getIdx(idx uint32) Value {
    89  	i := a.findIdx(idx)
    90  	if i < len(a.items) && a.items[i].idx == idx {
    91  		return a.items[i].value
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  func (a *sparseArrayObject) getStr(name unistring.String, receiver Value) Value {
    98  	return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
    99  }
   100  
   101  func (a *sparseArrayObject) getIdx(idx valueInt, receiver Value) Value {
   102  	prop := a.getOwnPropIdx(idx)
   103  	if prop == nil {
   104  		if a.prototype != nil {
   105  			if receiver == nil {
   106  				return a.prototype.self.getIdx(idx, a.val)
   107  			}
   108  			return a.prototype.self.getIdx(idx, receiver)
   109  		}
   110  	}
   111  	if prop, ok := prop.(*valueProperty); ok {
   112  		if receiver == nil {
   113  			return prop.get(a.val)
   114  		}
   115  		return prop.get(receiver)
   116  	}
   117  	return prop
   118  }
   119  
   120  func (a *sparseArrayObject) getLengthProp() *valueProperty {
   121  	a.lengthProp.value = intToValue(int64(a.length))
   122  	return &a.lengthProp
   123  }
   124  
   125  func (a *sparseArrayObject) getOwnPropStr(name unistring.String) Value {
   126  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   127  		return a._getIdx(idx)
   128  	}
   129  	if name == "length" {
   130  		return a.getLengthProp()
   131  	}
   132  	return a.baseObject.getOwnPropStr(name)
   133  }
   134  
   135  func (a *sparseArrayObject) getOwnPropIdx(idx valueInt) Value {
   136  	if idx := toIdx(idx); idx != math.MaxUint32 {
   137  		return a._getIdx(idx)
   138  	}
   139  	return a.baseObject.getOwnPropStr(idx.string())
   140  }
   141  
   142  func (a *sparseArrayObject) add(idx uint32, val Value) {
   143  	i := a.findIdx(idx)
   144  	a.items = append(a.items, sparseArrayItem{})
   145  	copy(a.items[i+1:], a.items[i:])
   146  	a.items[i] = sparseArrayItem{
   147  		idx:   idx,
   148  		value: val,
   149  	}
   150  }
   151  
   152  func (a *sparseArrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
   153  	var prop Value
   154  	i := a.findIdx(idx)
   155  	if i < len(a.items) && a.items[i].idx == idx {
   156  		prop = a.items[i].value
   157  	}
   158  
   159  	if prop == nil {
   160  		if proto := a.prototype; proto != nil {
   161  			// we know it's foreign because prototype loops are not allowed
   162  			if res, ok := proto.self.setForeignIdx(valueInt(idx), val, a.val, throw); ok {
   163  				return res
   164  			}
   165  		}
   166  
   167  		// new property
   168  		if !a.extensible {
   169  			a.val.runtime.typeErrorResult(throw, "Cannot add property %d, object is not extensible", idx)
   170  			return false
   171  		}
   172  
   173  		if idx >= a.length {
   174  			if !a.setLengthInt(idx+1, throw) {
   175  				return false
   176  			}
   177  		}
   178  
   179  		if a.expand(idx) {
   180  			a.items = append(a.items, sparseArrayItem{})
   181  			copy(a.items[i+1:], a.items[i:])
   182  			a.items[i] = sparseArrayItem{
   183  				idx:   idx,
   184  				value: val,
   185  			}
   186  		} else {
   187  			ar := a.val.self.(*arrayObject)
   188  			ar.values[idx] = val
   189  			ar.objCount++
   190  			return true
   191  		}
   192  	} else {
   193  		if prop, ok := prop.(*valueProperty); ok {
   194  			if !prop.isWritable() {
   195  				a.val.runtime.typeErrorResult(throw)
   196  				return false
   197  			}
   198  			prop.set(a.val, val)
   199  		} else {
   200  			a.items[i].value = val
   201  		}
   202  	}
   203  	return true
   204  }
   205  
   206  func (a *sparseArrayObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
   207  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   208  		return a._setOwnIdx(idx, val, throw)
   209  	} else {
   210  		if name == "length" {
   211  			return a.setLength(a.val.runtime.toLengthUint32(val), throw)
   212  		} else {
   213  			return a.baseObject.setOwnStr(name, val, throw)
   214  		}
   215  	}
   216  }
   217  
   218  func (a *sparseArrayObject) setOwnIdx(idx valueInt, val Value, throw bool) bool {
   219  	if idx := toIdx(idx); idx != math.MaxUint32 {
   220  		return a._setOwnIdx(idx, val, throw)
   221  	}
   222  
   223  	return a.baseObject.setOwnStr(idx.string(), val, throw)
   224  }
   225  
   226  func (a *sparseArrayObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
   227  	return a._setForeignStr(name, a.getOwnPropStr(name), val, receiver, throw)
   228  }
   229  
   230  func (a *sparseArrayObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
   231  	return a._setForeignIdx(name, a.getOwnPropIdx(name), val, receiver, throw)
   232  }
   233  
   234  type sparseArrayPropIter struct {
   235  	a   *sparseArrayObject
   236  	idx int
   237  }
   238  
   239  func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
   240  	for i.idx < len(i.a.items) {
   241  		name := asciiString(strconv.Itoa(int(i.a.items[i.idx].idx)))
   242  		prop := i.a.items[i.idx].value
   243  		i.idx++
   244  		if prop != nil {
   245  			return propIterItem{name: name, value: prop}, i.next
   246  		}
   247  	}
   248  
   249  	return i.a.baseObject.iterateStringKeys()()
   250  }
   251  
   252  func (a *sparseArrayObject) iterateStringKeys() iterNextFunc {
   253  	return (&sparseArrayPropIter{
   254  		a: a,
   255  	}).next
   256  }
   257  
   258  func (a *sparseArrayObject) stringKeys(all bool, accum []Value) []Value {
   259  	if all {
   260  		for _, item := range a.items {
   261  			accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
   262  		}
   263  	} else {
   264  		for _, item := range a.items {
   265  			if prop, ok := item.value.(*valueProperty); ok && !prop.enumerable {
   266  				continue
   267  			}
   268  			accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
   269  		}
   270  	}
   271  
   272  	return a.baseObject.stringKeys(all, accum)
   273  }
   274  
   275  func (a *sparseArrayObject) setValues(values []Value, objCount int) {
   276  	a.items = make([]sparseArrayItem, 0, objCount)
   277  	for i, val := range values {
   278  		if val != nil {
   279  			a.items = append(a.items, sparseArrayItem{
   280  				idx:   uint32(i),
   281  				value: val,
   282  			})
   283  		}
   284  	}
   285  }
   286  
   287  func (a *sparseArrayObject) hasOwnPropertyStr(name unistring.String) bool {
   288  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   289  		i := a.findIdx(idx)
   290  		return i < len(a.items) && a.items[i].idx == idx
   291  	} else {
   292  		return a.baseObject.hasOwnPropertyStr(name)
   293  	}
   294  }
   295  
   296  func (a *sparseArrayObject) hasOwnPropertyIdx(idx valueInt) bool {
   297  	if idx := toIdx(idx); idx != math.MaxUint32 {
   298  		i := a.findIdx(idx)
   299  		return i < len(a.items) && a.items[i].idx == idx
   300  	}
   301  
   302  	return a.baseObject.hasOwnPropertyStr(idx.string())
   303  }
   304  
   305  func (a *sparseArrayObject) expand(idx uint32) bool {
   306  	if l := len(a.items); l >= 1024 {
   307  		if ii := a.items[l-1].idx; ii > idx {
   308  			idx = ii
   309  		}
   310  		if (bits.UintSize == 64 || idx < math.MaxInt32) && int(idx)>>3 < l {
   311  			//log.Println("Switching sparse->standard")
   312  			ar := &arrayObject{
   313  				baseObject:     a.baseObject,
   314  				length:         a.length,
   315  				propValueCount: a.propValueCount,
   316  			}
   317  			ar.setValuesFromSparse(a.items, int(idx))
   318  			ar.val.self = ar
   319  			ar.lengthProp.writable = a.lengthProp.writable
   320  			a._put("length", &ar.lengthProp)
   321  			return false
   322  		}
   323  	}
   324  	return true
   325  }
   326  
   327  func (a *sparseArrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, throw bool) bool {
   328  	var existing Value
   329  	i := a.findIdx(idx)
   330  	if i < len(a.items) && a.items[i].idx == idx {
   331  		existing = a.items[i].value
   332  	}
   333  	prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
   334  	if ok {
   335  		if idx >= a.length {
   336  			if !a.setLengthInt(idx+1, throw) {
   337  				return false
   338  			}
   339  		}
   340  		if i >= len(a.items) || a.items[i].idx != idx {
   341  			if a.expand(idx) {
   342  				a.items = append(a.items, sparseArrayItem{})
   343  				copy(a.items[i+1:], a.items[i:])
   344  				a.items[i] = sparseArrayItem{
   345  					idx:   idx,
   346  					value: prop,
   347  				}
   348  				if idx >= a.length {
   349  					a.length = idx + 1
   350  				}
   351  			} else {
   352  				a.val.self.(*arrayObject).values[idx] = prop
   353  			}
   354  		} else {
   355  			a.items[i].value = prop
   356  		}
   357  		if _, ok := prop.(*valueProperty); ok {
   358  			a.propValueCount++
   359  		}
   360  	}
   361  	return ok
   362  }
   363  
   364  func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   365  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   366  		return a._defineIdxProperty(idx, descr, throw)
   367  	}
   368  	if name == "length" {
   369  		return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw)
   370  	}
   371  	return a.baseObject.defineOwnPropertyStr(name, descr, throw)
   372  }
   373  
   374  func (a *sparseArrayObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
   375  	if idx := toIdx(idx); idx != math.MaxUint32 {
   376  		return a._defineIdxProperty(idx, descr, throw)
   377  	}
   378  	return a.baseObject.defineOwnPropertyStr(idx.string(), descr, throw)
   379  }
   380  
   381  func (a *sparseArrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
   382  	i := a.findIdx(idx)
   383  	if i < len(a.items) && a.items[i].idx == idx {
   384  		if p, ok := a.items[i].value.(*valueProperty); ok {
   385  			if !p.configurable {
   386  				a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.toString())
   387  				return false
   388  			}
   389  			a.propValueCount--
   390  		}
   391  		copy(a.items[i:], a.items[i+1:])
   392  		a.items[len(a.items)-1].value = nil
   393  		a.items = a.items[:len(a.items)-1]
   394  	}
   395  	return true
   396  }
   397  
   398  func (a *sparseArrayObject) deleteStr(name unistring.String, throw bool) bool {
   399  	if idx := strToArrayIdx(name); idx != math.MaxUint32 {
   400  		return a._deleteIdxProp(idx, throw)
   401  	}
   402  	return a.baseObject.deleteStr(name, throw)
   403  }
   404  
   405  func (a *sparseArrayObject) deleteIdx(idx valueInt, throw bool) bool {
   406  	if idx := toIdx(idx); idx != math.MaxUint32 {
   407  		return a._deleteIdxProp(idx, throw)
   408  	}
   409  	return a.baseObject.deleteStr(idx.string(), throw)
   410  }
   411  
   412  func (a *sparseArrayObject) sortLen() int {
   413  	if len(a.items) > 0 {
   414  		return toIntStrict(int64(a.items[len(a.items)-1].idx) + 1)
   415  	}
   416  
   417  	return 0
   418  }
   419  
   420  func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} {
   421  	if v, exists := ctx.get(a.val); exists {
   422  		return v
   423  	}
   424  	arr := make([]interface{}, a.length)
   425  	ctx.put(a.val, arr)
   426  	var prevIdx uint32
   427  	for _, item := range a.items {
   428  		idx := item.idx
   429  		for i := prevIdx; i < idx; i++ {
   430  			if a.prototype != nil {
   431  				if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
   432  					arr[i] = exportValue(v, ctx)
   433  				}
   434  			}
   435  		}
   436  		v := item.value
   437  		if v != nil {
   438  			if prop, ok := v.(*valueProperty); ok {
   439  				v = prop.get(a.val)
   440  			}
   441  			arr[idx] = exportValue(v, ctx)
   442  		}
   443  		prevIdx = idx + 1
   444  	}
   445  	for i := prevIdx; i < a.length; i++ {
   446  		if a.prototype != nil {
   447  			if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
   448  				arr[i] = exportValue(v, ctx)
   449  			}
   450  		}
   451  	}
   452  	return arr
   453  }
   454  
   455  func (a *sparseArrayObject) exportType() reflect.Type {
   456  	return reflectTypeArray
   457  }
   458  
   459  func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
   460  	r := a.val.runtime
   461  	if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil {
   462  		l := toIntStrict(int64(a.length))
   463  		if typ.Kind() == reflect.Array {
   464  			if dst.Len() != l {
   465  				return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len())
   466  			}
   467  		} else {
   468  			dst.Set(reflect.MakeSlice(typ, l, l))
   469  		}
   470  		ctx.putTyped(a.val, typ, dst.Interface())
   471  		for _, item := range a.items {
   472  			val := item.value
   473  			if p, ok := val.(*valueProperty); ok {
   474  				val = p.get(a.val)
   475  			}
   476  			idx := toIntStrict(int64(item.idx))
   477  			if idx >= l {
   478  				break
   479  			}
   480  			err := r.toReflectValue(val, dst.Index(idx), ctx)
   481  			if err != nil {
   482  				return fmt.Errorf("could not convert array element %v to %v at %d: %w", item.value, typ, idx, err)
   483  			}
   484  		}
   485  		return nil
   486  	}
   487  	return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
   488  }