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