blob: 6530128556853bcf229822fee249934c3d80605a [file] [log] [blame]
// 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, "a, dup, dup, b", 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, "a, [dup], dup, b", false);
testHelper(type, strict, lazy, "a, dup, {b: dup}, c", false);
testHelper(type, strict, lazy, "a, {dup}, [dup], b", false);
testHelper(type, strict, lazy, "a, dup, ...dup", false);
testHelper(type, strict, lazy, "a, dup, dup, ...rest", false);
testHelper(type, strict, lazy, "a, dup, dup, b = 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);