// Copyright 2017 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: --allow-natives-syntax

// Test Array subclass default constructor with no parameters.
(function() {
  const A = class A extends Array { };

  function foo() { return new A; }

  assertInstanceof(foo(), A);
  assertEquals(0, foo().length);
  assertInstanceof(foo(), A);
  assertEquals(0, foo().length);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(0, foo().length);
})();

// Test Array subclass default constructor with small constant length.
(function() {
  const A = class A extends Array { };
  const L = 4;

  function foo() { return new A(L); }

  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
})();

// Test Array subclass default constructor with large constant length.
(function() {
  const A = class A extends Array { };
  const L = 1024 * 1024;

  function foo() { return new A(L); }

  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
})();

// Test Array subclass default constructor with known boolean.
(function() {
  const A = class A extends Array { };

  function foo() { return new A(true); }

  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals(true, foo()[0]);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals(true, foo()[0]);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals(true, foo()[0]);
})();

// Test Array subclass default constructor with known string.
(function() {
  const A = class A extends Array { };

  function foo() { return new A(""); }

  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals("", foo()[0]);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals("", foo()[0]);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals("", foo()[0]);
})();

// Test Array subclass default constructor with known object.
(function() {
  const A = class A extends Array { };
  const O = {foo: "foo"};

  function foo() { return new A(O); }

  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertSame(O, foo()[0]);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertSame(O, foo()[0]);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertSame(O, foo()[0]);
})();

// Test Array subclass default constructor with known small integers.
(function() {
  const A = class A extends Array { };

  function foo() { return new A(1, 2, 3); }

  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1, foo()[0]);
  assertEquals(2, foo()[1]);
  assertEquals(3, foo()[2]);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1, foo()[0]);
  assertEquals(2, foo()[1]);
  assertEquals(3, foo()[2]);
})();

// Test Array subclass default constructor with known numbers.
(function() {
  const A = class A extends Array { };

  function foo() { return new A(1.1, 2.2, 3.3); }

  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1.1, foo()[0]);
  assertEquals(2.2, foo()[1]);
  assertEquals(3.3, foo()[2]);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1.1, foo()[0]);
  assertEquals(2.2, foo()[1]);
  assertEquals(3.3, foo()[2]);
})();

// Test Array subclass default constructor with known strings.
(function() {
  const A = class A extends Array { };

  function foo() { return new A("a", "b", "c", "d"); }

  assertInstanceof(foo(), A);
  assertEquals(4, foo().length);
  assertEquals("a", foo()[0]);
  assertEquals("b", foo()[1]);
  assertEquals("c", foo()[2]);
  assertEquals("d", foo()[3]);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(4, foo().length);
  assertEquals("a", foo()[0]);
  assertEquals("b", foo()[1]);
  assertEquals("c", foo()[2]);
  assertEquals("d", foo()[3]);
})();

// Test Array subclass constructor with no parameters.
(function() {
  const A = class A extends Array {
    constructor() {
      super();
      this.bar = 1;
    }
  };

  function foo() { return new A; }

  assertInstanceof(foo(), A);
  assertEquals(0, foo().length);
  assertEquals(1, foo().bar);
  assertInstanceof(foo(), A);
  assertEquals(0, foo().length);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(0, foo().length);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with small constant length.
(function() {
  const A = class A extends Array {
    constructor(n) {
      super(n);
      this.bar = 1;
    }
  };
  const L = 4;

  function foo() { return new A(L); }

  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertEquals(1, foo().bar);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with large constant length.
(function() {
  const A = class A extends Array {
    constructor(n) {
      super(n);
      this.bar = 1;
    }
  };
  const L = 1024 * 1024;

  function foo() { return new A(L); }

  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertEquals(1, foo().bar);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(L, foo().length);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with known boolean.
(function() {
  const A = class A extends Array {
    constructor(n) {
      super(n);
      this.bar = 1;
    }
  };

  function foo() { return new A(true); }

  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals(true, foo()[0]);
  assertEquals(1, foo().bar);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals(true, foo()[0]);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals(true, foo()[0]);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with known string.
(function() {
  const A = class A extends Array {
    constructor(n) {
      super(n);
      this.bar = 1;
    }
  };

  function foo() { return new A(""); }

  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals("", foo()[0]);
  assertEquals(1, foo().bar);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals("", foo()[0]);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertEquals("", foo()[0]);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with known object.
(function() {
  const A = class A extends Array {
    constructor(n) {
      super(n);
      this.bar = 1;
    }
  };
  const O = {foo: "foo"};

  function foo() { return new A(O); }

  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertSame(O, foo()[0]);
  assertEquals(1, foo().bar);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertSame(O, foo()[0]);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(1, foo().length);
  assertSame(O, foo()[0]);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with known small integers.
(function() {
  const A = class A extends Array {
    constructor(x, y, z) {
      super(x, y, z);
      this.bar = 1;
    }
  };

  function foo() { return new A(1, 2, 3); }

  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1, foo()[0]);
  assertEquals(2, foo()[1]);
  assertEquals(3, foo()[2]);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1, foo()[0]);
  assertEquals(2, foo()[1]);
  assertEquals(3, foo()[2]);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with known numbers.
(function() {
  const A = class A extends Array {
    constructor(x, y, z) {
      super(x, y, z);
      this.bar = 1;
    }
  };

  function foo() { return new A(1.1, 2.2, 3.3); }

  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1.1, foo()[0]);
  assertEquals(2.2, foo()[1]);
  assertEquals(3.3, foo()[2]);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(3, foo().length);
  assertEquals(1.1, foo()[0]);
  assertEquals(2.2, foo()[1]);
  assertEquals(3.3, foo()[2]);
  assertEquals(1, foo().bar);
})();

// Test Array subclass constructor with known strings.
(function() {
  const A = class A extends Array {
    constructor(a, b, c, d) {
      super(a, b, c, d);
      this.bar = 1;
    }
  };

  function foo() { return new A("a", "b", "c", "d"); }

  assertInstanceof(foo(), A);
  assertEquals(4, foo().length);
  assertEquals("a", foo()[0]);
  assertEquals("b", foo()[1]);
  assertEquals("c", foo()[2]);
  assertEquals("d", foo()[3]);
  assertEquals(1, foo().bar);
  %OptimizeFunctionOnNextCall(foo);
  assertInstanceof(foo(), A);
  assertEquals(4, foo().length);
  assertEquals("a", foo()[0]);
  assertEquals("b", foo()[1]);
  assertEquals("c", foo()[2]);
  assertEquals("d", foo()[3]);
  assertEquals(1, foo().bar);
})();
