v8/test/mjsunit/duplicate-parameters.js
Marja Hölttä ab4233e38a [parser|cleanup] Add tests for duplicate parameters.
There are at least 3 mechanisms for detecting duplicate parameters.
- ExpressionClassifier
- Scope::DeclareParameter checking IsDeclaredParameter
- PatternRewriter::VisitVariableProxy failing to declare a duplicate parameter

The conditions for when duplicate parameters are allowed and when not are pretty
involved too. They are allowed when
- the function is not an arrow function and not a concise method *and*
- when the parameter list is simple *and*
- we're in sloppy mode (incl. the function doesn't declare itself strict).

In addition, we don't recognize some of the early errors, and it's 
non-trivial to see which ones are recognized and which not (see bug
v8:6108). E.g., (dup, dup) => {}; is recognized but (dup, [dup]) => {} is
not. And (dup, [dup]) => 1; is.

We do have tests for some aspects of duplicate parameters (e.g., arrow function
duplicate parameters are included in arrow function tests), but it's hard to see
whether all combinations of the relevant conditions are tested.

This CL adds more structured tests which hopefully enables reducing the
duplicate parameter detection mechanisms to 2 or maybe even to 1.

BUG=v8:6092

Change-Id: Idd3db43b380aae4b9a89be5f1ed0755d39bfb36d
Reviewed-on: https://chromium-review.googlesource.com/456336
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Daniel Vogelheim <vogelheim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43895}
2017-03-17 12:10:35 +00:00

180 lines
7.1 KiB
JavaScript

// 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);