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