1package goja
2
3import (
4	"reflect"
5	"strconv"
6
7	"github.com/dop251/goja/unistring"
8)
9
10type objectGoSlice struct {
11	baseObject
12	data       *[]interface{}
13	lengthProp valueProperty
14}
15
16func (o *objectGoSlice) init() {
17	o.baseObject.init()
18	o.class = classArray
19	o.prototype = o.val.runtime.global.ArrayPrototype
20	o.lengthProp.writable = true
21	o.extensible = true
22	o.updateLen()
23	o.baseObject._put("length", &o.lengthProp)
24}
25
26func (o *objectGoSlice) updateLen() {
27	o.lengthProp.value = intToValue(int64(len(*o.data)))
28}
29
30func (o *objectGoSlice) getStr(name unistring.String, receiver Value) Value {
31	var ownProp Value
32	if idx := strToGoIdx(name); idx >= 0 && idx < len(*o.data) {
33		v := (*o.data)[idx]
34		ownProp = o.val.runtime.ToValue(v)
35	} else if name == "length" {
36		ownProp = &o.lengthProp
37	}
38
39	return o.getStrWithOwnProp(ownProp, name, receiver)
40}
41
42func (o *objectGoSlice) getIdx(idx valueInt, receiver Value) Value {
43	if idx := int64(idx); idx >= 0 && idx < int64(len(*o.data)) {
44		v := (*o.data)[idx]
45		return o.val.runtime.ToValue(v)
46	}
47	if o.prototype != nil {
48		if receiver == nil {
49			return o.prototype.self.getIdx(idx, o.val)
50		}
51		return o.prototype.self.getIdx(idx, receiver)
52	}
53	return nil
54}
55
56func (o *objectGoSlice) getOwnPropStr(name unistring.String) Value {
57	if idx := strToGoIdx(name); idx >= 0 {
58		if idx < len(*o.data) {
59			v := o.val.runtime.ToValue((*o.data)[idx])
60			return &valueProperty{
61				value:      v,
62				writable:   true,
63				enumerable: true,
64			}
65		}
66		return nil
67	}
68	if name == "length" {
69		return &o.lengthProp
70	}
71	return nil
72}
73
74func (o *objectGoSlice) getOwnPropIdx(idx valueInt) Value {
75	if idx := int64(idx); idx >= 0 && idx < int64(len(*o.data)) {
76		v := o.val.runtime.ToValue((*o.data)[idx])
77		return &valueProperty{
78			value:      v,
79			writable:   true,
80			enumerable: true,
81		}
82	}
83	return nil
84}
85
86func (o *objectGoSlice) grow(size int) {
87	oldcap := cap(*o.data)
88	if oldcap < size {
89		n := make([]interface{}, size, growCap(size, len(*o.data), oldcap))
90		copy(n, *o.data)
91		*o.data = n
92	} else {
93		tail := (*o.data)[len(*o.data):size]
94		for k := range tail {
95			tail[k] = nil
96		}
97		*o.data = (*o.data)[:size]
98	}
99	o.updateLen()
100}
101
102func (o *objectGoSlice) shrink(size int) {
103	tail := (*o.data)[size:]
104	for k := range tail {
105		tail[k] = nil
106	}
107	*o.data = (*o.data)[:size]
108	o.updateLen()
109}
110
111func (o *objectGoSlice) putIdx(idx int, v Value, throw bool) {
112	if idx >= len(*o.data) {
113		o.grow(idx + 1)
114	}
115	(*o.data)[idx] = v.Export()
116}
117
118func (o *objectGoSlice) putLength(v Value, throw bool) bool {
119	newLen := toIntStrict(toLength(v))
120	curLen := len(*o.data)
121	if newLen > curLen {
122		o.grow(newLen)
123	} else if newLen < curLen {
124		o.shrink(newLen)
125	}
126	return true
127}
128
129func (o *objectGoSlice) setOwnIdx(idx valueInt, val Value, throw bool) bool {
130	if i := toIntStrict(int64(idx)); i >= 0 {
131		if i >= len(*o.data) {
132			if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok {
133				return res
134			}
135		}
136		o.putIdx(i, val, throw)
137	} else {
138		name := idx.string()
139		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
140			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
141			return false
142		} else {
143			return res
144		}
145	}
146	return true
147}
148
149func (o *objectGoSlice) setOwnStr(name unistring.String, val Value, throw bool) bool {
150	if idx := strToGoIdx(name); idx >= 0 {
151		if idx >= len(*o.data) {
152			if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok {
153				return res
154			}
155		}
156		o.putIdx(idx, val, throw)
157	} else {
158		if name == "length" {
159			return o.putLength(val, throw)
160		}
161		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
162			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
163			return false
164		} else {
165			return res
166		}
167	}
168	return true
169}
170
171func (o *objectGoSlice) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
172	return o._setForeignIdx(idx, trueValIfPresent(o.hasOwnPropertyIdx(idx)), val, receiver, throw)
173}
174
175func (o *objectGoSlice) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
176	return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw)
177}
178
179func (o *objectGoSlice) hasOwnPropertyIdx(idx valueInt) bool {
180	if idx := int64(idx); idx >= 0 {
181		return idx < int64(len(*o.data))
182	}
183	return false
184}
185
186func (o *objectGoSlice) hasOwnPropertyStr(name unistring.String) bool {
187	if idx := strToIdx64(name); idx >= 0 {
188		return idx < int64(len(*o.data))
189	}
190	return name == "length"
191}
192
193func (o *objectGoSlice) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
194	if i := toIntStrict(int64(idx)); i >= 0 {
195		if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) {
196			return false
197		}
198		val := descr.Value
199		if val == nil {
200			val = _undefined
201		}
202		o.putIdx(i, val, throw)
203		return true
204	}
205	o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx)
206	return false
207}
208
209func (o *objectGoSlice) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
210	if idx := strToGoIdx(name); idx >= 0 {
211		if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
212			return false
213		}
214		val := descr.Value
215		if val == nil {
216			val = _undefined
217		}
218		o.putIdx(idx, val, throw)
219		return true
220	}
221	if name == "length" {
222		return o.val.runtime.defineArrayLength(&o.lengthProp, descr, o.putLength, throw)
223	}
224	o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name)
225	return false
226}
227
228func (o *objectGoSlice) toPrimitiveNumber() Value {
229	return o.toPrimitiveString()
230}
231
232func (o *objectGoSlice) toPrimitiveString() Value {
233	return o.val.runtime.arrayproto_join(FunctionCall{
234		This: o.val,
235	})
236}
237
238func (o *objectGoSlice) toPrimitive() Value {
239	return o.toPrimitiveString()
240}
241
242func (o *objectGoSlice) _deleteIdx(idx int64) {
243	if idx < int64(len(*o.data)) {
244		(*o.data)[idx] = nil
245	}
246}
247
248func (o *objectGoSlice) deleteStr(name unistring.String, throw bool) bool {
249	if idx := strToIdx64(name); idx >= 0 {
250		o._deleteIdx(idx)
251		return true
252	}
253	return o.baseObject.deleteStr(name, throw)
254}
255
256func (o *objectGoSlice) deleteIdx(i valueInt, throw bool) bool {
257	idx := int64(i)
258	if idx >= 0 {
259		o._deleteIdx(idx)
260	}
261	return true
262}
263
264type goslicePropIter struct {
265	o          *objectGoSlice
266	idx, limit int
267}
268
269func (i *goslicePropIter) next() (propIterItem, iterNextFunc) {
270	if i.idx < i.limit && i.idx < len(*i.o.data) {
271		name := strconv.Itoa(i.idx)
272		i.idx++
273		return propIterItem{name: unistring.String(name), enumerable: _ENUM_TRUE}, i.next
274	}
275
276	return propIterItem{}, nil
277}
278
279func (o *objectGoSlice) enumerateOwnKeys() iterNextFunc {
280	return (&goslicePropIter{
281		o:     o,
282		limit: len(*o.data),
283	}).next
284}
285
286func (o *objectGoSlice) ownKeys(_ bool, accum []Value) []Value {
287	for i := range *o.data {
288		accum = append(accum, asciiString(strconv.Itoa(i)))
289	}
290
291	return accum
292}
293
294func (o *objectGoSlice) export(*objectExportCtx) interface{} {
295	return *o.data
296}
297
298func (o *objectGoSlice) exportType() reflect.Type {
299	return reflectTypeArray
300}
301
302func (o *objectGoSlice) equal(other objectImpl) bool {
303	if other, ok := other.(*objectGoSlice); ok {
304		return o.data == other.data
305	}
306	return false
307}
308
309func (o *objectGoSlice) sortLen() int64 {
310	return int64(len(*o.data))
311}
312
313func (o *objectGoSlice) sortGet(i int64) Value {
314	return o.getIdx(valueInt(i), nil)
315}
316
317func (o *objectGoSlice) swap(i, j int64) {
318	ii := valueInt(i)
319	jj := valueInt(j)
320	x := o.getIdx(ii, nil)
321	y := o.getIdx(jj, nil)
322
323	o.setOwnIdx(ii, y, false)
324	o.setOwnIdx(jj, x, false)
325}
326