• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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