123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- package goja
- import (
- "fmt"
- "math"
- "math/bits"
- "reflect"
- "strconv"
- "github.com/dop251/goja/unistring"
- )
- 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)
- }
- if ta, ok := ai.obj.self.(*typedArrayObject); ok {
- ta.viewedArrayBuf.ensureNotDetached(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 := nilSafe(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 = classObject
- ai.val = o
- ai.extensible = true
- o.self = ai
- ai.prototype = r.getArrayIteratorPrototype()
- 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 uint32, throw bool) bool {
- 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
- }
- func (a *arrayObject) 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 *arrayObject) 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 *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 unistring.String) Value {
- if len(a.values) > 0 {
- if i := strToArrayIdx(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() int {
- return len(a.values)
- }
- func (a *arrayObject) sortGet(i int) Value {
- v := a.values[i]
- if p, ok := v.(*valueProperty); ok {
- v = p.get(a.val)
- }
- return v
- }
- func (a *arrayObject) swap(i int, j int) {
- a.values[i], a.values[j] = a.values[j], a.values[i]
- }
- func (a *arrayObject) getStr(name unistring.String, receiver Value) Value {
- return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
- }
- func (a *arrayObject) getLengthProp() *valueProperty {
- 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(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 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 *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 unistring.String, val, receiver Value, throw bool) (bool, bool) {
- return a._setForeignStr(name, a.getOwnPropStr(name), val, receiver, throw)
- }
- type arrayPropIter struct {
- a *arrayObject
- limit int
- idx int
- }
- func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
- for i.idx < len(i.a.values) && i.idx < i.limit {
- name := asciiString(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.iterateStringKeys()()
- }
- func (a *arrayObject) iterateStringKeys() iterNextFunc {
- return (&arrayPropIter{
- a: a,
- limit: len(a.values),
- }).next
- }
- func (a *arrayObject) stringKeys(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.stringKeys(all, accum)
- }
- func (a *arrayObject) hasOwnPropertyStr(name unistring.String) bool {
- if idx := strToArrayIdx(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) hasPropertyIdx(idx valueInt) bool {
- if a.hasOwnPropertyIdx(idx) {
- return true
- }
- if a.prototype != nil {
- return a.prototype.self.hasPropertyIdx(idx)
- }
- return false
- }
- 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: a.length,
- propValueCount: a.propValueCount,
- }
- sa.setValues(a.values, a.objCount+1)
- sa.val.self = sa
- sa.lengthProp.writable = a.lengthProp.writable
- sa._put("length", &sa.lengthProp)
- return false
- } else {
- if bits.UintSize == 32 {
- if targetLen >= math.MaxInt32 {
- panic(a.val.runtime.NewTypeError("Array index overflows int"))
- }
- }
- tl := int(targetLen)
- newValues := make([]Value, tl, growCap(tl, len(a.values), cap(a.values)))
- copy(newValues, a.values)
- a.values = newValues
- }
- }
- }
- return true
- }
- func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(uint32, bool) bool, throw bool) bool {
- var newLen uint32
- ret := true
- if descr.Value != nil {
- newLen = r.toLengthUint32(descr.Value)
- }
- if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
- ret = false
- goto Reject
- }
- if descr.Value != nil {
- oldLen := uint32(prop.value.ToInteger())
- if oldLen != newLen {
- 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(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 a.expand(idx) {
- a.values[idx] = prop
- a.objCount++
- if _, ok := prop.(*valueProperty); ok {
- a.propValueCount++
- }
- } else {
- a.val.self.(*sparseArrayObject).add(idx, prop)
- }
- }
- return ok
- }
- func (a *arrayObject) 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 *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 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 *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(ctx *objectExportCtx) interface{} {
- if v, exists := ctx.get(a.val); exists {
- return v
- }
- arr := make([]interface{}, a.length)
- ctx.put(a.val, arr)
- if a.propValueCount == 0 && a.length == uint32(len(a.values)) && uint32(a.objCount) == a.length {
- for i, v := range a.values {
- if v != nil {
- arr[i] = exportValue(v, ctx)
- }
- }
- } else {
- for i := uint32(0); i < a.length; i++ {
- v := a.getIdx(valueInt(i), nil)
- if v != nil {
- arr[i] = exportValue(v, ctx)
- }
- }
- }
- return arr
- }
- func (a *arrayObject) exportType() reflect.Type {
- return reflectTypeArray
- }
- func (a *arrayObject) 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 i := 0; i < l; i++ {
- if i >= len(a.values) {
- break
- }
- val := a.values[i]
- if p, ok := val.(*valueProperty); ok {
- val = p.get(a.val)
- }
- err := r.toReflectValue(val, dst.Index(i), ctx)
- if err != nil {
- return fmt.Errorf("could not convert array element %v to %v at %d: %w", val, typ, i, err)
- }
- }
- return nil
- }
- return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
- }
- 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
- }
|