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