123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- package goja
- import (
- "math"
- "reflect"
- "sort"
- "strconv"
- )
- type sparseArrayItem struct {
- idx int64
- value Value
- }
- type sparseArrayObject struct {
- baseObject
- items []sparseArrayItem
- length int64
- propValueCount int
- lengthProp valueProperty
- }
- func (a *sparseArrayObject) init() {
- a.baseObject.init()
- a.lengthProp.writable = true
- a._put("length", &a.lengthProp)
- }
- func (a *sparseArrayObject) getLength() Value {
- return intToValue(a.length)
- }
- func (a *sparseArrayObject) findIdx(idx int64) int {
- return sort.Search(len(a.items), func(i int) bool {
- return a.items[i].idx >= idx
- })
- }
- func (a *sparseArrayObject) _setLengthInt(l int64, throw bool) bool {
- if l >= 0 && l <= math.MaxUint32 {
- 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
- }
- panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
- }
- func (a *sparseArrayObject) setLengthInt(l int64, 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 Value, throw bool) bool {
- l, ok := toIntIgnoreNegZero(v)
- if ok && l == a.length {
- return true
- }
- if !a.lengthProp.writable {
- a.val.runtime.typeErrorResult(throw, "length is not writable")
- return false
- }
- if ok {
- return a._setLengthInt(l, throw)
- }
- panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
- }
- func (a *sparseArrayObject) getIdx(idx int64, origNameStr string, origName Value) (v Value) {
- i := a.findIdx(idx)
- if i < len(a.items) && a.items[i].idx == idx {
- return a.items[i].value
- }
- if a.prototype != nil {
- if origName != nil {
- v = a.prototype.self.getProp(origName)
- } else {
- v = a.prototype.self.getPropStr(origNameStr)
- }
- }
- return
- }
- func (a *sparseArrayObject) getProp(n Value) Value {
- if idx := toIdx(n); idx >= 0 {
- return a.getIdx(idx, "", n)
- }
- if n.String() == "length" {
- return a.getLengthProp()
- }
- return a.baseObject.getProp(n)
- }
- func (a *sparseArrayObject) getLengthProp() Value {
- a.lengthProp.value = intToValue(a.length)
- return &a.lengthProp
- }
- func (a *sparseArrayObject) getOwnProp(name string) Value {
- if idx := strToIdx(name); idx >= 0 {
- i := a.findIdx(idx)
- if i < len(a.items) && a.items[i].idx == idx {
- return a.items[i].value
- }
- return nil
- }
- if name == "length" {
- return a.getLengthProp()
- }
- return a.baseObject.getOwnProp(name)
- }
- func (a *sparseArrayObject) getPropStr(name string) Value {
- if i := strToIdx(name); i >= 0 {
- return a.getIdx(i, name, nil)
- }
- if name == "length" {
- return a.getLengthProp()
- }
- return a.baseObject.getPropStr(name)
- }
- func (a *sparseArrayObject) putIdx(idx int64, val Value, throw bool, origNameStr string, origName Value) {
- 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 a.prototype != nil {
- var pprop Value
- if origName != nil {
- pprop = a.prototype.self.getProp(origName)
- } else {
- pprop = a.prototype.self.getPropStr(origNameStr)
- }
- if pprop, ok := pprop.(*valueProperty); ok {
- if !pprop.isWritable() {
- a.val.runtime.typeErrorResult(throw)
- return
- }
- if pprop.accessor {
- pprop.set(a.val, val)
- return
- }
- }
- }
- if !a.extensible {
- a.val.runtime.typeErrorResult(throw)
- return
- }
- if idx >= a.length {
- if !a.setLengthInt(idx+1, throw) {
- return
- }
- }
- if a.expand() {
- a.items = append(a.items, sparseArrayItem{})
- copy(a.items[i+1:], a.items[i:])
- a.items[i] = sparseArrayItem{
- idx: idx,
- value: val,
- }
- } else {
- a.val.self.(*arrayObject).putIdx(idx, val, throw, origNameStr, origName)
- return
- }
- } else {
- if prop, ok := prop.(*valueProperty); ok {
- if !prop.isWritable() {
- a.val.runtime.typeErrorResult(throw)
- return
- }
- prop.set(a.val, val)
- return
- } else {
- a.items[i].value = val
- }
- }
- }
- func (a *sparseArrayObject) put(n Value, val Value, throw bool) {
- if idx := toIdx(n); idx >= 0 {
- a.putIdx(idx, val, throw, "", n)
- } else {
- if n.String() == "length" {
- a.setLength(val, throw)
- } else {
- a.baseObject.put(n, val, throw)
- }
- }
- }
- func (a *sparseArrayObject) putStr(name string, val Value, throw bool) {
- if idx := strToIdx(name); idx >= 0 {
- a.putIdx(idx, val, throw, name, nil)
- } else {
- if name == "length" {
- a.setLength(val, throw)
- } else {
- a.baseObject.putStr(name, val, throw)
- }
- }
- }
- type sparseArrayPropIter struct {
- a *sparseArrayObject
- recursive bool
- idx int
- }
- func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
- for i.idx < len(i.a.items) {
- name := 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._enumerate(i.recursive)()
- }
- func (a *sparseArrayObject) _enumerate(recursive bool) iterNextFunc {
- return (&sparseArrayPropIter{
- a: a,
- recursive: recursive,
- }).next
- }
- func (a *sparseArrayObject) enumerate(all, recursive bool) iterNextFunc {
- return (&propFilterIter{
- wrapped: a._enumerate(recursive),
- all: all,
- seen: make(map[string]bool),
- }).next
- }
- func (a *sparseArrayObject) setValues(values []Value) {
- a.items = nil
- for i, val := range values {
- if val != nil {
- a.items = append(a.items, sparseArrayItem{
- idx: int64(i),
- value: val,
- })
- }
- }
- }
- func (a *sparseArrayObject) hasOwnProperty(n Value) bool {
- if idx := toIdx(n); idx >= 0 {
- i := a.findIdx(idx)
- if i < len(a.items) && a.items[i].idx == idx {
- return a.items[i].value != _undefined
- }
- return false
- } else {
- return a.baseObject.hasOwnProperty(n)
- }
- }
- func (a *sparseArrayObject) hasOwnPropertyStr(name string) bool {
- if idx := strToIdx(name); idx >= 0 {
- i := a.findIdx(idx)
- if i < len(a.items) && a.items[i].idx == idx {
- return a.items[i].value != _undefined
- }
- return false
- } else {
- return a.baseObject.hasOwnPropertyStr(name)
- }
- }
- func (a *sparseArrayObject) expand() bool {
- if l := len(a.items); l >= 1024 {
- if int(a.items[l-1].idx)/l < 8 {
- //log.Println("Switching sparse->standard")
- ar := &arrayObject{
- baseObject: a.baseObject,
- length: a.length,
- propValueCount: a.propValueCount,
- }
- ar.setValuesFromSparse(a.items)
- ar.val.self = ar
- ar.init()
- ar.lengthProp.writable = a.lengthProp.writable
- return false
- }
- }
- return true
- }
- func (a *sparseArrayObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
- if idx := toIdx(n); idx >= 0 {
- 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(n, existing, descr, 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() {
- 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 {
- return a.val.self.defineOwnProperty(n, descr, throw)
- }
- } else {
- a.items[i].value = prop
- }
- if _, ok := prop.(*valueProperty); ok {
- a.propValueCount++
- }
- }
- return ok
- } else {
- if n.String() == "length" {
- return a.val.runtime.defineArrayLength(&a.lengthProp, descr, a.setLength, throw)
- }
- return a.baseObject.defineOwnProperty(n, descr, throw)
- }
- }
- func (a *sparseArrayObject) _deleteProp(idx int64, 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) delete(n Value, throw bool) bool {
- if idx := toIdx(n); idx >= 0 {
- return a._deleteProp(idx, throw)
- }
- return a.baseObject.delete(n, throw)
- }
- func (a *sparseArrayObject) deleteStr(name string, throw bool) bool {
- if idx := strToIdx(name); idx >= 0 {
- return a._deleteProp(idx, throw)
- }
- return a.baseObject.deleteStr(name, throw)
- }
- func (a *sparseArrayObject) sortLen() int64 {
- if len(a.items) > 0 {
- return a.items[len(a.items)-1].idx + 1
- }
- return 0
- }
- func (a *sparseArrayObject) sortGet(i int64) Value {
- idx := a.findIdx(i)
- if idx < len(a.items) && a.items[idx].idx == i {
- v := a.items[idx].value
- if p, ok := v.(*valueProperty); ok {
- v = p.get(a.val)
- }
- return v
- }
- return nil
- }
- func (a *sparseArrayObject) swap(i, j int64) {
- idxI := a.findIdx(i)
- idxJ := a.findIdx(j)
- if idxI < len(a.items) && a.items[idxI].idx == i && idxJ < len(a.items) && a.items[idxJ].idx == j {
- a.items[idxI].value, a.items[idxJ].value = a.items[idxJ].value, a.items[idxI].value
- }
- }
- func (a *sparseArrayObject) export() interface{} {
- arr := make([]interface{}, a.length)
- for _, item := range a.items {
- if item.value != nil {
- arr[item.idx] = item.value.Export()
- }
- }
- return arr
- }
- func (a *sparseArrayObject) exportType() reflect.Type {
- return reflectTypeArray
- }
|