123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- package goja
- import (
- "math"
- "math/bits"
- "reflect"
- "strconv"
- )
- type arrayIterObject struct {
- baseObject
- obj *Object
- nextIdx int64
- kind iterationKind
- }
- func (ai *arrayIterObject) next() Value {
- if ai.obj == nil {
- return ai.val.runtime.createIterResultObject(_undefined, true)
- }
- l := toLength(ai.obj.self.getStr("length", nil))
- index := ai.nextIdx
- if index >= l {
- ai.obj = nil
- return ai.val.runtime.createIterResultObject(_undefined, true)
- }
- ai.nextIdx++
- idxVal := valueInt(index)
- if ai.kind == iterationKindKey {
- return ai.val.runtime.createIterResultObject(idxVal, false)
- }
- elementValue := ai.obj.self.getIdx(idxVal, nil)
- var result Value
- if ai.kind == iterationKindValue {
- result = elementValue
- } else {
- result = ai.val.runtime.newArrayValues([]Value{idxVal, elementValue})
- }
- return ai.val.runtime.createIterResultObject(result, false)
- }
- func (r *Runtime) createArrayIterator(iterObj *Object, kind iterationKind) Value {
- o := &Object{runtime: r}
- ai := &arrayIterObject{
- obj: iterObj,
- kind: kind,
- }
- ai.class = classArrayIterator
- ai.val = o
- ai.extensible = true
- o.self = ai
- ai.prototype = r.global.ArrayIteratorPrototype
- ai.init()
- return o
- }
- type arrayObject struct {
- baseObject
- values []Value
- length uint32
- objCount int
- propValueCount int
- lengthProp valueProperty
- }
- func (a *arrayObject) init() {
- a.baseObject.init()
- a.lengthProp.writable = true
- a._put("length", &a.lengthProp)
- }
- func (a *arrayObject) _setLengthInt(l int64, throw bool) bool {
- if l >= 0 && l <= math.MaxUint32 {
- l := uint32(l)
- ret := true
- if l <= a.length {
- if a.propValueCount > 0 {
- // Slow path
- for i := len(a.values) - 1; i >= int(l); i-- {
- if prop, ok := a.values[i].(*valueProperty); ok {
- if !prop.configurable {
- l = uint32(i) + 1
- ret = false
- break
- }
- a.propValueCount--
- }
- }
- }
- }
- if l <= uint32(len(a.values)) {
- if l >= 16 && l < uint32(cap(a.values))>>2 {
- ar := make([]Value, l)
- copy(ar, a.values)
- a.values = ar
- } else {
- ar := a.values[l:len(a.values)]
- for i := range ar {
- ar[i] = nil
- }
- a.values = a.values[:l]
- }
- }
- 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 *arrayObject) setLengthInt(l int64, throw bool) bool {
- if l == int64(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 *arrayObject) setLength(v Value, throw bool) bool {
- l, ok := toIntIgnoreNegZero(v)
- if ok && l == int64(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 *arrayObject) 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 *arrayObject) getOwnPropStr(name string) Value {
- if i := strToIdx(name); i != math.MaxUint32 {
- if i < uint32(len(a.values)) {
- return a.values[i]
- }
- }
- if name == "length" {
- return a.getLengthProp()
- }
- return a.baseObject.getOwnPropStr(name)
- }
- func (a *arrayObject) getOwnPropIdx(idx valueInt) Value {
- if i := toIdx(idx); i != math.MaxUint32 {
- if i < uint32(len(a.values)) {
- return a.values[i]
- }
- return nil
- }
- return a.baseObject.getOwnPropStr(idx.String())
- }
- func (a *arrayObject) sortLen() int64 {
- return int64(len(a.values))
- }
- func (a *arrayObject) sortGet(i int64) Value {
- v := a.values[i]
- if p, ok := v.(*valueProperty); ok {
- v = p.get(a.val)
- }
- return v
- }
- func (a *arrayObject) swap(i, j int64) {
- a.values[i], a.values[j] = a.values[j], a.values[i]
- }
- func (a *arrayObject) getStr(name string, receiver Value) Value {
- return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
- }
- func (a *arrayObject) getLengthProp() Value {
- a.lengthProp.value = intToValue(int64(a.length))
- return &a.lengthProp
- }
- func (a *arrayObject) setOwnIdx(idx valueInt, val Value, throw bool) bool {
- if i := toIdx(idx); i != math.MaxUint32 {
- return a._setOwnIdx(i, val, throw)
- } else {
- return a.baseObject.setOwnStr(idx.String(), val, throw)
- }
- }
- func (a *arrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
- var prop Value
- if idx < uint32(len(a.values)) {
- prop = a.values[idx]
- }
- 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
- } else {
- if idx >= a.length {
- if !a.setLengthInt(int64(idx)+1, throw) {
- return false
- }
- }
- if idx >= uint32(len(a.values)) {
- if !a.expand(idx) {
- a.val.self.(*sparseArrayObject).add(idx, val)
- return true
- }
- }
- a.objCount++
- }
- } else {
- if prop, ok := prop.(*valueProperty); ok {
- if !prop.isWritable() {
- a.val.runtime.typeErrorResult(throw)
- return false
- }
- prop.set(a.val, val)
- return true
- }
- }
- a.values[idx] = val
- return true
- }
- func (a *arrayObject) setOwnStr(name string, val Value, throw bool) bool {
- if idx := strToIdx(name); idx != math.MaxUint32 {
- return a._setOwnIdx(idx, val, throw)
- } else {
- if name == "length" {
- return a.setLength(val, throw)
- } else {
- return a.baseObject.setOwnStr(name, val, throw)
- }
- }
- }
- func (a *arrayObject) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
- return a._setForeignIdx(idx, a.getOwnPropIdx(idx), val, receiver, throw)
- }
- func (a *arrayObject) setForeignStr(name string, val, receiver Value, throw bool) (bool, bool) {
- return a._setForeignStr(name, a.getOwnPropStr(name), val, receiver, throw)
- }
- type arrayPropIter struct {
- a *arrayObject
- idx int
- }
- func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
- for i.idx < len(i.a.values) {
- name := strconv.Itoa(i.idx)
- prop := i.a.values[i.idx]
- i.idx++
- if prop != nil {
- return propIterItem{name: name, value: prop}, i.next
- }
- }
- return i.a.baseObject.enumerateUnfiltered()()
- }
- func (a *arrayObject) enumerateUnfiltered() iterNextFunc {
- return (&arrayPropIter{
- a: a,
- }).next
- }
- func (a *arrayObject) ownKeys(all bool, accum []Value) []Value {
- for i, prop := range a.values {
- name := strconv.Itoa(i)
- if prop != nil {
- if !all {
- if prop, ok := prop.(*valueProperty); ok && !prop.enumerable {
- continue
- }
- }
- accum = append(accum, asciiString(name))
- }
- }
- return a.baseObject.ownKeys(all, accum)
- }
- func (a *arrayObject) hasOwnPropertyStr(name string) bool {
- if idx := strToIdx(name); idx != math.MaxUint32 {
- return idx < uint32(len(a.values)) && a.values[idx] != nil
- } else {
- return a.baseObject.hasOwnPropertyStr(name)
- }
- }
- func (a *arrayObject) hasOwnPropertyIdx(idx valueInt) bool {
- if idx := toIdx(idx); idx != math.MaxUint32 {
- return idx < uint32(len(a.values)) && a.values[idx] != nil
- }
- return a.baseObject.hasOwnPropertyStr(idx.String())
- }
- func (a *arrayObject) expand(idx uint32) bool {
- targetLen := idx + 1
- if targetLen > uint32(len(a.values)) {
- if targetLen < uint32(cap(a.values)) {
- a.values = a.values[:targetLen]
- } else {
- if idx > 4096 && (a.objCount == 0 || idx/uint32(a.objCount) > 10) {
- //log.Println("Switching standard->sparse")
- sa := &sparseArrayObject{
- baseObject: a.baseObject,
- length: uint32(a.length),
- propValueCount: a.propValueCount,
- }
- sa.setValues(a.values, a.objCount+1)
- sa.val.self = sa
- sa.init()
- sa.lengthProp.writable = a.lengthProp.writable
- return false
- } else {
- if bits.UintSize == 32 {
- if targetLen >= math.MaxInt32 {
- panic(a.val.runtime.NewTypeError("Array index overflows int"))
- }
- }
- tl := int(targetLen)
- // Use the same algorithm as in runtime.growSlice
- newcap := cap(a.values)
- doublecap := newcap + newcap
- if tl > doublecap {
- newcap = tl
- } else {
- if len(a.values) < 1024 {
- newcap = doublecap
- } else {
- for newcap < tl {
- newcap += newcap / 4
- }
- }
- }
- newValues := make([]Value, tl, newcap)
- copy(newValues, a.values)
- a.values = newValues
- }
- }
- }
- return true
- }
- func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(Value, bool) bool, throw bool) bool {
- ret := true
- if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
- ret = false
- goto Reject
- }
- if newLen := descr.Value; newLen != nil {
- ret = setter(newLen, false)
- } else {
- ret = true
- }
- if descr.Writable != FLAG_NOT_SET {
- w := descr.Writable.Bool()
- if prop.writable {
- prop.writable = w
- } else {
- if w {
- ret = false
- goto Reject
- }
- }
- }
- Reject:
- if !ret {
- r.typeErrorResult(throw, "Cannot redefine property: length")
- }
- return ret
- }
- func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, throw bool) bool {
- var existing Value
- if idx < uint32(len(a.values)) {
- existing = a.values[idx]
- }
- prop, ok := a.baseObject._defineOwnProperty(strconv.FormatUint(uint64(idx), 10), existing, desc, throw)
- if ok {
- if idx >= uint32(a.length) {
- if !a.setLengthInt(int64(idx)+1, throw) {
- return false
- }
- }
- if a.expand(idx) {
- a.values[idx] = prop
- a.objCount++
- if _, ok := prop.(*valueProperty); ok {
- a.propValueCount++
- }
- } else {
- a.val.self.(*sparseArrayObject).add(uint32(idx), prop)
- }
- }
- return ok
- }
- func (a *arrayObject) defineOwnPropertyStr(name string, descr PropertyDescriptor, throw bool) bool {
- if idx := strToIdx(name); idx != math.MaxUint32 {
- return a._defineIdxProperty(idx, descr, throw)
- }
- if name == "length" {
- return a.val.runtime.defineArrayLength(&a.lengthProp, descr, a.setLength, throw)
- }
- return a.baseObject.defineOwnPropertyStr(name, descr, throw)
- }
- func (a *arrayObject) 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 *arrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
- if idx < uint32(len(a.values)) {
- if v := a.values[idx]; v != nil {
- if p, ok := v.(*valueProperty); ok {
- if !p.configurable {
- a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.toString())
- return false
- }
- a.propValueCount--
- }
- a.values[idx] = nil
- a.objCount--
- }
- }
- return true
- }
- func (a *arrayObject) deleteStr(name string, throw bool) bool {
- if idx := strToIdx(name); idx != math.MaxUint32 {
- return a._deleteIdxProp(idx, throw)
- }
- return a.baseObject.deleteStr(name, throw)
- }
- func (a *arrayObject) 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 *arrayObject) export() interface{} {
- arr := make([]interface{}, a.length)
- for i, v := range a.values {
- if v != nil {
- arr[i] = v.Export()
- }
- }
- return arr
- }
- func (a *arrayObject) exportType() reflect.Type {
- return reflectTypeArray
- }
- func (a *arrayObject) setValuesFromSparse(items []sparseArrayItem, newMaxIdx int) {
- a.values = make([]Value, newMaxIdx+1)
- for _, item := range items {
- a.values[item.idx] = item.value
- }
- a.objCount = len(items)
- }
- func toIdx(v valueInt) uint32 {
- if v >= 0 && v < math.MaxUint32 {
- return uint32(v)
- }
- return math.MaxUint32
- }
- func strToIdx64(s string) int64 {
- if s == "" {
- return -1
- }
- l := len(s)
- if s[0] == '0' {
- if l == 1 {
- return 0
- }
- return -1
- }
- var n int64
- if l < 19 {
- // guaranteed not to overflow
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return -1
- }
- n = n*10 + int64(c-'0')
- }
- return n
- }
- if l > 19 {
- // guaranteed to overflow
- return -1
- }
- c18 := s[18]
- if c18 < '0' || c18 > '9' {
- return -1
- }
- for i := 0; i < 18; i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return -1
- }
- n = n*10 + int64(c-'0')
- }
- if n >= math.MaxInt64/10+1 {
- return -1
- }
- n *= 10
- n1 := n + int64(c18-'0')
- if n1 < n {
- return -1
- }
- return n1
- }
- func strToIdx(s string) uint32 {
- if s == "" {
- return math.MaxUint32
- }
- l := len(s)
- if s[0] == '0' {
- if l == 1 {
- return 0
- }
- return math.MaxUint32
- }
- var n uint32
- if l < 10 {
- // guaranteed not to overflow
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return math.MaxUint32
- }
- n = n*10 + uint32(c-'0')
- }
- return n
- }
- if l > 10 {
- // guaranteed to overflow
- return math.MaxUint32
- }
- c9 := s[9]
- if c9 < '0' || c9 > '9' {
- return math.MaxUint32
- }
- for i := 0; i < 9; i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return math.MaxUint32
- }
- n = n*10 + uint32(c-'0')
- }
- if n >= math.MaxUint32/10+1 {
- return math.MaxUint32
- }
- n *= 10
- n1 := n + uint32(c9-'0')
- if n1 < n {
- return math.MaxUint32
- }
- return n1
- }
- func strToGoIdx(s string) int {
- if bits.UintSize == 64 {
- return int(strToIdx64(s))
- }
- i := strToIdx(s)
- if i == math.MaxUint32 {
- return -1
- }
- if i >= math.MaxInt32 {
- return -1
- }
- return int(i)
- }
|