// |jit-test| skip-variant-if: --ion-eager, getBuildConfiguration("simulator")

// Slow in simulators with --ion-eager.

const byteLengths = [
  // Byte lengths applicable for inline storage
  0, 1, 4, 16, 32, 64, 96,

  // Too large for inline storage
  128, 1024,
];

// Most values in |byteLengths| are powers-of-two. Add +1/-1 to test for
// off-by-one errors.
function offByOne(v) {
  if (v === 0) {
    return [v, v + 1];
  }
  return [v - 1, v, v + 1];
}

// Fast alternative to |assertEq| to avoid a slow VM-call.
// Should only be used when looping over the TypedArray contents to avoid
// unnecessary test slowdowns.
function assertEqNum(e, a) {
  if (e !== a) {
    assertEq(e, a);
  }
}

// Inline or malloced ArrayBuffer.
{
  for (let byteLength of byteLengths.flatMap(offByOne)) {
    for (let newByteLength of offByOne(byteLength)) {
      for (let transfer of [
        ArrayBuffer.prototype.transfer,
        ArrayBuffer.prototype.transferToFixedLength,
      ]) {
        let buffer = new ArrayBuffer(byteLength);
        let i8 = new Uint8Array(buffer);
        for (let i = 0; i < byteLength; ++i) {
          assertEqNum(i8[i], 0);
          i8[i] = i;
        }

        assertEq(buffer.byteLength, byteLength);
        assertEq(buffer.detached, false);

        let copy = transfer.call(buffer, newByteLength);

        assertEq(buffer.byteLength, 0);
        assertEq(buffer.detached, true);

        assertEq(copy.byteLength, newByteLength);
        assertEq(copy.detached, false);

        i8 = new Uint8Array(copy);
        for (let i = 0; i < Math.min(byteLength, newByteLength); ++i) {
          assertEqNum(i8[i], i & 0xff);
        }
        for (let i = byteLength; i < newByteLength; ++i) {
          assertEqNum(i8[i], 0);
        }
      }
    }
  }
}

// External buffer.
{
  for (let byteLength of byteLengths.flatMap(offByOne)) {
    for (let newByteLength of offByOne(byteLength)) {
      for (let transfer of [
        ArrayBuffer.prototype.transfer,
        ArrayBuffer.prototype.transferToFixedLength,
      ]) {
        let buffer = createExternalArrayBuffer(byteLength);
        let i8 = new Uint8Array(buffer);
        for (let i = 0; i < byteLength; ++i) {
          assertEqNum(i8[i], 0);
          i8[i] = i;
        }

        assertEq(buffer.byteLength, byteLength);
        assertEq(buffer.detached, false);

        let copy = transfer.call(buffer, newByteLength);

        assertEq(buffer.byteLength, 0);
        assertEq(buffer.detached, true);

        assertEq(copy.byteLength, newByteLength);
        assertEq(copy.detached, false);

        i8 = new Uint8Array(copy);
        for (let i = 0; i < Math.min(byteLength, newByteLength); ++i) {
          assertEqNum(i8[i], i & 0xff);
        }
        for (let i = byteLength; i < newByteLength; ++i) {
          assertEqNum(i8[i], 0);
        }
      }
    }
  }
}

// User-controlled buffer.
{
  for (let byteLength of byteLengths.flatMap(offByOne)) {
    for (let newByteLength of offByOne(byteLength)) {
      for (let transfer of [
        ArrayBuffer.prototype.transfer,
        ArrayBuffer.prototype.transferToFixedLength,
      ]) {
        let buffer = createUserArrayBuffer(byteLength);
        let i8 = new Uint8Array(buffer);
        for (let i = 0; i < byteLength; ++i) {
          assertEqNum(i8[i], 0);
          i8[i] = i;
        }

        assertEq(buffer.byteLength, byteLength);
        assertEq(buffer.detached, false);

        let copy = transfer.call(buffer, newByteLength);

        assertEq(buffer.byteLength, 0);
        assertEq(buffer.detached, true);

        assertEq(copy.byteLength, newByteLength);
        assertEq(copy.detached, false);

        i8 = new Uint8Array(copy);
        for (let i = 0; i < Math.min(byteLength, newByteLength); ++i) {
          assertEqNum(i8[i], i & 0xff);
        }
        for (let i = byteLength; i < newByteLength; ++i) {
          assertEqNum(i8[i], 0);
        }
      }
    }
  }
}

// Mapped file buffer.
if (getBuildConfiguration("mapped-array-buffer")) {
  let fileName = "arraybuffer-transfer-mapped.txt";
  let fileContent = os.file.readRelativeToScript(fileName, "binary");

  let byteLength = fileContent.byteLength;
  assertEq(byteLength > 0, true);

  for (let newByteLength of offByOne(byteLength)) {
    for (let transfer of [
      ArrayBuffer.prototype.transfer,
      ArrayBuffer.prototype.transferToFixedLength,
    ]) {
      let buffer = createMappedArrayBuffer(fileName);

      assertEq(buffer.byteLength, byteLength);
      assertEq(buffer.detached, false);

      let copy = transfer.call(buffer, newByteLength);

      assertEq(buffer.byteLength, 0);
      assertEq(buffer.detached, true);

      assertEq(copy.byteLength, newByteLength);
      assertEq(copy.detached, false);

      i8 = new Uint8Array(copy);
      for (let i = 0; i < Math.min(byteLength, newByteLength); ++i) {
        assertEqNum(i8[i], fileContent[i]);
      }
      for (let i = byteLength; i < newByteLength; ++i) {
        assertEqNum(i8[i], 0);
      }
    }
  }
}

// Cross-compartment
{
  let g = newGlobal({ newCompartment: true });

  let byteLengthGetter = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
  let detachedGetter = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "detached").get;

  for (let byteLength of byteLengths) {
    for (let transfer of [
      ArrayBuffer.prototype.transfer,
      ArrayBuffer.prototype.transferToFixedLength,
    ]) {
      let buffer = new g.ArrayBuffer(byteLength);

      assertEq(byteLengthGetter.call(buffer), byteLength);
      assertEq(detachedGetter.call(buffer), false);

      let copy = transfer.call(buffer);

      assertEq(detachedGetter.call(buffer), true);
      assertEq(byteLengthGetter.call(buffer), 0);

      assertEq(detachedGetter.call(copy), false);
      assertEq(byteLengthGetter.call(copy), byteLength);

      // NOTE: Incorrect realm due to CallNonGenericMethod.
      assertEq(copy instanceof g.ArrayBuffer, true);
    }
  }
}
