/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//-----------------------------------------------------------------------------
var BUGNUMBER     = "336376";
var summary = "Tests reserved words in contexts in which they are not reserved";
var actual, expect;

printBugNumber(BUGNUMBER);
printStatus(summary);

/**************
 * TEST SETUP *
 **************/

//
// New tests go in Tester.prototype._tests.  A test is called with a single
// argument, the keyword to test in the syntax tested by that test.  Tests
// should not return anything, and they should signal failure by throwing an
// explanatory exception and success by not throwing one.
//
// If you define a new test, make sure to name it using an informative string
// for ease of use if any keywords ever manually define the array of tests they
// should pass, and add it as a string to ALL_TESTS.
//

// all tests
const ALL_TESTS =
  [
    "CONTEXT_OBJECT_LITERAL_PROPERTY",
    "CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE",
    "CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE_IS_FUNCTION",
    "CONTEXT_OBJECT_PROPERTY_DOT_GET",
    "CONTEXT_OBJECT_PROPERTY_DOT_SET",
    "CONTEXT_XML_DESCENDANTS",
    "CONTEXT_XML_NAMESPACE_QUALIFIED_ELEMENT",
    "CONTEXT_XML_NAMESPACE_QUALIFIED_ATTR",
    "CONTEXT_XML_ATTRIBUTE_SELECTOR",
    ];

function r(keyword, tests)
{
  /**
   * @param keyword
   *   the keyword as a string
   * @param tests
   *   array of test numbers against it, or leave undefined to run all tests
   *   against it
   */
  function Reserved(keyword, tests)
  {
    this.keyword = keyword;
    if (tests)
      this.tests = tests;
    else
      this.tests = ALL_TESTS;
  }
  Reserved.prototype =
    {
      toString:
      function()
      {
	return "'" + this.keyword + "' being run against tests " +
	this.tests;
      }
    };
  return new Reserved(keyword, tests);
}

// ECMA-262, 3rd. ed. keywords -- see 7.5.2
const ECMA_262_3_KEYWORD =
  [
    r("break"),
    r("case"),
    r("catch"),
    r("continue"),
    r("default"),
    r("delete"),
    r("do"),
    r("else"),
    r("finally"),
    r("for"),
    r("function"),
    r("if"),
    r("in"),
    r("instanceof"),
    r("new"),
    r("return"),
    r("switch"),
    r("this"),
    r("throw"),
    r("try"),
    r("typeof"),
    r("var"),
    r("void"),
    r("while"),
    r("with"),
    ];

// ECMA-262, 3rd. ed. future reserved keywords -- see 7.5.3
const ECMA_262_3_FUTURERESERVEDKEYWORD =
  [
    r("abstract"),
    r("boolean"),
    r("byte"),
    r("char"),
    r("class"),
    r("const"),
    r("debugger"),
    r("double"),
    r("enum"),
    r("export"),
    r("extends"),
    r("final"),
    r("float"),
    r("goto"),
    r("implements"),
    r("import"),
    r("int"),
    r("interface"),
    r("long"),
    r("native"),
    r("package"),
    r("private"),
    r("protected"),
    r("public"),
    r("short"),
    r("static"),
    r("super"),
    r("synchronized"),
    r("throws"),
    r("transient"),
    r("volatile"),
    ];

// like reserved words, but not quite reserved words
const PSEUDO_RESERVED =
  [
    r("true"),
    r("false"),
    r("null"),
    r("each"),  // |for each|
    ];

// new-in-ES4 reserved words -- fill this as each is implemented
const ECMA_262_4_RESERVED_WORDS =
  [
    r("let")
    ];



/**
 * @param keyword
 *   string containing the tested keyword
 * @param test
 *   the number of the failing test
 * @param error
 *   the exception thrown when running the test
 */
function Failure(keyword, test, error)
{
  this.keyword = keyword;
  this.test = test;
  this.error = error;
}
Failure.prototype =
{
  toString:
  function()
  {
    return "*** FAILURE on '" + this.keyword + "'!\n" +
    "* test:     " + this.test + "\n" +
    "* error:    " + this.error + "\n";
  }
};

