/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

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

/*
 * In strict mode, it is a syntax error for an identifier to appear
 * more than once in a function's argument list.
 */

/*
 * The parameters of ordinary function definitions should not contain
 * duplicate identifiers.
 */
assertEq(testLenientAndStrict('function f(x,y) {}',
                              parsesSuccessfully,
                              parsesSuccessfully),
         true);
assertEq(testLenientAndStrict('function f(x,x) {}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f(x,y,z,y) {}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);

/* Exercise the hashed local name map case. */
assertEq(testLenientAndStrict('function f(a,b,c,d,e,f,g,h,d) {}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);

/*
 * SpiderMonkey has always treated duplicates in destructuring
 * patterns as an error. Strict mode should not affect this.
 */
assertEq(testLenientAndStrict('function f([x,y]) {}',
                              parsesSuccessfully,
                              parsesSuccessfully),
         true);
assertEq(testLenientAndStrict('function f([x,x]){}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f(x,[x]){}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);

/*
 * Strict rules apply to the parameters if the function's body is
 * strict.
 */
assertEq(testLenientAndStrict('function f(x,x) { "use strict" };',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);

/*
 * Calls to the function constructor should not be affected by the
 * strictness of the calling code, but should be affected by the
 * strictness of the function body.
 */
assertEq(testLenientAndStrict('Function("x","x","")',
                              completesNormally,
                              completesNormally),
         true);
assertEq(testLenientAndStrict('Function("x","y","")',
                              completesNormally,
                              completesNormally),
         true);
assertEq(testLenientAndStrict('Function("x","x","\'use strict\'")',
                              raisesException(SyntaxError),
                              raisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('Function("x","y","\'use strict\'")',
                              completesNormally,
                              completesNormally),
         true);


/*
 * The parameter lists of getters and setters in object literals
 * should not contain duplicate identifiers.
 */
assertEq(testLenientAndStrict('({get x(y,y) {}})',
                               parsesSuccessfully,
                               parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x(y,y) { "use strict"; }})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x(y,y) {}})',
                               parsesSuccessfully,
                               parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x(y,y) { "use strict"; }})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);

/*
 * The parameter lists of function expressions should not contain
 * duplicate identifiers.
 */
assertEq(testLenientAndStrict('(function (x,x) 2)',
                               parsesSuccessfully,
                               parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function (x,y) 2)',
                              parsesSuccessfully,
                              parsesSuccessfully),
         true);

/*
 * All permutations of:
 * - For the two magic identifiers 'arguments' or 'eval'
 *   - For function definitions, function expressions, expression closures,
 *     and getter and setter property definitions,
 *     - For forms that inherit their context's strictness, and forms that
 *       include their own strictness directives,
 *       - For ordinary parameters, array destructuring parameters, and 
 *         object destructuring parameters,
 *         - the magic identifiers may be used to name such parameters
 *           in lenient code, but not in strict code
 *       - the magic identifiers may be used as function names in lenient code,
 *         but not in strict code
 */
assertEq(testLenientAndStrict('function f(eval){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f([eval]){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f({x:eval}){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function eval(){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f(eval){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f([eval]){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f({x:eval}){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function eval(){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f(eval){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f([eval]){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f({x:eval}){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function eval(){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f(eval){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f([eval]){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f({x:eval}){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function eval(){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f(eval) 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f([eval]) 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f({x:eval}) 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function eval() 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x(eval){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x([eval]){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x({x:eval}){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x(eval){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x([eval]){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x({x:eval}){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x(eval){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x([eval]){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x({x:eval}){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x(eval){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x([eval]){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x({x:eval}){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f(arguments){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f([arguments]){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f({x:arguments}){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function arguments(){}',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f(arguments){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f([arguments]){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function f({x:arguments}){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('function arguments(){"use strict";}',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f(arguments){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f([arguments]){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f({x:arguments}){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function arguments(){})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f(arguments){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f([arguments]){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f({x:arguments}){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function arguments(){"use strict";})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f(arguments) 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f([arguments]) 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function f({x:arguments}) 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('(function arguments() 2)',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x(arguments){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x([arguments]){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x({x:arguments}){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x(arguments){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x([arguments]){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({get x({x:arguments}){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x(arguments){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x([arguments]){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x({x:arguments}){}})',
                              parsesSuccessfully,
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x(arguments){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x([arguments]){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('({set x({x:arguments}){"use strict";}})',
                              parseRaisesException(SyntaxError),
                              parseRaisesException(SyntaxError)),
         true);

/*
 * Functions produced using the Function constructor may not use
 * 'eval' or 'arguments' as a parameter name if their body is strict
 * mode code. The strictness of the calling code does not affect the
 * constraints applied to the parameters.
 */
assertEq(testLenientAndStrict('Function("eval","")',
                              completesNormally,
                              completesNormally),
         true);
assertEq(testLenientAndStrict('Function("eval","\'use strict\';")',
                              raisesException(SyntaxError),
                              raisesException(SyntaxError)),
         true);
assertEq(testLenientAndStrict('Function("arguments","")',
                              completesNormally,
                              completesNormally),
         true);
assertEq(testLenientAndStrict('Function("arguments","\'use strict\';")',
                              raisesException(SyntaxError),
                              raisesException(SyntaxError)),
         true);


reportCompare(true, true);
