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