1/* 2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package escompat; 17 18// NOTE: autogenerated file 19 20% template = ERB.new(File.read("Array_header.erb"), trim_mode: '%', eoutvar: '_sub04') 21<%= template.result(binding) %> 22 23class ArrayKeysIterator<T> implements IterableIterator<number> { 24 private parent: Array<T> 25 private idx: int = 0 26 private isDone: boolean = false 27 28 constructor(parent: Array<T>) { 29 this.parent = parent 30 } 31 32 override next(): IteratorResult<number> { 33 if (this.isDone || this.idx >= this.parent.actualLength) { 34 this.isDone = true 35 return new IteratorResult<number>() 36 } 37 return new IteratorResult<number>(this.idx++ as number) 38 } 39 40 override $_iterator(): IterableIterator<number> { 41 return this 42 } 43} 44 45class FromBuffer {} 46// initialized in _initializerBlock_.ets 47const FROM_BUFFER: FromBuffer; 48 49/** 50 * Represents JS API-compatible Array 51 */ 52export class Array<T> implements ReadonlyArray<T>, Iterable<T> { 53 private buffer: FixedArray<NullishType> 54 internal actualLength: int 55 56 override get length(): number { 57 return this.actualLength as number 58 } 59 60 set length(newLen: number) { 61 const len = newLen as int 62 if (len < 0 || len > this.actualLength) { 63 throw new RangeError("can't change length to bigger or negative") 64 } 65 this.actualLength = len 66 } 67 68 public override $_get(index: number): T { 69 return this.$_get(index as int) 70 } 71 72 public $_set(i: number, val: T): void { 73 this.$_set(i as int, val) 74 } 75 76 public native $_get(idx: int): T; 77 78 internal final $_get_unsafe(idx: int): T { 79 return this.buffer[idx] as T 80 } 81 82 public native $_set(idx: int, val: T): void; 83 84 private $_set_unsafe(idx: int, val: T): void { 85 this.buffer[idx] = val 86 } 87 88 /** 89 * Creates a new instance of Array 90 */ 91 public constructor(arrayLen: int) { 92 this.buffer = new NullishType[arrayLen] 93 this.actualLength = arrayLen 94 } 95 96 public constructor(arrayLen: number) { 97 this(arrayLen as int) 98 } 99 100 internal constructor(_tag: FromBuffer, buf: FixedArray<NullishType>) { 101 this.buffer = buf 102 this.actualLength = buf.length 103 } 104 105 internal constructor() { 106 this.buffer = new NullishType[4] 107 this.actualLength = 0 108 } 109 110 /** 111 * Creates a new instance of Array based on Object[] 112 * 113 * @param d Array initializer 114 */ 115 public constructor(first: T, ...d: T[]) { 116 this.buffer = new NullishType[d.length + 1] 117 this.actualLength = d.length as int + 1 118 119 this.buffer[0] = first 120 121 for (let k: int = 0; k < d.length; k++) { 122 this.$_set_unsafe(k + 1, d[k]) 123 } 124 } 125 126 /** 127 * Creates a new instance of Array. 128 * 129 * @param arrayLength amount of elements. 130 * 131 * @param initialValue initial value of elements. 132 * 133 */ 134 public static create<T>(arrayLength: number, initialValue: T): Array<T> { 135 let other = new Array<T>(arrayLength as int) 136 other.fill(initialValue) 137 return other 138 } 139 140 /** 141 * Extends Array with new elements to specified length. 142 * 143 * @param arrayLength new length of the array. 144 * 145 * @param initialValue initial value of added elements. 146 * 147 */ 148 public extendTo(arrayLength: number, initialValue: T): void { 149 if (arrayLength > Int.MAX_VALUE) { 150 throw new RangeError("arrayLength must be <= int32 max") 151 } 152 const len = arrayLength as int 153 const delta: int = len - this.actualLength 154 if (delta <= 0) { 155 return 156 } 157 this.ensureUnusedCapacity(delta) 158 for (let i: int = 0; i < delta; i++) { 159 this.buffer[this.actualLength + i] = initialValue 160 } 161 this.actualLength = len 162 } 163 164 /** 165 * Shrinks Array to specified length. 166 * 167 * @param arrayLength length at which to shrink. 168 * 169 */ 170 public shrinkTo(arrayLength: number): void { 171 if (arrayLength >= this.actualLength) { 172 return 173 } 174 let newLen: int = arrayLength as int 175 if (newLen < 0) { 176 // Convert from signed to unsigned 177 newLen = newLen & Int.MAX_VALUE 178 } 179 const other = this.slice(0, newLen) 180 this.buffer = other.buffer 181 this.actualLength = other.actualLength 182 } 183 184 /** 185 * Creates a new instance of an Array with the specified length 186 * 187 * @param arrayLength The length of the array to be created (optional). 188 * 189 * @returns A new Array instance with the specified length 190 */ 191 static $_invoke<T>(): Array<T> { 192 return new Array<T>(); 193 } 194 195 /** 196 * Creates a new instance of an Array with the specified length 197 * 198 * @param arrayLength The length of the array to be created (optional). 199 * 200 * @returns A new Array instance with the specified length 201 */ 202 static $_invoke<T>(arrayLength?: number): Array<T> { 203 if (arrayLength != undefined) { 204 return new Array<T>(arrayLength); 205 } else { 206 return new Array<T>(); 207 } 208 } 209 210 /** 211 * Creates a new instance of an Array with the specified length 212 * 213 * @param items array of values. 214 * 215 * @returns A new Array instance with the specified length 216 */ 217 static $_invoke<T>(...items: T[]): Array<T> { 218 if (items.length == 0) { 219 return new Array<T>(0) 220 } 221 return new Array<T>(items[0], ...items.slice(1)) 222 } 223 224 /** 225 * Creates a new `Array` instance from `Object[]` primitive array. 226 * 227 * @param iterable an iterable object to convert to an array. 228 * 229 * @returns `Array` intance constructed from `Object[]` primitive array. 230 */ 231 public static from<T>(iterable: ArrayLike<T> | Iterable<T>): Array<T> { 232 const ret = new Array<T>() 233 iteratorForEach<T>(iterable.$_iterator(), (x: T): void => { 234 ret.push(x) 235 }) 236 return ret 237 } 238 239 /** 240 * Creates a new `Array` instance from `Object[]` ArrayLike. 241 * 242 * @param arr an iterable object to convert to an array. 243 * 244 * @returns `Array` intance constructed from `Object[]` ArrayLike. 245 */ 246 public static from<T>(arr: ArrayLike<T>): Array<T> { 247 const ret = new Array<T>(arr.length as int) 248 let i = 0 249 iteratorForEach<T>(arr.$_iterator(), (x: T): void => { 250 ret[i] = x 251 i += 1 252 }) 253 return ret 254 } 255 256 /** 257 * Creates a new `Array` instance from `Object[]` primitive array. 258 * 259 * @param iterable an iterable object to convert to an array. 260 * 261 * @param mapfn a mapping function to call on every element of the array. 262 * Every value to be added to the array is first passed through this function, and `mapfn`'s return value 263 * is added to the array instead. 264 * 265 * @returns `Array` intance constructed from `Object[]` primitive array and given function. 266 */ 267 public static from<T, U>(iterable: ArrayLike<T> | Iterable<T>, mapfn: (v: T, k: number) => U): Array<U> { 268 const ret = new Array<U>() 269 // NOTE (ikorobkov): Please don't replace idx as int[1] with int-variable, because of value of single variable doesn't change (idx++) into lambda call by unknown reason 270 const idx : FixedArray<int> = new int[1] 271 idx[0] = 0 272 iteratorForEach<T>(iterable.$_iterator(), (x: T): void => { 273 ret.push(mapfn(x, idx[0] as number)) 274 idx[0] += 1 275 }) 276 return ret 277 } 278 279 public static from<T, U>(values: FixedArray<T>, mapfn: (v: T, k: number) => U): Array<U> { 280 const ret = new Array<U>(values.length) 281 for (let i = 0; i < values.length; ++i) { 282 ret[i] = mapfn(values[i], i) 283 } 284 return ret 285 } 286 287 /** 288 * Creates a new `Array` instance from `Object[]` primitive array. 289 * 290 * @param arr primitive array. 291 * 292 * @returns `Array` intance constructed from `Object[]` primitive array. 293 */ 294 public static from<T>(arr: FixedArray<T>): Array<T> { 295 const len = arr.length 296 const ret : FixedArray<NullishType> = new NullishType[len as int] 297 for (let i: int = 0; i < len; i++) { 298 ret[i] = arr[i] as NullishType 299 } 300 return new Array<T>(FROM_BUFFER, ret) 301 } 302 303 private lastIndexOfUndefined(fi: int): int { 304 for (let i = fi; i >= 0; i--) { 305 if (this.$_get_unsafe(i) instanceof undefined) { 306 return i 307 } 308 } 309 return -1 310 } 311 312 private lastIndexOfNull(fi: int): int { 313 for (let i = fi; i >= 0; i--) { 314 if (this.$_get_unsafe(i) instanceof null) { 315 return i 316 } 317 } 318 return -1 319 } 320 321 private lastIndexOfString(val: String, fi: int): int { 322 for (let i = fi; i >= 0; i--) { 323 const tmp = this.$_get_unsafe(i) 324 if (tmp instanceof String) { 325 if (tmp == val) { 326 return i 327 } 328 } 329 } 330 return -1 331 } 332 333 private lastIndexOfNumber(val: Number, fi: int): int { 334 const unboxedVal: number = val.valueOf() 335 if (isNaN(val)) { 336 return -1 337 } 338 for (let i = fi; i >= 0; i--) { 339 const tmp = this.$_get_unsafe(i) 340 if (tmp instanceof Number) { 341 if (unboxedVal == tmp.unboxed()) { 342 return i 343 } 344 } 345 } 346 return -1 347 } 348 349 private lastIndexOfFloat(val: Float, fi: int): int { 350 const unboxedVal: float = val.unboxed() 351 if (isNaN(val)) { 352 return -1 353 } 354 for (let i = fi; i >= 0; i--) { 355 const tmp = this.$_get_unsafe(i) 356 if (tmp instanceof Float) { 357 if (unboxedVal == tmp.unboxed()) { 358 return i 359 } 360 } 361 } 362 return -1 363 } 364 365 private lastIndexOfLong(val: Long, fi: int): int { 366 const unboxedVal: long = val.unboxed() 367 for (let i = fi; i >= 0; i--) { 368 const tmp = this.$_get_unsafe(i) 369 if (tmp instanceof Long) { 370 if (tmp == unboxedVal) { 371 return i 372 } 373 } 374 } 375 return -1 376 } 377 378 private lastIndexOfInt(val: Int, fi: int): int { 379 const unboxedVal: int = val.unboxed() 380 for (let i = fi; i >= 0; i--) { 381 const tmp = this.$_get_unsafe(i) 382 if (tmp instanceof Int) { 383 if (tmp == unboxedVal) { 384 return i 385 } 386 } 387 } 388 return -1 389 } 390 391 private lastIndexOfCommon(val: T, fi: int): int { 392 for (let i = fi; i >= 0; i--) { 393 if (val == this.$_get_unsafe(i)) { 394 return i 395 } 396 } 397 return -1 398 } 399 400 private searchUndefined(fi: int, len: int): boolean { 401 for (let i = fi; i < len; i++) { 402 if (this.$_get_unsafe(i) instanceof undefined) { 403 return true 404 } 405 } 406 return false 407 } 408 409 private searchNull(fi: int, len: int): boolean { 410 for (let i = fi; i < len; i++) { 411 if (this.$_get_unsafe(i) instanceof null) { 412 return true 413 } 414 } 415 return false 416 } 417 418 private searchString(val: String, fi: int, len: int): boolean { 419 for (let i = fi; i < len; i++) { 420 const tmp = this.$_get_unsafe(i) 421 if (tmp instanceof String) { 422 if (tmp == val) { 423 return true 424 } 425 } 426 } 427 return false 428 } 429 430 private searchNumber(val: Number, fi: int, len: int): boolean { 431 const unboxedVal: number = val.valueOf() 432 if (isNaN(unboxedVal)) { 433 for (let i = fi; i < len; i++) { 434 const tmp = this.$_get_unsafe(i) 435 if (tmp instanceof Number) { 436 if (isNaN(tmp.valueOf())) { 437 return true 438 } 439 } 440 } 441 } else { 442 for (let i = fi; i < len; i++) { 443 const tmp = this.$_get_unsafe(i) 444 if (tmp instanceof Number) { 445 if (unboxedVal == tmp.unboxed()) { 446 return true 447 } 448 } 449 } 450 } 451 return false 452 } 453 454 private searchFloat(val: Float, fi: int, len: int): boolean { 455 const unboxedVal: float = val.unboxed() 456 if (isNaN(unboxedVal)) { 457 for (let i = fi; i < len; i++) { 458 const tmp = this.$_get_unsafe(i) 459 if (tmp instanceof Float) { 460 if (isNaN(tmp.unboxed())) { 461 return true 462 } 463 } 464 } 465 } else { 466 for (let i = fi; i < len; i++) { 467 const tmp = this.$_get_unsafe(i) 468 if (tmp instanceof Float) { 469 if (unboxedVal == tmp.unboxed()) { 470 return true 471 } 472 } 473 } 474 } 475 return false 476 } 477 478 private searchLong(val: Long, fi: int, len: int): boolean { 479 const unboxedVal: long = val.unboxed() 480 for (let i = fi; i < len; i++) { 481 const tmp = this.$_get_unsafe(i) 482 if (tmp instanceof Long) { 483 if (tmp == unboxedVal) { 484 return true 485 } 486 } 487 } 488 return false 489 } 490 491 private searchInt(val: Int, fi: int, len: int): boolean { 492 const unboxedVal: int = val.unboxed() 493 for (let i = fi; i < len; i++) { 494 const tmp = this.$_get_unsafe(i) 495 if (tmp instanceof Int) { 496 if (tmp == unboxedVal) { 497 return true 498 } 499 } 500 } 501 return false 502 } 503 504 private searchCommon(val: T, fi: int, len: int): boolean { 505 for (let i = fi; i < len; i++) { 506 if (val == this.$_get_unsafe(i)) { 507 return true; 508 } 509 } 510 return false 511 } 512 513 private indexOfUndefined(fi: int, len: int): int { 514 for (let i = fi; i < len; i++) { 515 if (this.$_get_unsafe(i) instanceof undefined) { 516 return i 517 } 518 } 519 return -1 520 } 521 522 private indexOfNull(fi: int, len: int): int { 523 for (let i = fi; i < len; i++) { 524 if (this.$_get_unsafe(i) instanceof null) { 525 return i 526 } 527 } 528 return -1 529 } 530 531 private indexOfString(val: String, fi: int, len: int): int { 532 for (let i = fi; i < len; i++) { 533 const tmp = this.$_get_unsafe(i) 534 if (tmp instanceof String) { 535 if (tmp == val) { 536 return i 537 } 538 } 539 } 540 return -1 541 } 542 543 private indexOfNumber(val: Number, fi: int, len: int): int { 544 const unboxedVal: number = val.valueOf() 545 if (isNaN(val)) { 546 return -1 547 } 548 for (let i = fi; i < len; i++) { 549 const tmp = this.$_get_unsafe(i) 550 if (tmp instanceof Number) { 551 if (unboxedVal == tmp.unboxed()) { 552 return i 553 } 554 } 555 } 556 return -1 557 } 558 559 private indexOfFloat(val: Float, fi: int, len: int): int { 560 const unboxedVal: float = val.unboxed() 561 if (isNaN(val)) { 562 return -1 563 } 564 for (let i = fi; i < len; i++) { 565 const tmp = this.$_get_unsafe(i) 566 if (tmp instanceof Float) { 567 if (unboxedVal == tmp.unboxed()) { 568 return i 569 } 570 } 571 } 572 return -1 573 } 574 575 private indexOfLong(val: Long, fi: int, len: int): int { 576 const unboxedVal: long = val.unboxed() 577 for (let i = fi; i < len; i++) { 578 const tmp = this.$_get_unsafe(i) 579 if (tmp instanceof Long) { 580 if (tmp == unboxedVal) { 581 return i 582 } 583 } 584 } 585 return -1 586 } 587 588 private indexOfInt(val: Int, fi: int, len: int): int { 589 const unboxedVal: int = val.unboxed() 590 for (let i = fi; i < len; i++) { 591 const tmp = this.$_get_unsafe(i) 592 if (tmp instanceof Int) { 593 if (tmp == unboxedVal) { 594 return i 595 } 596 } 597 } 598 return -1 599 } 600 601 private indexOfCommon(val: T, fi: int, len: int): int { 602 for (let i = fi; i < len; i++) { 603 if (val == this.$_get_unsafe(i)) { 604 return i 605 } 606 } 607 return -1 608 } 609 /** 610 * Default comparison function for sort algorithm. 611 * Objects are compared as string. Both objects are convereted to string 612 * using `toString()` method and compared using `compareTo() method of `string` class. 613 * 614 * @param a: Object - Object to be compared 615 * 616 * @param b: Object - Object to be compared 617 * 618 * @returns Returns one of values -1, 0, 1 (_less_, _equal_, _greater_ respectively). 619 */ 620 private static defaultComparator(a: NullishType, b: NullishType): number { 621 if (a instanceof Number && b instanceof Number) { 622 const x = (a as Number).valueOf() 623 const y = (b as Number).valueOf() 624 if (Number.isInteger(x) && Number.isInteger(y) && 625 x <= Int.MAX_VALUE / 128 && x >= Int.MIN_VALUE / 128 && 626 y <= Int.MAX_VALUE / 128 && y >= Int.MIN_VALUE / 128) { 627 let z = x as int 628 let w = y as int 629 return Array.defaultComparatorInts(z, w) 630 } 631 } else if (a instanceof String && b instanceof String) { 632 return a.compareTo(b) 633 } 634 let sa = new String(a) 635 let sb = new String(b) 636 return sa.compareTo(sb) 637 } 638 639 private static defaultComparatorInts(a: int, b: int): number { 640 if (a < 0) { 641 if (b >= 0) { 642 return -1 643 } 644 a *= -1 645 b *= -1 646 } else if (b < 0) { 647 return 1 648 } 649 let aDigs = 1 650 while (10 * aDigs <= a) { 651 aDigs *= 10 652 } 653 let bDigs = 1 654 while (10 * bDigs <= b) { 655 bDigs *= 10 656 } 657 658 while (aDigs > 0 && bDigs > 0) { 659 let r = (a / aDigs) - (b / bDigs) 660 if (r != 0) { 661 return r 662 } 663 aDigs /= 10 664 bDigs /= 10 665 } 666 return (aDigs - bDigs) 667 } 668 669 private static defaultComparatorStr(a: String, b: String) { 670 return a.compareTo(b) 671 } 672 673 /** 674 * Helper function preparing copy of `this` instance of `Array` class' data array. 675 * 676 * @returns Copy of an `Array`'s primitive array data. 677 */ 678 private copyArray(): FixedArray<NullishType> { 679 let len: int = this.actualLength 680 let res : FixedArray<NullishType> = new NullishType[len] 681 for (let i = 0; i < len; i++) { 682 res[i] = this.$_get_unsafe(i) 683 } 684 return res 685 } 686 687 private wrap_default_sort(): void { 688 let idxNonUndef = 0 689 type arrType = String | undefined 690 try { 691 let strArr : FixedArray<arrType> = new arrType[this.actualLength] 692 for (let i = 0; i < this.actualLength; i++) { 693 const vl = this.$_get_unsafe(i) 694 if (vl !== undefined) { 695 if (vl == null) { 696 this.$_set_unsafe(idxNonUndef, vl as T) 697 strArr[idxNonUndef] = "null" 698 } else { 699 this.$_set_unsafe(idxNonUndef, vl) 700 strArr[idxNonUndef] = (vl as object).toString() // #26217 701 } 702 idxNonUndef++ 703 } 704 } 705 let sortTo = idxNonUndef 706 for (let i = idxNonUndef; i < this.actualLength; i++) { 707 this.$_set_unsafe(i, undefined as T) 708 } 709 710 sort_default<NullishType>(this.buffer, strArr, 0, sortTo) 711 } 712 catch (e) { 713 if (e instanceof OutOfMemoryError) { 714 this.slow_default_sort() 715 } else { 716 throw e as Error 717 } 718 } 719 } 720 721 private slow_default_sort(): void { 722 let idxNonUndef = 0 723 const cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => { 724 return Array.defaultComparator(l, r) 725 } 726 for (let i = 0; i < this.actualLength; i++) { 727 const vl = this.$_get_unsafe(i) 728 if (vl !== undefined) { 729 this.$_set_unsafe(idxNonUndef, vl) 730 idxNonUndef++ 731 } 732 } 733 let sortTo = idxNonUndef 734 for (let i = idxNonUndef; i < this.actualLength; i++) { 735 this.$_set_unsafe(i, undefined as T) 736 } 737 sort_stable<NullishType>(this.buffer, 0, sortTo, cmp) 738 } 739 740 private move_undefined_end(): int { 741 let writeIndex: int = 0 742 for (let i = 0; i < this.actualLength; i++) { 743 let val = this.$_get_unsafe(i) 744 if (val !== undefined) { 745 if(writeIndex != i) { 746 this.$_set_unsafe(writeIndex, val) 747 } 748 writeIndex++ 749 } 750 } 751 for (let i = writeIndex; i < this.actualLength; i++) { 752 this.$_set_unsafe(i, undefined as T) 753 } 754 return writeIndex 755 } 756 757 /** 758 * Reorders elements of `this` using comparator function. 759 * 760 * @see ECMA-262, 23.1.3.30 761 * 762 * @param comparator function that defines the sort order. 763 * 764 * @returns `this` instance of `Array` class. 765 * 766 * @note Mutating method 767 * 768 * NOTE clarify UTF-16 or UTF-8 769 */ 770 public sort(comparator?: (a: T, b: T) => number): this { 771 if (this.actualLength <= 1) 772 return this 773 774 if (comparator == undefined) { 775 this.wrap_default_sort() 776 return this 777 } 778 779 const compareTo = this.move_undefined_end() 780 let cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => { 781 return comparator!(l as T, r as T) 782 } 783 sort_stable<NullishType>(this.buffer, 0, compareTo, cmp) 784 return this 785 } 786 787 /** 788 * Removes the first element from an array and returns that removed element. 789 * This method changes the length of the array. 790 * 791 * @returns shifted element, i.e. that was at index zero 792 */ 793 public shift(): T | undefined { 794 if(this.actualLength == 0) { 795 return undefined 796 } 797 let obj = this.$_get_unsafe(0) 798 const other = this.slice(1, this.actualLength) 799 this.buffer = other.buffer 800 this.actualLength = other.actualLength 801 return obj 802 } 803 804 /** 805 * Removes the last element from an array and returns that element. 806 * This method changes the length of the array. 807 * 808 * @returns removed element 809 */ 810 public pop(): T | undefined { 811 if(this.actualLength == 0) { 812 return undefined 813 } 814 let obj = this.$_get_unsafe(this.actualLength - 1) 815 this.buffer[this.actualLength - 1] = null 816 this.actualLength-- 817 return obj 818 } 819 820 /** 821 * Adds the specified elements to the end of an array and returns the new length of the array. 822 * 823 * @returns new length 824 */ 825 public push(...val: T[]): number { 826 this.ensureUnusedCapacity(val.length as int) 827 for (let i = 0; i < val.length; i++) { 828 this.buffer[this.actualLength + i] = val[i] 829 } 830 this.actualLength += val.length 831 return this.actualLength 832 } 833 834 /** 835 * Adds the specified elements to the end of an array and returns the new length of the array. 836 * 837 * @returns new length 838 */ 839 public pushECMA(...val: T[]): number { 840 this.ensureUnusedCapacity(val.length as int) 841 for (let i = 0; i < val.length; i++) { 842 this.buffer[this.actualLength + i] = val[i] 843 } 844 this.actualLength += val.length 845 return this.actualLength 846 } 847 848 private ensureUnusedCapacity(cap: int): void { 849 if (this.actualLength + cap > this.buffer.length) { 850 const copy : FixedArray<NullishType> = new NullishType[this.buffer.length * 2 + cap] 851 for (let i = 0; i < this.actualLength; i++) { 852 copy[i] = this.buffer[i] 853 } 854 this.buffer = copy 855 } 856 } 857 858 /** 859 * Changes the contents of an array by removing or replacing existing elements 860 * and/or adding new elements in place. 861 * 862 * @param start index 863 * 864 * @param delete number of items after start index 865 * 866 * @returns an Array with deleted elements 867 */ 868 public splice(start: number, delete: Number | undefined, ...items: T[]): Array<T> { 869 return this.splice(start as int, asIntOrDefault(delete, 0), ...items) 870 } 871 872 /** 873 * Changes the contents of an array by removing or replacing existing elements 874 * and/or adding new elements in place. 875 * 876 * @param start index 877 * 878 * @param delete number of items after start index 879 * 880 * @returns an Array with deleted elements 881 */ 882 public splice(start: int, delete: int, ...items: T[]): Array<T> { 883 start = normalizeIndex(start, this.actualLength) 884 if (delete < 0) { 885 delete = 0 886 } 887 if (start > this.actualLength - delete) { 888 delete = this.actualLength - start 889 } 890 // this: [left middle right], we must replace middle with `items` 891 892 this.ensureUnusedCapacity(items.length as int - delete) 893 const oldLen = this.actualLength 894 this.actualLength = this.actualLength - delete + items.length as int 895 896 let ret = new Array<T>(delete) 897 let lastSet = start 898 // left part remains unchanged 899 // copy excluded part 900 for (let i = 0; i < delete; i++) { 901 ret.buffer[i] = this.buffer[start + i] 902 } 903 // move right part to the right of the buffer 904 const rightLen = oldLen - start - delete 905 if (items.length > delete) { 906 for (let i = 0; i < rightLen; i++) { 907 this.buffer[this.actualLength - 1 - i] = this.buffer[oldLen - 1 - i] 908 } 909 } else { 910 for (let i = 0; i < rightLen; i++) { 911 this.buffer[start + items.length as int + i] = this.buffer[start + delete + i] 912 } 913 } 914 // insert middle part 915 for (let i = 0; i < items.length; i++) { 916 this.buffer[start + i] = items[i] 917 } 918 return ret 919 } 920 921 /** 922 * Changes the contents of an array by removing or replacing existing elements 923 * and/or adding new elements in place. 924 * 925 * @param start index 926 * 927 * @returns an Array with deleted elements from start to the last element of the current instance 928 */ 929 public splice(start: number): Array<T> { 930 return this.splice(start as int) 931 } 932 933 /** 934 * Changes the contents of an array by removing or replacing existing elements 935 * and/or adding new elements in place. 936 * 937 * @param start index 938 * 939 * @returns an Array with deleted elements from start to the last element of the current instance 940 */ 941 public splice(start: int): Array<T> { 942 return this.splice(start, this.actualLength) 943 } 944 945 /** 946 * Checks whether the passed value is an Array. 947 * 948 * @param arr 949 * 950 * @returns true is arr is a non-nullish array, false otherwise 951 */ 952 public static isArray(o: NullishType): boolean { 953 if (o instanceof Array) { 954 return true 955 } 956 return (Type.of(o) instanceof ArrayType) 957 } 958 959 /** 960 * Creates a new Array instance from a variable number of arguments, 961 * regardless of number or type of the arguments. 962 * 963 * @param values an initilizer 964 * 965 * @returns a newly created Array 966 */ 967 public static of<T>(...values: T[]): Array<T> { 968 const ret = new Array<T>() 969 ret.ensureUnusedCapacity(values.length as int) 970 for (let i = 0; i < values.length; i++) { 971 ret.push(values[i]) 972 } 973 return ret 974 } 975 976 /** 977 * Adds the specified elements to the beginning of an Array 978 * and returns the new length of the Array. 979 * 980 * @param values data to be added 981 * 982 * @returns new length of the Array 983 */ 984 public unshift(...values: T[]): number { 985 let buffer = this.buffer 986 if (this.buffer.length <= values.length + this.actualLength) { 987 buffer = new NullishType[this.buffer.length * 2 + values.length] 988 } 989 for (let i = 0; i < this.actualLength; i++) { 990 buffer[this.actualLength + values.length as int - i - 1] = this.buffer[this.actualLength - 1 - i] 991 } 992 for (let i = 0; i < values.length; i++) { 993 buffer[i] = values[i] 994 } 995 this.buffer = buffer 996 this.actualLength += values.length 997 return this.actualLength 998 } 999 1000 /** 1001 * Returns an iterator over all indices 1002 */ 1003 public override keys(): IterableIterator<Number> { 1004 return new ArrayKeysIterator<T>(this) 1005 } 1006 1007 /** 1008 * Returns an iterator over all values 1009 */ 1010 public override $_iterator(): IterableIterator<T> { 1011 return this.values() 1012 } 1013 1014 // === methods with uncompatible implementation === 1015 /** 1016 * Returns the elements of an array that meet the condition specified in a callback function. 1017 * 1018 * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. 1019 * 1020 * @returns New `Array` instance constructed from `this` with elements filtered using test function `predicate`. 1021 */ 1022 public override filter(predicate: (value: T, index: number, array: Array<T>) => boolean): Array<T> { 1023 const res = new Array<T>() 1024 for (let i: int = 0; i < this.actualLength; i++) { 1025 const val = this.$_get_unsafe(i) 1026 if (predicate(val, i as number, this)) { 1027 res.push(val) 1028 } 1029 } 1030 return res 1031 } 1032 1033 /** 1034 * Creates a new Array with all sub-array elements concatenated 1035 * into it recursively up to the specified depth. 1036 * 1037 * @param depth 1038 * 1039 * @returns a flattened Array with respect to depth 1040 */ 1041 public flat<U>(depth: number): Array<U> { 1042 return this.flat<U>(depth as int) 1043 } 1044 1045 /** 1046 * Creates a new Array with all sub-array elements concatenated 1047 * into it recursively up to the specified depth. 1048 * 1049 * @param depth 1050 * 1051 * @returns a flattened Array with respect to depth 1052 */ 1053 public flat<U>(depth: int): Array<U> { 1054 let ret = new Array<U>() 1055 this.flatImpl<U>(depth, ret) 1056 return ret 1057 } 1058 1059 private flatImpl<U>(depth: int, to: Array<U>) { 1060 throw new Error("not implemented") 1061 } 1062 1063 /** 1064 * Creates a new Array with all sub-array elements concatenated 1065 * 1066 * @returns a flattened Array 1067 */ 1068 public flat<U>(): Array<U> { 1069 return this.flat<U>(1) 1070 } 1071 1072 /** 1073 * Applies flat and than map 1074 * 1075 * fn a function to apply 1076 * 1077 * @return new Array after map and than flat 1078 */ 1079 // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray 1080 public flatMap<U>(fn: (v: T, k: number, arr: Array<T>) => U): Array<U> { 1081 let mapped: Array<U> = this.map<U>(fn) 1082 return mapped.flat<U>() 1083 } 1084 1085 // === methods common among all arrays === 1086% require 'ostruct' 1087% ctx = OpenStruct.new 1088% $ctx = ctx 1089% ctx.this = 'this' 1090% ctx.this_arg = '' 1091% ctx.this_len_int = 'this.actualLength' 1092% ctx.this_array = 'escompat_array' 1093% ctx.array_len_int = Proc.new { |v| "#{v}.actualLength" } 1094% ctx.access_public = 'public' 1095% ctx.access_private = 'private' 1096% ctx.override = 'override' 1097% ctx.override_expected_here = '' # hide 'override' when it cannot be implemented 1098% ctx.get_unsafe = Proc.new { |t, i| "#{t}.$_get_unsafe(#{i})" } 1099% ctx.set_unsafe = Proc.new { |t, i, v| "#{t}.$_set_unsafe(#{i}, #{v})" } 1100% ctx.el_type = 'T' 1101% ctx.el_type_boxed = 'T' 1102% ctx.function_type = 'has_native' 1103% ctx.this_type = 'Array<T>' 1104% ctx.this_return_type = 'this' 1105% ctx.clone_this = 'new Array<T>(FROM_BUFFER, this.copyArray())' 1106% ctx.make_buffer = Proc.new { |l, elt| 1107% "new NullishType[#{l}]" 1108% } 1109% ctx.make_fixed_array = 'FixedArray<NullishType>' 1110% ctx.from_buffer = Proc.new { |b, elt| 1111% elt ||= ctx.el_type 1112% "new Array<#{elt}>(FROM_BUFFER, #{b})" 1113% } 1114% ctx.array_of_type = Proc.new { |t| "Array<#{t}>" } 1115% ctx.this_call = Proc.new { |f| "this.#{f}(" } 1116% ctx.arr_method_call = Proc.new { |t, f, *args| "#{t}.#{f}(#{args.join(', ')})" } 1117% ctx.this_generic = '' 1118% ctx.this_generic_one = '' 1119% ctx.this_iterator_generic = '<T>' 1120% template = ERB.new(File.read("Array_common.erb"), trim_mode: '%', eoutvar: '_sub01') 1121<%= template.result(ctx.instance_eval { binding }).gsub(/^/, ' ') %> 1122% template = ERB.new(File.read("Array_map.erb"), trim_mode: '%', eoutvar: '_sub02') 1123% ctx.mapped_type = 'U' 1124% ctx.map_generic = '<U>' 1125<%= template.result(ctx.instance_eval { binding }).gsub(/^/, ' ') %> 1126} 1127% template = ERB.new(File.read("Array_common_top_scope.erb"), trim_mode: '%', eoutvar: '_sub03') 1128<%= template.result(ctx.instance_eval { binding }).rstrip %> 1129