function Tester()
{
  this._failedTests = [];
}
Tester.prototype =
{
  testReservedWords:
  function(reservedArray)
  {
    var rv;
    for (var i = 0, sz = reservedArray.length; i < sz; i++)
    {
      var res = reservedArray[i];
      if (!res)
	continue;

      var tests = res.tests;
      for (var j = 0, sz2 = tests.length; j < sz2; j++)
      {
	var test = tests[j];
	if (!test)
	  continue;

	try
	{
	  this._tests[test](res.keyword);
	}
	catch (e)
	{
	  this._failedTests.push(new Failure(res.keyword, test, e));
	}
      }
    }
  },
  flushErrors:
  function ()
  {
    if (this._failedTests.length > 0) {
      var except = "*************************\n" +
      "* FAILURES ENCOUNTERED! *\n" +
      "*************************\n";
      for (var i = 0, sz = this._failedTests.length; i < sz; i++)
	except += this._failedTests[i];
      throw except;
    }
  },
  _tests:
  {
    CONTEXT_OBJECT_LITERAL_PROPERTY:
    function(keyword)
    {
      try
      {
	eval("var o = { " + keyword + ": 17 };\n" +
	     "if (o['" + keyword + "'] != 17)\n" +
	     "throw \"o['" + keyword + "'] == 17\";");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE:
    function(keyword)
    {
      try
      {
	eval("var o = { \"" + keyword + "\": 17, baz: null };\n" +
	     "if (o." + keyword + " != 17)\n" +
	     "throw \"o." + keyword + " == 17\";");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE_IS_FUNCTION:
    function(keyword)
    {
      try
      {
	eval("var o = { '" + keyword + "': function() { return 17; }, baz: null };\n" +
	     "if (o." + keyword + "() != 17)\n" +
	     "throw \"o." + keyword + " == 17\";");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_OBJECT_PROPERTY_DOT_GET:
    function(keyword)
    {
      try
      {
	var o = {};
	eval("o['" + keyword + "'] = 17;\n" +
	     "if (o." + keyword + " != 17)\n" +
	     "throw \"'o." + keyword + " != 17' failed!\";");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_OBJECT_PROPERTY_DOT_SET:
    function(keyword)
    {
      try
      {
	var o = {};
	eval("o." + keyword + " = 17;\n" +
	     "if (o['" + keyword + "'] != 17)\n" +
	     "throw \"'o." + keyword + " = 17' failed!\";");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_XML_DESCENDANTS:
    function(keyword)
    {
      try
      {
	eval("var x = <foo><biz><" + keyword + " id='1'/></biz><" + keyword + " f='g'/></foo>;\n" +
	     "if (x.." + keyword + ".length() != 2 ||\n" +
	     "    x.." + keyword + " !=              \n" +
	     "      <><" + keyword + " id='1'/><" + keyword + " f='g'/></>)\n" +
	     "throw \"'x.." + keyword + ".length()' failed!\";");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_XML_NAMESPACE_QUALIFIED_ELEMENT:
    function(keyword)
    {
      try
      {
	var bar = new Namespace("http://localhost/");
	eval("var x = <foo xmlns:bar='http://localhost/'>\n\
                              <bar>\n\
                                <baz/>\n\
                              </bar>\n\
                              <bar:" + keyword + " id='17'/>\n\
                              <quiz>\n\
                                <bar:" + keyword + ">\n\
                                  <bunk/>\n\
                                </bar:" + keyword + ">\n\
                              </quiz>\n\
                            </foo>;\n\
                    if (x.quiz.bar::" + keyword + " != \n\
                          <bar:" + keyword + "  xmlns:bar='http://localhost/'>\n\
                            <bunk/>\n\
                          </bar:" + keyword + "> ||\n\
                        x..bar::" + keyword + " != \n\
                            <><bar:" + keyword + "\n\
                                xmlns:bar='http://localhost/' id='17'/>\n\
                              <bar:" + keyword + " xmlns:bar='http://localhost/'>\n\
                                <bunk/>\n\
                              </bar:" + keyword + "></>)\n\
                      throw 'reserved names in XML namespace-qualified stuff are broken!';");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_XML_NAMESPACE_QUALIFIED_ATTR:
    function(keyword)
    {
      try
      {
	var bar = new Namespace("http://localhost/");
	eval("var x = <foo xmlns:bar='http://localhost/'>\
                              <fin bar:" + keyword + "='5'/>\
                            </foo>;\n\
                    if (x.fin.@bar::" + keyword + " != 5)\n\
                      throw 'namespaced attributes which are keywords are broken!';");
      }
      catch (e)
      {
	throw e;
      }
    },
    CONTEXT_XML_ATTRIBUTE_SELECTOR:
    function(keyword)
    {
      try
      {
	eval("var x = <foo " + keyword + "='idref'/>;\n\
                    if (x.@" + keyword + " != 'idref')\n\
                      throw 'keywords on the right of the @ E4X selector are broken!';");
      }
      catch (e)
      {
	throw e;
      }
    }
  }
};


/***************
 * BEGIN TESTS *
 ***************/

var failed = false;

try
{
  var tester = new Tester();
  tester.testReservedWords(ECMA_262_3_KEYWORD);
  tester.testReservedWords(ECMA_262_3_FUTURERESERVEDKEYWORD);
  tester.testReservedWords(PSEUDO_RESERVED);
  tester.testReservedWords(ECMA_262_4_RESERVED_WORDS);
  tester.flushErrors();
}
catch (e)
{
  failed = e;
}

expect = false;
actual = failed;

reportCompare(expect, actual, summary);
