ai_old/goja/array_sparse.go

501 lines
12 KiB
Go
Raw Normal View History

2024-09-20 16:50:35 +08:00
package goja
import (
"fmt"
"math"
"math/bits"
"reflect"
"sort"
"strconv"
"github.com/dop251/goja/unistring"
)
type sparseArrayItem struct {
idx uint32
value Value
}
type sparseArrayObject struct {
baseObject
items []sparseArrayItem
length uint32
propValueCount int
lengthProp valueProperty
}
func (a *sparseArrayObject) findIdx(idx uint32) int {
return sort.Search(len(a.items), func(i int) bool {
return a.items[i].idx >= idx
})
}
func (a *sparseArrayObject) _setLengthInt(l uint32, throw bool) bool {
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
for i := len(a.items) - 1; i >= 0; i-- {
item := a.items[i]
if item.idx <= l {
break
}
if prop, ok := item.value.(*valueProperty); ok {
if !prop.configurable {
l = item.idx + 1
ret = false
break
}
a.propValueCount--
}
}
}
}
idx := a.findIdx(l)
aa := a.items[idx:]
for i := range aa {
aa[i].value = nil
}
a.items = a.items[:idx]
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
func (a *sparseArrayObject) setLengthInt(l uint32, throw bool) bool {
if l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(l, throw)
}
func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(v, throw)
}
func (a *sparseArrayObject) _getIdx(idx uint32) Value {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
return a.items[i].value
}
return nil
}
func (a *sparseArrayObject) getStr(name unistring.String, receiver Value) Value {
return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
}
func (a *sparseArrayObject) getIdx(idx valueInt, receiver Value) Value {
prop := a.getOwnPropIdx(idx)
if prop == nil {
if a.prototype != nil {
if receiver == nil {
return a.prototype.self.getIdx(idx, a.val)
}
return a.prototype.self.getIdx(idx, receiver)
}
}
if prop, ok := prop.(*valueProperty); ok {
if receiver == nil {
return prop.get(a.val)
}
return prop.get(receiver)
}
return prop
}
func (a *sparseArrayObject) getLengthProp() *valueProperty {
a.lengthProp.value = intToValue(int64(a.length))
return &a.lengthProp
}
func (a *sparseArrayObject) getOwnPropStr(name unistring.String) Value {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._getIdx(idx)
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getOwnPropStr(name)
}
func (a *sparseArrayObject) getOwnPropIdx(idx valueInt) Value {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._getIdx(idx)
}
return a.baseObject.getOwnPropStr(idx.string())
}
func (a *sparseArrayObject) add(idx uint32, val Value) {
i := a.findIdx(idx)
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: val,
}
}
func (a *sparseArrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
var prop Value
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
prop = a.items[i].value
}
if prop == nil {
if proto := a.prototype; proto != nil {
// we know it's foreign because prototype loops are not allowed
if res, ok := proto.self.setForeignIdx(valueInt(idx), val, a.val, throw); ok {
return res
}
}
// new property
if !a.extensible {
a.val.runtime.typeErrorResult(throw, "Cannot add property %d, object is not extensible", idx)
return false
}
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if a.expand(idx) {
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: val,
}
} else {
ar := a.val.self.(*arrayObject)
ar.values[idx] = val
ar.objCount++
return true
}
} else {
if prop, ok := prop.(*valueProperty); ok {
if !prop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return false
}
prop.set(a.val, val)
} else {
a.items[i].value = val
}
}
return true
}
func (a *sparseArrayObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
} else {
if name == "length" {
return a.setLength(a.val.runtime.toLengthUint32(val), throw)
} else {
return a.baseObject.setOwnStr(name, val, throw)
}
}
}
func (a *sparseArrayObject) setOwnIdx(idx valueInt, val Value, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
}
return a.baseObject.setOwnStr(idx.string(), val, throw)
}
func (a *sparseArrayObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
return a._setForeignStr(name, a.getOwnPropStr(name), val, receiver, throw)
}
func (a *sparseArrayObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
return a._setForeignIdx(name, a.getOwnPropIdx(name), val, receiver, throw)
}
type sparseArrayPropIter struct {
a *sparseArrayObject
idx int
}
func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.a.items) {
name := asciiString(strconv.Itoa(int(i.a.items[i.idx].idx)))
prop := i.a.items[i.idx].value
i.idx++
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
return i.a.baseObject.iterateStringKeys()()
}
func (a *sparseArrayObject) iterateStringKeys() iterNextFunc {
return (&sparseArrayPropIter{
a: a,
}).next
}
func (a *sparseArrayObject) stringKeys(all bool, accum []Value) []Value {
if all {
for _, item := range a.items {
accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
}
} else {
for _, item := range a.items {
if prop, ok := item.value.(*valueProperty); ok && !prop.enumerable {
continue
}
accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
}
}
return a.baseObject.stringKeys(all, accum)
}
func (a *sparseArrayObject) setValues(values []Value, objCount int) {
a.items = make([]sparseArrayItem, 0, objCount)
for i, val := range values {
if val != nil {
a.items = append(a.items, sparseArrayItem{
idx: uint32(i),
value: val,
})
}
}
}
func (a *sparseArrayObject) hasOwnPropertyStr(name unistring.String) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
i := a.findIdx(idx)
return i < len(a.items) && a.items[i].idx == idx
} else {
return a.baseObject.hasOwnPropertyStr(name)
}
}
func (a *sparseArrayObject) hasOwnPropertyIdx(idx valueInt) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
i := a.findIdx(idx)
return i < len(a.items) && a.items[i].idx == idx
}
return a.baseObject.hasOwnPropertyStr(idx.string())
}
func (a *sparseArrayObject) hasPropertyIdx(idx valueInt) bool {
if a.hasOwnPropertyIdx(idx) {
return true
}
if a.prototype != nil {
return a.prototype.self.hasPropertyIdx(idx)
}
return false
}
func (a *sparseArrayObject) expand(idx uint32) bool {
if l := len(a.items); l >= 1024 {
if ii := a.items[l-1].idx; ii > idx {
idx = ii
}
if (bits.UintSize == 64 || idx < math.MaxInt32) && int(idx)>>3 < l {
//log.Println("Switching sparse->standard")
ar := &arrayObject{
baseObject: a.baseObject,
length: a.length,
propValueCount: a.propValueCount,
}
ar.setValuesFromSparse(a.items, int(idx))
ar.val.self = ar
ar.lengthProp.writable = a.lengthProp.writable
a._put("length", &ar.lengthProp)
return false
}
}
return true
}
func (a *sparseArrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, throw bool) bool {
var existing Value
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
existing = a.items[i].value
}
prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
if ok {
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if i >= len(a.items) || a.items[i].idx != idx {
if a.expand(idx) {
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: prop,
}
if idx >= a.length {
a.length = idx + 1
}
} else {
a.val.self.(*arrayObject).values[idx] = prop
}
} else {
a.items[i].value = prop
}
if _, ok := prop.(*valueProperty); ok {
a.propValueCount++
}
}
return ok
}
func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw)
}
return a.baseObject.defineOwnPropertyStr(name, descr, throw)
}
func (a *sparseArrayObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
return a.baseObject.defineOwnPropertyStr(idx.string(), descr, throw)
}
func (a *sparseArrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
if p, ok := a.items[i].value.(*valueProperty); ok {
if !p.configurable {
a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.toString())
return false
}
a.propValueCount--
}
copy(a.items[i:], a.items[i+1:])
a.items[len(a.items)-1].value = nil
a.items = a.items[:len(a.items)-1]
}
return true
}
func (a *sparseArrayObject) deleteStr(name unistring.String, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
}
func (a *sparseArrayObject) deleteIdx(idx valueInt, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(idx.string(), throw)
}
func (a *sparseArrayObject) sortLen() int {
if len(a.items) > 0 {
return toIntStrict(int64(a.items[len(a.items)-1].idx) + 1)
}
return 0
}
func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} {
if v, exists := ctx.get(a.val); exists {
return v
}
arr := make([]interface{}, a.length)
ctx.put(a.val, arr)
var prevIdx uint32
for _, item := range a.items {
idx := item.idx
for i := prevIdx; i < idx; i++ {
if a.prototype != nil {
if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
arr[i] = exportValue(v, ctx)
}
}
}
v := item.value
if v != nil {
if prop, ok := v.(*valueProperty); ok {
v = prop.get(a.val)
}
arr[idx] = exportValue(v, ctx)
}
prevIdx = idx + 1
}
for i := prevIdx; i < a.length; i++ {
if a.prototype != nil {
if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
arr[i] = exportValue(v, ctx)
}
}
}
return arr
}
func (a *sparseArrayObject) exportType() reflect.Type {
return reflectTypeArray
}
func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
r := a.val.runtime
if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil {
l := toIntStrict(int64(a.length))
if typ.Kind() == reflect.Array {
if dst.Len() != l {
return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len())
}
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
ctx.putTyped(a.val, typ, dst.Interface())
for _, item := range a.items {
val := item.value
if p, ok := val.(*valueProperty); ok {
val = p.get(a.val)
}
idx := toIntStrict(int64(item.idx))
if idx >= l {
break
}
err := r.toReflectValue(val, dst.Index(idx), ctx)
if err != nil {
return fmt.Errorf("could not convert array element %v to %v at %d: %w", item.value, typ, idx, err)
}
}
return nil
}
return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
}