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