github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/array_sparse.go (about) 1 package goja 2 3 import ( 4 "fmt" 5 "math" 6 "math/bits" 7 "reflect" 8 "sort" 9 "strconv" 10 11 "github.com/nuvolaris/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 // Slow path 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 // we know it's foreign because prototype loops are not allowed 162 if res, ok := proto.self.setForeignIdx(valueInt(idx), val, a.val, throw); ok { 163 return res 164 } 165 } 166 167 // new property 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) expand(idx uint32) bool { 306 if l := len(a.items); l >= 1024 { 307 if ii := a.items[l-1].idx; ii > idx { 308 idx = ii 309 } 310 if (bits.UintSize == 64 || idx < math.MaxInt32) && int(idx)>>3 < l { 311 //log.Println("Switching sparse->standard") 312 ar := &arrayObject{ 313 baseObject: a.baseObject, 314 length: a.length, 315 propValueCount: a.propValueCount, 316 } 317 ar.setValuesFromSparse(a.items, int(idx)) 318 ar.val.self = ar 319 ar.lengthProp.writable = a.lengthProp.writable 320 a._put("length", &ar.lengthProp) 321 return false 322 } 323 } 324 return true 325 } 326 327 func (a *sparseArrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, throw bool) bool { 328 var existing Value 329 i := a.findIdx(idx) 330 if i < len(a.items) && a.items[i].idx == idx { 331 existing = a.items[i].value 332 } 333 prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw) 334 if ok { 335 if idx >= a.length { 336 if !a.setLengthInt(idx+1, throw) { 337 return false 338 } 339 } 340 if i >= len(a.items) || a.items[i].idx != idx { 341 if a.expand(idx) { 342 a.items = append(a.items, sparseArrayItem{}) 343 copy(a.items[i+1:], a.items[i:]) 344 a.items[i] = sparseArrayItem{ 345 idx: idx, 346 value: prop, 347 } 348 if idx >= a.length { 349 a.length = idx + 1 350 } 351 } else { 352 a.val.self.(*arrayObject).values[idx] = prop 353 } 354 } else { 355 a.items[i].value = prop 356 } 357 if _, ok := prop.(*valueProperty); ok { 358 a.propValueCount++ 359 } 360 } 361 return ok 362 } 363 364 func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 365 if idx := strToArrayIdx(name); idx != math.MaxUint32 { 366 return a._defineIdxProperty(idx, descr, throw) 367 } 368 if name == "length" { 369 return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw) 370 } 371 return a.baseObject.defineOwnPropertyStr(name, descr, throw) 372 } 373 374 func (a *sparseArrayObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { 375 if idx := toIdx(idx); idx != math.MaxUint32 { 376 return a._defineIdxProperty(idx, descr, throw) 377 } 378 return a.baseObject.defineOwnPropertyStr(idx.string(), descr, throw) 379 } 380 381 func (a *sparseArrayObject) _deleteIdxProp(idx uint32, throw bool) bool { 382 i := a.findIdx(idx) 383 if i < len(a.items) && a.items[i].idx == idx { 384 if p, ok := a.items[i].value.(*valueProperty); ok { 385 if !p.configurable { 386 a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.toString()) 387 return false 388 } 389 a.propValueCount-- 390 } 391 copy(a.items[i:], a.items[i+1:]) 392 a.items[len(a.items)-1].value = nil 393 a.items = a.items[:len(a.items)-1] 394 } 395 return true 396 } 397 398 func (a *sparseArrayObject) deleteStr(name unistring.String, throw bool) bool { 399 if idx := strToArrayIdx(name); idx != math.MaxUint32 { 400 return a._deleteIdxProp(idx, throw) 401 } 402 return a.baseObject.deleteStr(name, throw) 403 } 404 405 func (a *sparseArrayObject) deleteIdx(idx valueInt, throw bool) bool { 406 if idx := toIdx(idx); idx != math.MaxUint32 { 407 return a._deleteIdxProp(idx, throw) 408 } 409 return a.baseObject.deleteStr(idx.string(), throw) 410 } 411 412 func (a *sparseArrayObject) sortLen() int { 413 if len(a.items) > 0 { 414 return toIntStrict(int64(a.items[len(a.items)-1].idx) + 1) 415 } 416 417 return 0 418 } 419 420 func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} { 421 if v, exists := ctx.get(a.val); exists { 422 return v 423 } 424 arr := make([]interface{}, a.length) 425 ctx.put(a.val, arr) 426 var prevIdx uint32 427 for _, item := range a.items { 428 idx := item.idx 429 for i := prevIdx; i < idx; i++ { 430 if a.prototype != nil { 431 if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil { 432 arr[i] = exportValue(v, ctx) 433 } 434 } 435 } 436 v := item.value 437 if v != nil { 438 if prop, ok := v.(*valueProperty); ok { 439 v = prop.get(a.val) 440 } 441 arr[idx] = exportValue(v, ctx) 442 } 443 prevIdx = idx + 1 444 } 445 for i := prevIdx; i < a.length; i++ { 446 if a.prototype != nil { 447 if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil { 448 arr[i] = exportValue(v, ctx) 449 } 450 } 451 } 452 return arr 453 } 454 455 func (a *sparseArrayObject) exportType() reflect.Type { 456 return reflectTypeArray 457 } 458 459 func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { 460 r := a.val.runtime 461 if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil { 462 l := toIntStrict(int64(a.length)) 463 if typ.Kind() == reflect.Array { 464 if dst.Len() != l { 465 return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len()) 466 } 467 } else { 468 dst.Set(reflect.MakeSlice(typ, l, l)) 469 } 470 ctx.putTyped(a.val, typ, dst.Interface()) 471 for _, item := range a.items { 472 val := item.value 473 if p, ok := val.(*valueProperty); ok { 474 val = p.get(a.val) 475 } 476 idx := toIntStrict(int64(item.idx)) 477 if idx >= l { 478 break 479 } 480 err := r.toReflectValue(val, dst.Index(idx), ctx) 481 if err != nil { 482 return fmt.Errorf("could not convert array element %v to %v at %d: %w", item.value, typ, idx, err) 483 } 484 } 485 return nil 486 } 487 return a.baseObject.exportToArrayOrSlice(dst, typ, ctx) 488 }