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