// Test iteration with a mapped arguments object.

function simple() {
  function f() {
    var sum = 0;
    for (var v of arguments) {
      sum += v;
    }
    return sum;
  }

  for (var i = 0; i < 100; ++i) {
    assertEq(f(1, 2, 3), 6);
  }
}
simple();

function spreadCall() {
  function f() {
    var sum = 0;
    for (var v of arguments) {
      sum += v;
    }
    return sum;
  }

  function g() {
    return f(...arguments);
  }

  for (var i = 0; i < 100; ++i) {
    assertEq(g(1, 2, 3), 6);
  }
}
spreadCall();

function spreadArray() {
  function f() {
    var arr = [...arguments];
    var sum = 0;
    for (var v of arr) {
      sum += v;
    }
    return sum;
  }

  for (var i = 0; i < 100; ++i) {
    assertEq(f(1, 2, 3), 6);
  }
}
spreadArray();

function reifyIterator() {
  var reify = false;
  function f() {
    if (reify) {
      // Redefining any property attributes will reify the iterator property.
      Object.defineProperty(arguments, Symbol.iterator, {
        writable: false
      });
    }

    var sum = 0;
    for (var v of arguments) {
      sum += v;
    }
    return sum;
  }

  for (var i = 0; i <= 100; ++i) {
    reify = i >= 50;
    assertEq(f(1, 2, 3), 6);
  }
}
reifyIterator();

function overwriteIterator() {
  var callCount = 0;
  function Iterator() {
    callCount += 1;
    return Array.prototype[Symbol.iterator].call(this);
  }

  var overwrite = false;
  function f() {
    if (overwrite) {
      arguments[Symbol.iterator] = Iterator;
    }

    var sum = 0;
    for (var v of arguments) {
      sum += v;
    }
    return sum;
  }

  for (var i = 0; i <= 100; ++i) {
    overwrite = i > 50;
    assertEq(f(1, 2, 3), 6);
  }
  assertEq(callCount, 50);
}
overwriteIterator();

function deleteIterator() {
  var remove = false;
  function f() {
    // Deleting Symbol.iterator won't change the shape of the arguments object.
    // That's why we need to use a separate guard instruction to check if the
    // iterator property was modified.
    if (remove) {
      delete arguments[Symbol.iterator];
    }

    var sum = 0;
    for (var v of arguments) {
      sum += v;
    }
    return sum;
  }

  var error;
  try {
    for (var i = 0; i <= 100; ++i) {
      remove = i === 100;
      assertEq(f(1, 2, 3), 6);
    }
  } catch (e) {
    error = e;
  }
  assertEq(error instanceof TypeError, true);
}
deleteIterator();

function deleteIteratorInherit() {
  var callCount = 0;
  function Iterator() {
    callCount += 1;
    return Array.prototype[Symbol.iterator].call(this);
  }

  Object.prototype[Symbol.iterator] = Iterator;

  var remove = false;
  function f() {
    // Deleting Symbol.iterator won't change the shape of the arguments object.
    // That's why we need to use a separate guard instruction to check if the
    // iterator property was modified.
    if (remove) {
      delete arguments[Symbol.iterator];
    }

    var sum = 0;
    for (var v of arguments) {
      sum += v;
    }
    return sum;
  }

  for (var i = 0; i <= 100; ++i) {
    remove = i === 100;
    assertEq(f(1, 2, 3), 6);
  }
  assertEq(callCount, 1);

  delete Object.prototype[Symbol.iterator];
}
deleteIteratorInherit();

// Don't add tests below this point because |Object.prototype[Symbol.iterator]|
// was modified, which may lead to engine-wide deoptimisations.