• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2024 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"), nil, '%', eoutvar: '_sub04')
21<%= template.result(binding) %>
22
23class ArrayKeysIterator<T> implements IterableIterator<number> {
24    private parent: Array<T>
25    private idx: int = 0
26
27    constructor(parent: Array<T>) {
28        this.parent = parent
29    }
30
31    override next(): IteratorResult<number> {
32        if (this.idx >= this.parent.actualLength) {
33            return new IteratorResult<number>()
34        }
35        return new IteratorResult<number>(this.idx++ as number)
36    }
37
38    override $_iterator(): IterableIterator<number> {
39        return this
40    }
41}
42
43class FromBuffer {}
44const FROM_BUFFER = new FromBuffer()
45
46/**
47 * Represents JS API-compatible Array
48 */
49export class Array<T> implements ReadonlyArray<T>, Iterable<T> {
50    private buffer: NullishType[]
51    internal actualLength: int
52
53    override get length(): number {
54        return this.actualLength as number
55    }
56
57    set length(newLen: number): void {
58        const len = newLen as int
59        if (len < 0 || len > this.actualLength) {
60            throw new RangeError("can't change length to bigger or negative")
61        }
62        this.actualLength = len
63    }
64
65    public override $_get(index: number): T {
66        return this.$_get(index as int)
67    }
68
69    public $_set(i: number, val: T): void {
70        this.$_set(i as int, val)
71    }
72
73    public $_get(idx: int): T {
74        if (idx >= this.actualLength || idx < 0) {
75            throw new RangeError("Out of bounds")
76        }
77        return this.buffer[idx] as T
78    }
79
80    internal $_get_unsafe(idx: int): T {
81        return this.buffer[idx] as T
82    }
83
84    public $_set(idx: int, val: T): void {
85        if (idx >= this.actualLength) {
86            throw new RangeError("Out of bounds")
87        }
88        this.buffer[idx] = val as Object
89    }
90
91    private $_set_unsafe(idx: int, val: T): void {
92        this.buffer[idx] = val
93    }
94
95    /**
96     * Creates a new instance of Array
97     */
98    public constructor(arrayLen: int) {
99        this.buffer = new NullishType[arrayLen]
100        this.actualLength = arrayLen
101    }
102
103    public constructor(arrayLen: number) {
104        this(arrayLen as int)
105    }
106
107    internal constructor(_tag: FromBuffer, buf: NullishType[]) {
108        this.buffer = buf
109        this.actualLength = buf.length
110    }
111
112    internal constructor() {
113        this.buffer = new NullishType[4]
114        this.actualLength = 0
115    }
116
117    /**
118     * Creates a new instance of Array based on Object[]
119     *
120     * @param d Array initializer
121     */
122    public constructor(first: T, ...d: T[]) {
123        this.buffer = new NullishType[d.length + 1]
124        this.actualLength = d.length + 1
125
126        this.buffer[0] = first
127
128        for (let k: int = 0; k < d.length; k++) {
129            this.$_set_unsafe(k + 1, d[k])
130        }
131    }
132
133    /**
134     * Creates a new instance of an Array with the specified length
135     *
136     * @param arrayLength The length of the array to be created (optional).
137     *
138     * @returns A new Array instance with the specified length
139     */
140    static invoke<T>(arrayLength?: number): Array<T> {
141        if (arrayLength != undefined) {
142            return new Array<T>(arrayLength);
143        } else {
144            return new Array<T>();
145        }
146    }
147
148    /**
149     * Creates a new `Array` instance from `Object[]` primitive array.
150     *
151     * @param arr an iterable object to convert to an array.
152     *
153     * @returns `Array` intance constructed from `Object[]` primitive array.
154     */
155    public static from<T>(iterable: ArrayLike<T> | Iterable<T>): Array<T> {
156        return Array.from<T, T>(iterable, (x: T, k: number): T => x)
157    }
158
159    /**
160     * Creates a new `Array` instance from `Object[]` primitive array.
161     *
162     * @param iterable an iterable object to convert to an array.
163     *
164     * @param mapfn a mapping function to call on every element of the array.
165     * Every value to be added to the array is first passed through this function, and `mapfn`'s return value
166     * is added to the array instead.
167     *
168     * @returns `Array` intance constructed from `Object[]` primitive array and given function.
169     */
170    public static from<T, U>(iterable: ArrayLike<T> | Iterable<T>, mapfn: (v: T, k: number) => U): Array<U> {
171        const ret = new Array<U>()
172        // 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
173        const idx = new int[1]
174        idx[0] = 0
175        iteratorForEach<T>(iterable.$_iterator(), (x: T): void => {
176            ret.push(mapfn(x, idx[0] as number))
177            idx[0] += 1
178        })
179        return ret
180    }
181
182    /**
183    * Creates a new `Array` instance from `Object[]` primitive array.
184    *
185    * @param arr primitive array.
186    *
187    * @returns `Array` intance constructed from `Object[]` primitive array.
188    */
189    public static from<T>(arr: T[]): Array<T> {
190        const len = arr.length
191        const ret = new NullishType[len as int]
192        for (let i: int = 0; i < len; i++) {
193            ret[i] = arr[i] as NullishType
194        }
195        return new Array<T>(FROM_BUFFER, ret)
196    }
197
198    /**
199     * Default comparison function for sort algorithm.
200     * Objects are compared as string. Both objects are convereted to string
201     * using `toString()` method and compared using `compareTo() method of `string` class.
202     *
203     * @param a: Object - Object to be compared
204     *
205     * @param b: Object - Object to be compared
206     *
207     * @returns Returns one of values -1, 0, 1 (_less_, _equal_, _greater_ respectively).
208     */
209    private static defaultComparator(a: NullishType, b: NullishType): number {
210        if (a instanceof Number && b instanceof Number) {
211            const x = (a as Number).valueOf()
212            const y = (b as Number).valueOf()
213            if (Number.isInteger(x) && Number.isInteger(y) &&
214                x <= Int.MAX_VALUE / 128 && x >= Int.MIN_VALUE / 128 &&
215                y <= Int.MAX_VALUE / 128 && y >= Int.MIN_VALUE / 128) {
216                let z = x as int
217                let w = y as int
218                return Array.defaultComparatorInts(z, w)
219            }
220        } else if (a instanceof String && b instanceof String) {
221            return a.compareTo(b)
222        }
223        let sa = new String(a)
224        let sb = new String(b)
225        return sa.compareTo(sb)
226    }
227
228    private static defaultComparatorInts(a: int, b: int): number {
229        if (a < 0) {
230            if (b >= 0) {
231                return -1
232            }
233            a *= -1
234            b *= -1
235        } else if (b < 0) {
236            return 1
237        }
238        let aDigs = 1
239        while (10 * aDigs <= a) {
240            aDigs *= 10
241        }
242        let bDigs = 1
243        while (10 * bDigs <= b) {
244            bDigs *= 10
245        }
246
247        while (aDigs > 0 && bDigs > 0) {
248            let r = (a / aDigs) - (b / bDigs)
249            if (r != 0) {
250                return r
251            }
252            aDigs /= 10
253            bDigs /= 10
254        }
255        return (aDigs - bDigs)
256    }
257
258    private static defaultComparatorStr(a: String, b: String) {
259        return a.compareTo(b)
260    }
261
262    /**
263     * Helper function preparing copy of `this` instance of `Array` class' data array.
264     *
265     * @returns Copy of an `Array`'s primitive array data.
266     */
267    private copyArray(): NullishType[] {
268        let len: int = this.actualLength
269        let res = new NullishType[len]
270        for (let i = 0; i < len; i++) {
271            res[i] = this.$_get_unsafe(i)
272        }
273        return res
274    }
275
276    private wrap_default_sort(): void {
277        let idxNonUndef = 0
278        type arrType = String | null
279        try {
280            let strArr = new arrType[this.actualLength]
281            for (let i = 0; i < this.actualLength; i++) {
282                const vl = this.$_get_unsafe(i)
283                // NOTE(aleksander-sotov) We can't use === to compare with undefined due to 18518
284                if (!__runtimeIsSameReference(vl, undefined)) {
285                    if (vl == null) {
286                        this.$_set_unsafe(idxNonUndef, vl as T)
287                        strArr[idxNonUndef] = "null"
288                    } else {
289                        this.$_set_unsafe(idxNonUndef, vl)
290                        strArr[idxNonUndef] = vl.toString()
291                    }
292                    idxNonUndef++
293                }
294            }
295            let sortTo = idxNonUndef
296            for (let i = idxNonUndef; i < this.actualLength; i++) {
297                this.$_set_unsafe(i, undefined as T)
298            }
299
300            sort_default<NullishType>(this.buffer, strArr, 0, sortTo)
301        }
302        catch (e) {
303            if (e instanceof OutOfMemoryError) {
304                this.slow_default_sort()
305            } else {
306                throw e as Error
307            }
308        }
309    }
310
311    private slow_default_sort(): void {
312        let idxNonUndef = 0
313        const cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => {
314            return Array.defaultComparator(l, r)
315        }
316        for (let i = 0; i < this.actualLength; i++) {
317            const vl = this.$_get_unsafe(i)
318            if (!__runtimeIsSameReference(vl, undefined)) {
319                this.$_set_unsafe(idxNonUndef, vl)
320                idxNonUndef++
321            }
322        }
323        let sortTo = idxNonUndef
324        for (let i = idxNonUndef; i < this.actualLength; i++) {
325            this.$_set_unsafe(i, undefined as T)
326        }
327        sort_stable<NullishType>(this.buffer, 0, sortTo, cmp)
328    }
329
330    /**
331     * Reorders elements of `this` using comparator function.
332     *
333     * @param comparator function that defines the sort order.
334     *
335     * @note Mutating method
336     *
337     * NOTE clarify UTF-16 or UTF-8
338     */
339    public sort(comparator?: (a: T, b: T) => number): this {
340        let cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => {
341            return Array.defaultComparator(l, r)
342        }
343        let sortTo = this.actualLength
344        if (!__runtimeIsSameReference(comparator, undefined)) {
345            cmp = (l: NullishType, r: NullishType): number => {
346                return comparator!(l as T, r as T)
347            }
348            sort_stable<NullishType>(this.buffer, 0, sortTo, cmp)
349        } else {
350            this.wrap_default_sort()
351        }
352        return this
353    }
354
355    /**
356     * Removes the first element from an array and returns that removed element.
357     * This method changes the length of the array.
358     *
359     * @returns shifted element, i.e. that was at index zero
360     */
361    public shift(): T | undefined {
362        if(this.actualLength == 0) {
363            return undefined
364        }
365        let obj = this.$_get_unsafe(0)
366        const other = this.slice(1, this.actualLength)
367        this.buffer = other.buffer
368        this.actualLength = other.actualLength
369        return obj
370    }
371
372    /**
373     * Removes the last element from an array and returns that element.
374     * This method changes the length of the array.
375     *
376     * @returns removed element
377     */
378    public pop(): T | undefined {
379        if(this.actualLength == 0) {
380            return undefined
381        }
382        let obj = this.$_get_unsafe(this.actualLength - 1)
383        this.buffer[this.actualLength - 1] = null
384        this.actualLength--
385        return obj
386    }
387
388    // NOTE(kprokopenko): remove when #14756 is fixed and rename below fucntion to push
389    public push(val: T): number {
390        this.ensureUnusedCapacity(1)
391        this.buffer[this.actualLength] = val
392        this.actualLength += 1
393        return this.actualLength
394    }
395
396    /**
397     * Adds the specified elements to the end of an array and returns the new length of the array.
398     *
399     * @returns new length
400     */
401    public pushECMA(...val: T[]): number {
402        this.ensureUnusedCapacity(val.length)
403        for (let i = 0; i < val.length; i++) {
404            this.buffer[this.actualLength + i] = val[i]
405        }
406        this.actualLength += val.length
407        return this.actualLength
408    }
409
410    private ensureUnusedCapacity(cap: int): void {
411        if (this.actualLength + cap > this.buffer.length) {
412            const copy = new NullishType[this.buffer.length * 2 + cap]
413            for (let i = 0; i < this.actualLength; i++) {
414                copy[i] = this.buffer[i]
415            }
416            this.buffer = copy
417        }
418    }
419
420    /**
421     * Changes the contents of an array by removing or replacing existing elements
422     * and/or adding new elements in place.
423     *
424     * @param start index
425     *
426     * @param delete number of items after start index
427     *
428     * @returns an Array with deleted elements
429     */
430    public splice(start: number, delete: Number | undefined, ...items: T[]): Array<T> {
431        return this.splice(start as int, asIntOrDefault(delete, this.actualLength), ...items)
432    }
433
434    /**
435     * Changes the contents of an array by removing or replacing existing elements
436     * and/or adding new elements in place.
437     *
438     * @param start index
439     *
440     * @param delete number of items after start index
441     *
442     * @returns an Array with deleted elements
443     */
444    public splice(start: int, delete: int, ...items: T[]): Array<T> {
445        start = normalizeIndex(start, this.actualLength)
446        if (delete < 0) {
447            delete = 0
448        }
449        if (start > this.actualLength - delete) {
450            delete = this.actualLength - start
451        }
452        // this: [left middle right], we must replace middle with `items`
453
454        this.ensureUnusedCapacity(items.length - delete)
455        const oldLen = this.actualLength
456        this.actualLength = this.actualLength - delete + items.length
457
458        let ret = new Array<T>(delete)
459        let lastSet = start
460        // left part remains unchanged
461        // copy excluded part
462        for (let i = 0; i < delete; i++) {
463            ret.buffer[i] = this.buffer[start + i]
464        }
465        // move right part to the right of the buffer
466        const rightLen = oldLen - start - delete
467        if (items.length > delete) {
468            for (let i = 0; i < rightLen; i++) {
469                this.buffer[this.actualLength - 1 - i] = this.buffer[oldLen - 1 - i]
470            }
471        } else {
472            for (let i = 0; i < rightLen; i++) {
473                this.buffer[start + items.length + i] = this.buffer[start + delete + i]
474            }
475        }
476        // insert middle part
477        for (let i = 0; i < items.length; i++) {
478            this.buffer[start + i] = items[i]
479        }
480        return ret
481    }
482
483    /**
484     * Changes the contents of an array by removing or replacing existing elements
485     * and/or adding new elements in place.
486     *
487     * @param start index
488     *
489     * @returns an Array with deleted elements from start to the last element of the current instance
490     */
491    public splice(start: number): Array<T> {
492        return this.splice(start as int)
493    }
494
495    /**
496     * Changes the contents of an array by removing or replacing existing elements
497     * and/or adding new elements in place.
498     *
499     * @param start index
500     *
501     * @returns an Array with deleted elements from start to the last element of the current instance
502     */
503    public splice(start: int): Array<T> {
504        return this.splice(start, this.actualLength)
505    }
506
507    /**
508     * Checks whether the passed value is an Array.
509     *
510     * @param arr
511     *
512     * @returns true is arr is a non-nullish array, false otherwise
513     */
514    public static isArray(o: NullishType): boolean {
515        if (o instanceof Array) {
516            return true
517        }
518        return (Type.of(o) instanceof ArrayType)
519    }
520
521    /**
522     * Creates a new Array instance from a variable number of arguments,
523     * regardless of number or type of the arguments.
524     *
525     * @param values an initilizer
526     *
527     * @returns a newly created Array
528     */
529    public static of<T>(...values: T[]): Array<T> {
530        const ret = new Array<T>()
531        ret.ensureUnusedCapacity(values.length)
532        for (let i = 0; i < values.length; i++) {
533            ret.push(values[i])
534        }
535        return ret
536    }
537
538    /**
539     * Adds the specified elements to the beginning of an Array
540     * and returns the new length of the Array.
541     *
542     * @param values data to be added
543     *
544     * @returns new length of the Array
545     */
546    public unshift(...values: T[]): number {
547        let buffer = this.buffer
548        if (this.buffer.length <= values.length + this.actualLength) {
549            buffer = new NullishType[this.buffer.length * 2 + values.length]
550        }
551        for (let i = 0; i < this.actualLength; i++) {
552            buffer[this.actualLength + values.length - i - 1] = this.buffer[this.actualLength - 1 - i]
553        }
554        for (let i = 0; i < values.length; i++) {
555            buffer[i] = values[i]
556        }
557        this.buffer = buffer
558        this.actualLength += values.length
559        return this.actualLength
560    }
561
562    /**
563     * Returns an iterator over all indices
564     */
565    public override keys(): IterableIterator<Number> {
566        return new ArrayKeysIterator<T>(this)
567    }
568
569    /**
570     * Returns an iterator over all values
571     */
572    public override $_iterator(): IterableIterator<T> {
573        return this.values()
574    }
575
576    // === methods with uncompatible implementation ===
577    /**
578     * Returns the elements of an array that meet the condition specified in a callback function.
579     *
580     * @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.
581     *
582     * @returns New `Array` instance constructed from `this` with elements filtered using test function `predicate`.
583     */
584    public override filter(predicate: (value: T, index: number) => boolean): Array<T> {
585        const res = new Array<T>()
586        for (let i: int = 0; i < this.actualLength; i++) {
587            const val = this.$_get_unsafe(i)
588            if (predicate(val, i as number)) {
589                res.push(val)
590            }
591        }
592        return res
593    }
594
595    /**
596     * Creates a new Array with all sub-array elements concatenated
597     * into it recursively up to the specified depth.
598     *
599     * @param depth
600     *
601     * @returns a flattened Array with respect to depth
602     */
603    public flat<U>(depth: number): Array<U> {
604        return this.flat<U>(depth as int)
605    }
606
607    /**
608     * Creates a new Array with all sub-array elements concatenated
609     * into it recursively up to the specified depth.
610     *
611     * @param depth
612     *
613     * @returns a flattened Array with respect to depth
614     */
615    public flat<U>(depth: int): Array<U> {
616        let ret = new Array<U>()
617        this.flatImpl<U>(depth, ret)
618        return ret
619    }
620
621    private flatImpl<U>(depth: int, to: Array<U>) {
622        throw new Error("not implemented")
623    }
624
625    /**
626     * Creates a new Array with all sub-array elements concatenated
627     *
628     * @returns a flattened Array
629     */
630    public flat<U>(): Array<U> {
631        return this.flat<U>(1)
632    }
633
634    /**
635     * Applies flat and than map
636     *
637     * fn a function to apply
638     *
639     * @return new Array after map and than flat
640     */
641    // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
642    public flatMap<U>(fn: (v: T, k: number, arr: Array<T>) => U): Array<U> {
643        let mapped: Array<U> = this.map<U>(fn)
644        return mapped.flat<U>()
645    }
646
647    /**
648     * Applies flat and than map
649     *
650     * fn a function to apply
651     *
652     * @return new Array after map and than flat
653     */
654     // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
655    public flatMap<U>(fn: (v: T, k: number) => U): Array<U> {
656        let mapped: Array<U> = this.map<U>(fn)
657        return mapped.flat<U>()
658    }
659
660    /**
661     * Applies flat and than map
662     *
663     * fn a function to apply
664     *
665     * @return new Array after map and than flat
666     */
667    // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
668    public flatMap<U>(fn: (v: T) => U): Array<U> {
669        let mapped: Array<U> = this.map<U>(fn)
670        return mapped.flat<U>()
671    }
672
673    // === methods common among all arrays ===
674% require 'ostruct'
675% ctx = OpenStruct.new
676% $ctx = ctx
677% ctx.this = 'this'
678% ctx.this_arg = ''
679% ctx.this_len_int = 'this.actualLength'
680% ctx.array_len_int = Proc.new { |v| "#{v}.actualLength" }
681% ctx.access_public = 'public'
682% ctx.access_private = 'private'
683% ctx.override = 'override'
684% ctx.override_expected_here = '' # hide 'override' when it cannot be implemented
685% ctx.get_unsafe = Proc.new { |t, i| "#{t}.$_get_unsafe(#{i})" }
686% ctx.set_unsafe = Proc.new { |t, i, v| "#{t}.$_set_unsafe(#{i}, #{v})" }
687% ctx.el_type = 'T'
688% ctx.el_type_boxed = 'T'
689% ctx.this_type = 'Array<T>'
690% ctx.this_return_type = 'this'
691% ctx.clone_this = 'new Array<T>(FROM_BUFFER, this.copyArray())'
692% ctx.make_buffer = Proc.new { |l, elt|
693%   "new NullishType[#{l}]"
694% }
695% ctx.from_buffer = Proc.new { |b, elt|
696%   elt ||= ctx.el_type
697%   "new Array<#{elt}>(FROM_BUFFER, #{b})"
698% }
699% ctx.array_of_type = Proc.new { |t| "Array<#{t}>" }
700% ctx.this_call = Proc.new { |f| "this.#{f}(" }
701% ctx.arr_method_call = Proc.new { |t, f| "#{t}.#{f}(" }
702% ctx.this_generic = ''
703% ctx.this_generic_one = ''
704% ctx.this_iterator_generic = '<T>'
705% template = ERB.new(File.read("Array_common.erb"), nil, '%', eoutvar: '_sub01')
706<%= template.result(ctx.instance_eval { binding }).gsub(/^/, '    ') %>
707% template = ERB.new(File.read("Array_map.erb"), nil, '%', eoutvar: '_sub02')
708% ctx.mapped_type = 'U'
709% ctx.map_generic = '<U>'
710<%= template.result(ctx.instance_eval { binding }).gsub(/^/, '    ') %>
711}
712% template = ERB.new(File.read("Array_common_top_scope.erb"), nil, '%', eoutvar: '_sub03')
713<%= template.result(ctx.instance_eval { binding }).rstrip %>
714