// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --harmony-change-array-by-copy

assertEquals(0, Array.prototype.toReversed.length);
assertEquals("toReversed", Array.prototype.toReversed.name);

(function TestSmiPacked() {
  let a = [1,2,3,4];
  let r = a.toReversed();
  assertEquals([1,2,3,4], a);
  assertEquals([4,3,2,1], r);
  assertFalse(a === r);
})();

(function TestDoublePacked() {
  let a = [1.1,2.2,3.3,4.4];
  let r = a.toReversed();
  assertEquals([1.1,2.2,3.3,4.4], a);
  assertEquals([4.4,3.3,2.2,1.1], r);
  assertFalse(a === r);
})();

(function TestPacked() {
  let a = [true,false,1,42.42];
  let r = a.toReversed();
  assertEquals([true,false,1,42.42], a);
  assertEquals([42.42,1,false,true], r);
  assertFalse(a === r);
})();

(function TestGeneric() {
  let a = { length: 4,
            get "0"() { return "hello"; },
            get "1"() { return "cursed"; },
            get "2"() { return "java"; },
            get "3"() { return "script" } };
  let r = Array.prototype.toReversed.call(a);
  assertEquals("hello", a[0]);
  assertEquals(["script","java","cursed","hello"], r);
  assertFalse(a === r);
  assertTrue(Array.isArray(r));
  assertEquals(Array, r.constructor);
})();

(function TestReadOrder() {
  let order = [];
  let a = { length: 4,
            get "0"() { order.push("4th"); return "4th"; },
            get "1"() { order.push("3rd"); return "3rd"; },
            get "2"() { order.push("2nd"); return "2nd"; },
            get "3"() { order.push("1st"); return "1st"; } };
  let r = Array.prototype.toReversed.call(a);
  assertEquals(["1st","2nd","3rd","4th"], r);
  assertEquals(["1st","2nd","3rd","4th"], order);
})();

(function TestTooBig() {
  let a = { length: Math.pow(2, 32) };
  assertThrows(() => Array.prototype.toReversed.call(a), RangeError);
})();

(function TestNoSpecies() {
  class MyArray extends Array {
    static get [Symbol.species]() { return MyArray; }
  }
  assertEquals(Array, (new MyArray()).toReversed().constructor);
})();

// All tests after this have an invalidated elements-on-prototype protector.
(function TestNoHoles() {
  let a = [,,,,];
  Array.prototype[3] = "on proto";
  let r = a.toReversed();
  assertEquals(["on proto",undefined,undefined,undefined], r);
  for (let i = 0; i < a.length; i++) {
    assertFalse(a.hasOwnProperty(i));
    assertTrue(r.hasOwnProperty(i));
  }
})();

(function TestMidIterationShenanigans() {
  let a = { length: 4,
            "0": 1,
            get "1"() { a.length = 1; return 2; },
            "2": 3,
            "3": 4,
            __proto__: Array.prototype };
  // The length is cached before iteration, so mid-iteration resizing does not
  // affect the copied array length.
  let r = a.toReversed();
  assertEquals(1, a.length);
  assertEquals([4,3,2,1], r);

  // Values can be changed mid-iteration.
  a = { length: 4,
        "0": 1,
        get "1"() { a[0] = "poof"; return 2; },
        "2": 3,
        "3": 4,
        __proto__: Array.prototype };
  r = a.toReversed();
  assertEquals([4,3,2,"poof"], r);
})();

assertEquals(Array.prototype[Symbol.unscopables].toReversed, true);