// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* - Duplicate parameters are allowed for - non-arrow functions which are not conscise methods *and* - when the parameter list is simple *and* - we're in sloppy mode (incl. the function doesn't declare itself strict). */ function assertDuplicateParametersError(code) { caught = false; try { eval(code); } catch(e) { // Assert that it's the duplicate parameters error, and e.g,. not a syntax // error because of a typo in the test. assertTrue(e.message.startsWith("Duplicate parameter name not allowed")); caught = true; } finally { assertTrue(caught); } } FunctionType = { NORMAL : 0, ARROW : 1, METHOD : 2, CONCISE_METHOD : 3, }; Laziness = { EAGER : 0, LAZY_BOUNDARY : 1, LAZY : 2 }; Strictness = { SLOPPY : 0, STRICT : 1, STRICT_FUNCTION : 2 }; function testHelper(type, strict, lazy, duplicate_params_string, ok) { code = "" strict_inside = ""; if (strict == Strictness.STRICT) { code = "'use strict'; "; } else if (strict == Strictness.STRICT_FUNCTION) { strict_inside = "'use strict'; "; } else { assertEquals(strict, Strictness.SLOPPY); } if (type == FunctionType.NORMAL) { if (lazy == Laziness.EAGER) { code += "(function foo(" + duplicate_params_string + ") { " + strict_inside + "})"; } else if (lazy == Laziness.LAZY_BOUNDARY) { code += "function foo(" + duplicate_params_string + ") { " + strict_inside + "}"; } else if (lazy == Laziness.LAZY) { code += 'function lazy() { function foo(' + duplicate_params_string + ') { ' + strict_inside + '} }'; } else { assertUnreachable(); } } else if (type == FunctionType.ARROW) { if (lazy == Laziness.EAGER) { // Force an arrow function to be eager by making its body trivial. assertEquals(strict, Strictness.SLOPPY); code += "(" + duplicate_params_string + ") => 1"; } else if (lazy == Laziness.LAZY_BOUNDARY) { // Duplicate parameters in non-simple parameter lists are not recognized // at the laziness boundary, when the lazy function is an arrow // function. Hack around this by calling the function. See // https://bugs.chromium.org/p/v8/issues/detail?id=6108. let simple = /^[a-z, ]*$/.test(duplicate_params_string); if (simple) { code += "(" + duplicate_params_string + ") => { " + strict_inside + "};"; } else { code += "let foo = (" + duplicate_params_string + ") => { " + strict_inside + "}; foo();"; } } else if (lazy == Laziness.LAZY) { // PreParser cannot detect duplicates in arrow function parameters. When // parsing the parameter list, it doesn't know it's an arrow function // parameter list, so it just discards the identifiers, and cannot do the // check any more when it sees the arrow. Work around this by calling the // function which forces parsing it. code += 'function lazy() { (' + duplicate_params_string + ') => { ' + strict_inside + '} } lazy();'; } else { assertUnreachable(); } } else if (type == FunctionType.METHOD) { code += "var o = {"; if (lazy == Laziness.EAGER) { code += "foo : (function(" + duplicate_params_string + ") { " + strict_inside + "})"; } else if (lazy == Laziness.LAZY_BOUNDARY) { code += "foo : function(" + duplicate_params_string + ") { " + strict_inside + "}"; } else if (lazy == Laziness.LAZY) { code += 'lazy: function() { function foo(' + duplicate_params_string + ') { ' + strict_inside + '} }'; } else { assertUnreachable(); } code += "};"; } else if (type == FunctionType.CONCISE_METHOD) { if (lazy == Laziness.LAZY_BOUNDARY) { code += "var o = { foo(" + duplicate_params_string + ") { " + strict_inside + "} };"; } else if (lazy == Laziness.LAZY) { code += 'function lazy() { var o = { foo(' + duplicate_params_string + ') { ' + strict_inside + '} }; }'; } else { assertUnreachable(); } } else { assertUnreachable(); } if (ok) { assertDoesNotThrow(code); } else { assertDuplicateParametersError(code); } } function test(type, strict, lazy, ok_if_param_list_simple) { // Simple duplicate params. testHelper(type, strict, lazy, "dup, dup", ok_if_param_list_simple) if (strict != Strictness.STRICT_FUNCTION) { // Generate test cases where the duplicate parameter occurs because of // destructuring or the rest parameter. That is always an error: duplicate // parameters are only allowed in simple parameter lists. These tests are // not possible if a function declares itself strict, since non-simple // parameters are not allowed then. testHelper(type, strict, lazy, "[dup], dup", false); testHelper(type, strict, lazy, "dup, {a: dup}", false); testHelper(type, strict, lazy, "{dup}, [dup]", false); testHelper(type, strict, lazy, "dup, ...dup", false); testHelper(type, strict, lazy, "dup, dup, ...rest", false); testHelper(type, strict, lazy, "dup, dup, a = 1", false); } } // No duplicate parameters allowed for arrow functions even in sloppy mode. test(FunctionType.ARROW, Strictness.SLOPPY, Laziness.EAGER, false); test(FunctionType.ARROW, Strictness.SLOPPY, Laziness.LAZY_BOUNDARY, false); test(FunctionType.ARROW, Strictness.SLOPPY, Laziness.LAZY, false); // Duplicate parameters allowed for normal functions in sloppy mode. test(FunctionType.NORMAL, Strictness.SLOPPY, Laziness.EAGER, true); test(FunctionType.NORMAL, Strictness.SLOPPY, Laziness.LAZY_BOUNDARY, true); test(FunctionType.NORMAL, Strictness.SLOPPY, Laziness.LAZY, true); test(FunctionType.NORMAL, Strictness.STRICT, Laziness.EAGER, false); test(FunctionType.NORMAL, Strictness.STRICT, Laziness.LAZY_BOUNDARY, false); test(FunctionType.NORMAL, Strictness.STRICT, Laziness.LAZY, false); test(FunctionType.NORMAL, Strictness.STRICT_FUNCTION, Laziness.EAGER, false); test(FunctionType.NORMAL, Strictness.STRICT_FUNCTION, Laziness.LAZY_BOUNDARY, false); test(FunctionType.NORMAL, Strictness.STRICT_FUNCTION, Laziness.LAZY, false); // No duplicate parameters allowed for conscise methods even in sloppy mode. test(FunctionType.CONCISE_METHOD, Strictness.SLOPPY, Laziness.LAZY_BOUNDARY, false); test(FunctionType.CONCISE_METHOD, Strictness.SLOPPY, Laziness.LAZY, false); // But non-concise methods follow the rules for normal funcs. test(FunctionType.METHOD, Strictness.SLOPPY, Laziness.EAGER, true); test(FunctionType.METHOD, Strictness.SLOPPY, Laziness.LAZY_BOUNDARY, true); test(FunctionType.METHOD, Strictness.SLOPPY, Laziness.LAZY, true); test(FunctionType.METHOD, Strictness.STRICT, Laziness.EAGER, false); test(FunctionType.METHOD, Strictness.STRICT, Laziness.LAZY_BOUNDARY, false); test(FunctionType.METHOD, Strictness.STRICT, Laziness.LAZY, false); test(FunctionType.METHOD, Strictness.STRICT_FUNCTION, Laziness.EAGER, false); test(FunctionType.METHOD, Strictness.STRICT_FUNCTION, Laziness.LAZY_BOUNDARY, false); test(FunctionType.METHOD, Strictness.STRICT_FUNCTION, Laziness.LAZY, false);