/*
 * Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/licenses/publicdomain/
 */

var gTestfile = "for-loop.js";
//-----------------------------------------------------------------------------
var BUGNUMBER = 985733;
var summary =
  "ES6 for-loop semantics for for(;;) loops whose heads contain lexical "
  "declarations";

print(BUGNUMBER + ": " + summary);

/**************
 * BEGIN TEST *
 **************/

function isError(code, type)
{
  try
  {
    Function(code);
    throw new Error("didn't throw");
  }
  catch (e)
  {
    assertEq(e instanceof type, true,
             "unexpected error for `" + code + "`: got " + e);
  }
}

function isOK(code)
{
  Function(code);
}

isError("for (const x; ; ) ;", SyntaxError);
isError("for (const x = 5, y; ; ) ;", SyntaxError);
isError("for (const [z]; ; ) ;", SyntaxError);
//isError("for (const [z, z]; ; ) ;", SyntaxError);
//isError("for (const [z, z] = [0, 1]; ; ) ;", SyntaxError);

isOK("for (let x; ; ) ;");
isOK("for (let x = 5, y; ; ) ;");

// I'm fairly sure this is supposed to work: the negative-lookahead rules in
// IterationStatement ensure that |for (let| *always* is a loop header starting
// with a lexical declaration.  But I'm not 100% certain, so these tests might
// need to be fixed when we implement the negative-lookahead restrictions.
isOK("for (let [z] = [3]; ; ) ;");
isError("for (let [z, z]; ; ) ;", SyntaxError); // because missing initializer

// This is wrong!  Per 13.2.1.1, "It is a Syntax Error if the BoundNames of
// BindingList contains any duplicate entries."  But we don't implement this
// yet, so it becomes a TypeError at runtime.
isError("for (let [z, z] = [0, 1]; ; ) ;", TypeError);

// A for-loop with lexical declarations, with a mixture of bindings that are and
// aren't aliased.  (The mixture stress-tests any code that incorrectly assumes
// all bindings are aliased.)
var funcs = [];
for (let [i, j, k] = [0, 1, 2]; i < 10; i++)
  funcs.push(() => i);

assertEq(funcs[0](), 0);
assertEq(funcs[1](), 1);
assertEq(funcs[2](), 2);
assertEq(funcs[3](), 3);
assertEq(funcs[4](), 4);
assertEq(funcs[5](), 5);
assertEq(funcs[6](), 6);
assertEq(funcs[7](), 7);
assertEq(funcs[8](), 8);
assertEq(funcs[9](), 9);

var outer = "OUTER V IGNORE";
var save;
for (let outer = (save = function() { return outer; }); ; )
  break;
assertEq(save(), "OUTER V IGNORE",
         "this is actually a bug: fix for(;;) loops to evaluate init RHSes " +
         "in the block scope containing all the LHS bindings!");



var funcs = [];
function t(i, name, expect)
{
  assertEq(funcs[i].name, name);
  assertEq(funcs[i](), expect);
}

if (save() !== "OUTER V IGNORE")
{
  var v = "OUTER V IGNORE";
  var i = 0;
  for (let v = (funcs.push(function init() { return v; }),
               0);
      v = (funcs.push(function test() { return v; }),
           v + 1);
      v = (funcs.push(function incr() { return v; }),
           v + 1))
  {
    v = (funcs.push(function body() { return v; }),
         v + 1);
    i++;
    if (i >= 3)
      break;
  }
  t(0, "init", 2);
  t(1, "test", 2);
  t(2, "body", 2);
  t(3, "incr", 5);
  t(4, "test", 5);
  t(5, "body", 5);
  t(6, "incr", 8);
  t(7, "test", 8);
  t(8, "body", 8);
  assertEq(funcs.length, 9);
}

/******************************************************************************/

if (typeof reportCompare === "function")
  reportCompare(true, true);

print("Tests complete");
