Improve support for binding pattern

Including improvements for parameters, variable assignment and
catch parameter.
This commit is contained in:
Anthony Van de Gejuchte
2016-08-20 22:32:29 +02:00
parent 1db50c3b16
commit 13ed445607
14 changed files with 727 additions and 312 deletions

View File

@@ -16,7 +16,7 @@ describe("Class", function() {
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: expand (...)";
e.message.substr(0, 31) === "SyntaxError: Unexpected token: ";
}
for (var i = 0; i < tests.length; i++) {

View File

@@ -2,6 +2,101 @@ var assert = require("assert");
var uglify = require("../../");
describe("Function", function() {
it ("Should parse binding patterns correctly", function() {
// Function argument nodes are correct
function get_args(args) {
return args.map(function (arg) {
return [arg.TYPE, arg.name];
});
}
// Destructurings as arguments
var destr_fun1 = uglify.parse('(function ({a, b}) {})').body[0].body;
var destr_fun2 = uglify.parse('(function ([a, [b]]) {})').body[0].body;
assert.equal(destr_fun1.argnames.length, 1);
assert.equal(destr_fun2.argnames.length, 1);
var destr_fun1 = uglify.parse('({a, b}) => null').body[0].body;
var destr_fun2 = uglify.parse('([a, [b]]) => null').body[0].body;
assert.equal(destr_fun1.argnames.length, 1);
assert.equal(destr_fun2.argnames.length, 1);
var destruct1 = destr_fun1.argnames[0];
var destruct2 = destr_fun2.argnames[0];
assert(destruct1 instanceof uglify.AST_Destructuring);
assert(destruct2 instanceof uglify.AST_Destructuring);
assert(destruct2.names[1] instanceof uglify.AST_Destructuring);
assert.equal(destruct1.start.value, '{');
assert.equal(destruct1.end.value, '}');
assert.equal(destruct2.start.value, '[');
assert.equal(destruct2.end.value, ']');
assert.equal(destruct1.is_array, false);
assert.equal(destruct2.is_array, true);
var aAndB = [
['SymbolFunarg', 'a'],
['SymbolFunarg', 'b']
];
assert.deepEqual(
[
destruct1.names[0].TYPE,
destruct1.names[0].name],
aAndB[0]);
assert.deepEqual(
[
destruct2.names[1].names[0].TYPE,
destruct2.names[1].names[0].name
],
aAndB[1]);
assert.deepEqual(
get_args(destr_fun1.args_as_names()),
aAndB);
assert.deepEqual(
get_args(destr_fun2.args_as_names()),
aAndB);
// Making sure we don't accidentally accept things which
// Aren't argument destructurings
assert.throws(function () {
uglify.parse('(function ( { a, [ b ] } ) { })')
});
assert.throws(function () {
uglify.parse('(function (1) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function (this) { })');
});
assert.throws(function () {
uglify.parse('(function ([1]) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function [a] { })');
});
// generators
var generators_def = uglify.parse('function* fn() {}').body[0];
assert.equal(generators_def.is_generator, true);
assert.throws(function () {
uglify.parse('function* (){ }');
});
var generators_yield_def = uglify.parse('function* fn() {\nyield remote();\}').body[0].body[0];
assert.strictEqual(generators_yield_def.body.is_star, false);
});
it("Should not accept spread on non-last parameters", function() {
var tests = [
"var a = function(...a, b) { return a.join(b) }",
@@ -20,11 +115,76 @@ describe("Function", function() {
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: expand (...)";
e.message.substr(0, 31) === "SyntaxError: Unexpected token: ";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept empty parameters after elision", function() {
var tests = [
"(function(,){})()",
"(function(a,){})()",
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Invalid function parameter";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept an initializer when parameter is a rest parameter", function() {
var tests = [
"(function(...a = b){})()",
"(function(a, ...b = [c, d]))"
];
var test = function(code) {
return function () {
uglify.parse(code, {fromString: true});
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
it("Shoult not accept duplicated identifiers inside parameters in strict mode or when using default assigment or spread", function() {
// From: ES2016 9.2.12 FunctionDeclarationInstantiation (func, argumentsList)
// NOTE Early errors ensure that duplicate parameter names can only occur
// in non-strict functions that do not have parameter default values or
// rest parameters.
var tests = [
"(function(a = 1, a){})()",
"(function(a, [a = 3]){})()",
"(function(a, b, c, d, [{e: [...a]}]){})()",
"'use strict'; (function(a, a){})",
"(function({a, a = b}))",
"(function(a, [...a]){})",
"(function(a, ...a){})",
"(function(a, [a, ...b]){})",
"(function(a, {b: a, c: [...d]}){})",
"(function(a, a, {b: [...c]}){})"
];
var test = function(code) {
return function () {
uglify.parse(code, {fromString: true});
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error &&
/^SyntaxError: Parameter [a-zA-Z]+ was used already$/.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
});

View File

@@ -0,0 +1,29 @@
var assert = require("assert");
var uglify = require("../../");
describe("Left-hand side expressions", function () {
it("Should parse destructuring with const/let/var correctly", function () {
var decls = uglify.parse('var {a,b} = foo, { c, d } = bar');
assert.equal(decls.body[0].TYPE, 'Var');
assert.equal(decls.body[0].definitions.length, 2);
assert.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
assert.equal(decls.body[0].definitions[0].value.TYPE, 'SymbolRef');
var nested_def = uglify.parse('var [{x}] = foo').body[0].definitions[0];
assert.equal(nested_def.name.names[0].names[0].TYPE, 'SymbolVar');
assert.equal(nested_def.name.names[0].names[0].name, 'x');
var holey_def = uglify.parse('const [,,third] = [1,2,3]').body[0].definitions[0];
assert.equal(holey_def.name.names[0].TYPE, 'Hole');
assert.equal(holey_def.name.names[2].TYPE, 'SymbolConst');
var expanding_def = uglify.parse('var [first, ...rest] = [1,2,3]').body[0].definitions[0];
assert.equal(expanding_def.name.names[0].TYPE, 'SymbolVar');
assert.equal(expanding_def.name.names[1].TYPE, 'Expansion');
assert.equal(expanding_def.name.names[1].expression.TYPE, 'SymbolVar');
});
});

22
test/mocha/try.js Normal file
View File

@@ -0,0 +1,22 @@
var assert = require("assert");
var uglify = require("../../");
describe("Try", function() {
it("Should not allow catch with an empty parameter", function() {
var tests = [
"try {} catch() {}"
];
var test = function(code) {
return function () {
uglify.parse(code, {fromString: true});
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
});

View File

@@ -90,7 +90,7 @@ describe("Yield", function() {
var fail = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected yield identifier inside strict mode";
/SyntaxError: Unexpected yield identifier (?:as parameter )?inside strict mode/.test(e.message);
}
var test = function(input) {