Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
491f16c766 | ||
|
|
a30092e20f | ||
|
|
b1abe92e1a | ||
|
|
b454ce667e | ||
|
|
32283a0def | ||
|
|
ac51d4c5a0 | ||
|
|
0432a7abb9 | ||
|
|
f3a1694a41 | ||
|
|
2e0dc97003 | ||
|
|
701035621d | ||
|
|
79334dda10 | ||
|
|
e918748d88 | ||
|
|
6b2f34769a | ||
|
|
48ffbef51d | ||
|
|
c0f3feae9f | ||
|
|
a00040dd93 | ||
|
|
ee95c1b38b | ||
|
|
4bceb85cbf |
8
.github/ISSUE_TEMPLATE.md
vendored
8
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,7 +1,9 @@
|
|||||||
- Bug report or feature request?
|
- Bug report or feature request? <!-- Note: sub-optimal but correct code is not a bug -->
|
||||||
- `uglify-js` version (`uglifyjs -V`)
|
- `uglify-js` version (`uglifyjs -V`)
|
||||||
- JavaScript input - ideally as small as possible.
|
- JavaScript input - ideally as small as possible.
|
||||||
- The `uglifyjs` CLI command executed or `minify()` options used.
|
- The `uglifyjs` CLI command executed or `minify()` options used.
|
||||||
- An example of JavaScript output produced and/or the error or warning.
|
- An example of JavaScript output produced and/or the error or warning.
|
||||||
|
<!--
|
||||||
Note: the release version of `uglify-js` only supports ES5. Those wishing to minify ES6 should use the experimental [`harmony`](https://github.com/mishoo/UglifyJS2#harmony) branch.
|
Note: the release version of uglify-js only supports ES5. Those wishing
|
||||||
|
to minify ES6 should use the experimental harmony branch.
|
||||||
|
-->
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ node_js:
|
|||||||
- "0.12"
|
- "0.12"
|
||||||
- "4"
|
- "4"
|
||||||
- "6"
|
- "6"
|
||||||
- "7"
|
|
||||||
env:
|
env:
|
||||||
- UGLIFYJS_TEST_ALL=1
|
- UGLIFYJS_TEST_ALL=1
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ There's also an
|
|||||||
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
|
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
|
||||||
Chrome and probably Safari).
|
Chrome and probably Safari).
|
||||||
|
|
||||||
Note: release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
|
#### Note:
|
||||||
|
- release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
|
||||||
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
|
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
|
||||||
|
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
|
||||||
|
|
||||||
Install
|
Install
|
||||||
-------
|
-------
|
||||||
|
|||||||
@@ -561,7 +561,7 @@ function getOptions(flag, constants) {
|
|||||||
|
|
||||||
var ast;
|
var ast;
|
||||||
try {
|
try {
|
||||||
ast = UglifyJS.parse(x, { expression: true });
|
ast = UglifyJS.parse(x, { cli: true, expression: true });
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||||
print_error("Error parsing arguments for flag `" + flag + "': " + x);
|
print_error("Error parsing arguments for flag `" + flag + "': " + x);
|
||||||
|
|||||||
@@ -131,6 +131,11 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
},
|
},
|
||||||
|
info: function() {
|
||||||
|
if (this.options.warnings == "verbose") {
|
||||||
|
AST_Node.warn.apply(AST_Node, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
warn: function(text, props) {
|
warn: function(text, props) {
|
||||||
if (this.options.warnings) {
|
if (this.options.warnings) {
|
||||||
// only emit unique warnings
|
// only emit unique warnings
|
||||||
@@ -587,9 +592,10 @@ merge(Compressor.prototype, {
|
|||||||
// Restrict var replacement to constants if side effects encountered.
|
// Restrict var replacement to constants if side effects encountered.
|
||||||
if (side_effects_encountered |= lvalues_encountered) continue;
|
if (side_effects_encountered |= lvalues_encountered) continue;
|
||||||
|
|
||||||
|
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
|
||||||
// Non-constant single use vars can only be replaced in same scope.
|
// Non-constant single use vars can only be replaced in same scope.
|
||||||
if (ref.scope !== self) {
|
if (ref.scope !== self) {
|
||||||
side_effects_encountered |= var_decl.value.has_side_effects(compressor);
|
side_effects_encountered |= value_has_side_effects;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,12 +620,25 @@ merge(Compressor.prototype, {
|
|||||||
|| node instanceof AST_IterationStatement
|
|| node instanceof AST_IterationStatement
|
||||||
|| (parent instanceof AST_If && node !== parent.condition)
|
|| (parent instanceof AST_If && node !== parent.condition)
|
||||||
|| (parent instanceof AST_Conditional && node !== parent.condition)
|
|| (parent instanceof AST_Conditional && node !== parent.condition)
|
||||||
|
|| (node instanceof AST_SymbolRef
|
||||||
|
&& value_has_side_effects
|
||||||
|
&& !are_references_in_scope(node.definition(), self))
|
||||||
|| (parent instanceof AST_Binary
|
|| (parent instanceof AST_Binary
|
||||||
&& (parent.operator == "&&" || parent.operator == "||")
|
&& (parent.operator == "&&" || parent.operator == "||")
|
||||||
&& node === parent.right)
|
&& node === parent.right)
|
||||||
|| (parent instanceof AST_Switch && node !== parent.expression)) {
|
|| (parent instanceof AST_Switch && node !== parent.expression)) {
|
||||||
return side_effects_encountered = unwind = true, node;
|
return side_effects_encountered = unwind = true, node;
|
||||||
}
|
}
|
||||||
|
function are_references_in_scope(def, scope) {
|
||||||
|
if (def.orig.length === 1
|
||||||
|
&& def.orig[0] instanceof AST_SymbolDefun) return true;
|
||||||
|
if (def.scope !== scope) return false;
|
||||||
|
var refs = def.references;
|
||||||
|
for (var i = 0, len = refs.length; i < len; i++) {
|
||||||
|
if (refs[i].scope !== scope) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
function postorder(node) {
|
function postorder(node) {
|
||||||
if (unwind) return node;
|
if (unwind) return node;
|
||||||
@@ -664,7 +683,7 @@ merge(Compressor.prototype, {
|
|||||||
// Further optimize statement after substitution.
|
// Further optimize statement after substitution.
|
||||||
stat.reset_opt_flags(compressor);
|
stat.reset_opt_flags(compressor);
|
||||||
|
|
||||||
compressor.warn("Collapsing " + (is_constant ? "constant" : "variable") +
|
compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
|
||||||
" " + var_name + " [{file}:{line},{col}]", node.start);
|
" " + var_name + " [{file}:{line},{col}]", node.start);
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return value;
|
return value;
|
||||||
@@ -1828,7 +1847,7 @@ merge(Compressor.prototype, {
|
|||||||
sym.__unused = true;
|
sym.__unused = true;
|
||||||
if (trim) {
|
if (trim) {
|
||||||
a.pop();
|
a.pop();
|
||||||
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
||||||
name : sym.name,
|
name : sym.name,
|
||||||
file : sym.start.file,
|
file : sym.start.file,
|
||||||
line : sym.start.line,
|
line : sym.start.line,
|
||||||
@@ -1843,7 +1862,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (drop_funcs && node instanceof AST_Defun && node !== self) {
|
if (drop_funcs && node instanceof AST_Defun && node !== self) {
|
||||||
if (!(node.name.definition().id in in_use_ids)) {
|
if (!(node.name.definition().id in in_use_ids)) {
|
||||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||||
name : node.name.name,
|
name : node.name.name,
|
||||||
file : node.name.start.file,
|
file : node.name.start.file,
|
||||||
line : node.name.start.line,
|
line : node.name.start.line,
|
||||||
@@ -1867,7 +1886,7 @@ merge(Compressor.prototype, {
|
|||||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
// place uninitialized names at the start
|
// place uninitialized names at the start
|
||||||
@@ -1942,6 +1961,9 @@ merge(Compressor.prototype, {
|
|||||||
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
|
} else if (is_empty(node.init)) {
|
||||||
|
node.init = null;
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== self)
|
if (node instanceof AST_Scope && node !== self)
|
||||||
@@ -2915,7 +2937,12 @@ merge(Compressor.prototype, {
|
|||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
|
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
|
||||||
field = cdr.left.is_constant() ? "right" : "left";
|
if (cdr.left.is_constant()) {
|
||||||
|
if (cdr.operator == "||" || cdr.operator == "&&") break;
|
||||||
|
field = "right";
|
||||||
|
} else {
|
||||||
|
field = "left";
|
||||||
|
}
|
||||||
} else if (cdr instanceof AST_Call
|
} else if (cdr instanceof AST_Call
|
||||||
|| cdr instanceof AST_Unary && cdr.operator != "++" && cdr.operator != "--") {
|
|| cdr instanceof AST_Unary && cdr.operator != "++" && cdr.operator != "--") {
|
||||||
field = "expression";
|
field = "expression";
|
||||||
@@ -3267,6 +3294,7 @@ merge(Compressor.prototype, {
|
|||||||
left: self.left,
|
left: self.left,
|
||||||
right: self.right.expression
|
right: self.right.expression
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// -a + b => b - a
|
// -a + b => b - a
|
||||||
if (self.left instanceof AST_UnaryPrefix
|
if (self.left instanceof AST_UnaryPrefix
|
||||||
@@ -3278,6 +3306,7 @@ merge(Compressor.prototype, {
|
|||||||
left: self.right,
|
left: self.right,
|
||||||
right: self.left.expression
|
right: self.left.expression
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case "*":
|
case "*":
|
||||||
associative = compressor.option("unsafe_math");
|
associative = compressor.option("unsafe_math");
|
||||||
@@ -3526,19 +3555,17 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var consequent = self.consequent;
|
var consequent = self.consequent;
|
||||||
var alternative = self.alternative;
|
var alternative = self.alternative;
|
||||||
|
// if (foo) exp = something; else exp = something_else;
|
||||||
|
// |
|
||||||
|
// v
|
||||||
|
// exp = foo ? something : something_else;
|
||||||
if (consequent instanceof AST_Assign
|
if (consequent instanceof AST_Assign
|
||||||
&& alternative instanceof AST_Assign
|
&& alternative instanceof AST_Assign
|
||||||
&& consequent.operator == alternative.operator
|
&& consequent.operator == alternative.operator
|
||||||
&& consequent.left.equivalent_to(alternative.left)
|
&& consequent.left.equivalent_to(alternative.left)
|
||||||
&& (!consequent.left.has_side_effects(compressor)
|
&& (!self.condition.has_side_effects(compressor)
|
||||||
|| !self.condition.has_side_effects(compressor))
|
|| consequent.operator == "="
|
||||||
) {
|
&& !consequent.left.has_side_effects(compressor))) {
|
||||||
/*
|
|
||||||
* Stuff like this:
|
|
||||||
* if (foo) exp = something; else exp = something_else;
|
|
||||||
* ==>
|
|
||||||
* exp = foo ? something : something_else;
|
|
||||||
*/
|
|
||||||
return make_node(AST_Assign, self, {
|
return make_node(AST_Assign, self, {
|
||||||
operator: consequent.operator,
|
operator: consequent.operator,
|
||||||
left: consequent.left,
|
left: consequent.left,
|
||||||
|
|||||||
@@ -799,7 +799,7 @@ function OutputStream(options) {
|
|||||||
output.print("for");
|
output.print("for");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
if (self.init && !(self.init instanceof AST_EmptyStatement)) {
|
if (self.init) {
|
||||||
if (self.init instanceof AST_Definitions) {
|
if (self.init instanceof AST_Definitions) {
|
||||||
self.init.print(output);
|
self.init.print(output);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -695,6 +695,7 @@ function parse($TEXT, options) {
|
|||||||
html5_comments : true,
|
html5_comments : true,
|
||||||
bare_returns : false,
|
bare_returns : false,
|
||||||
shebang : true,
|
shebang : true,
|
||||||
|
cli : false,
|
||||||
});
|
});
|
||||||
|
|
||||||
var S = {
|
var S = {
|
||||||
@@ -1456,7 +1457,7 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function make_unary(ctor, op, expr) {
|
function make_unary(ctor, op, expr) {
|
||||||
if ((op == "++" || op == "--") && !is_assignable(expr))
|
if ((op == "++" || op == "--") && !is_assignable(expr))
|
||||||
croak("Invalid use of " + op + " operator");
|
croak("Invalid use of " + op + " operator", null, ctor === AST_UnaryPrefix ? expr.start.col - 1 : null);
|
||||||
return new ctor({ operator: op, expression: expr });
|
return new ctor({ operator: op, expression: expr });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1501,9 +1502,8 @@ function parse($TEXT, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function is_assignable(expr) {
|
function is_assignable(expr) {
|
||||||
if (!options.strict) return true;
|
if (options.cli) return true;
|
||||||
if (expr instanceof AST_This) return false;
|
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
|
||||||
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var maybe_assign = function(no_in) {
|
var maybe_assign = function(no_in) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "2.8.14",
|
"version": "2.8.16",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"source-map": "~0.5.1",
|
"source-map": "~0.5.1",
|
||||||
"uglify-to-browserify": "~1.0.0",
|
|
||||||
"yargs": "~3.10.0"
|
"yargs": "~3.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -40,13 +39,15 @@
|
|||||||
"estraverse": "~1.5.1",
|
"estraverse": "~1.5.1",
|
||||||
"mocha": "~2.3.4"
|
"mocha": "~2.3.4"
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"uglify-to-browserify": "~1.0.0"
|
||||||
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
"transform": [
|
"transform": [
|
||||||
"uglify-to-browserify"
|
"uglify-to-browserify"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
|
|
||||||
"test": "node test/run-tests.js"
|
"test": "node test/run-tests.js"
|
||||||
},
|
},
|
||||||
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
|
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
|
||||||
|
|||||||
@@ -1415,3 +1415,178 @@ issue_1605_2: {
|
|||||||
(new Object).p = 1;
|
(new Object).p = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1631_1: {
|
||||||
|
options = {
|
||||||
|
cascade: true,
|
||||||
|
collapse_vars: true,
|
||||||
|
hoist_funs: true,
|
||||||
|
join_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var pc = 0;
|
||||||
|
function f(x) {
|
||||||
|
pc = 200;
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
function x() {
|
||||||
|
var t = f();
|
||||||
|
pc += t;
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
console.log(x());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(x) {
|
||||||
|
return pc = 200, 100;
|
||||||
|
}
|
||||||
|
function x() {
|
||||||
|
var t = f();
|
||||||
|
return pc += t;
|
||||||
|
}
|
||||||
|
var pc = 0;
|
||||||
|
console.log(x());
|
||||||
|
}
|
||||||
|
expect_stdout: "300"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1631_2: {
|
||||||
|
options = {
|
||||||
|
cascade: true,
|
||||||
|
collapse_vars: true,
|
||||||
|
hoist_funs: true,
|
||||||
|
join_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 0, b = 1;
|
||||||
|
function f() {
|
||||||
|
a = 2;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
var t = f();
|
||||||
|
b = a + t;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
return a = 2, 4;
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
var t = f();
|
||||||
|
return b = a + t;
|
||||||
|
}
|
||||||
|
var a = 0, b = 1;
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect_stdout: "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1631_3: {
|
||||||
|
options = {
|
||||||
|
cascade: true,
|
||||||
|
collapse_vars: true,
|
||||||
|
hoist_funs: true,
|
||||||
|
join_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function g() {
|
||||||
|
var a = 0, b = 1;
|
||||||
|
function f() {
|
||||||
|
a = 2;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
var t = f();
|
||||||
|
b = a + t;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function g() {
|
||||||
|
function f() {
|
||||||
|
return a = 2, 4;
|
||||||
|
}
|
||||||
|
var a = 0, b = 1, t = f();
|
||||||
|
return b = a + t;
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect_stdout: "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
var_side_effects_1: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var print = console.log.bind(console);
|
||||||
|
function foo(x) {
|
||||||
|
var twice = x * 2;
|
||||||
|
print('Foo:', twice);
|
||||||
|
}
|
||||||
|
foo(10);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var print = console.log.bind(console);
|
||||||
|
function foo(x) {
|
||||||
|
print('Foo:', 2 * x);
|
||||||
|
}
|
||||||
|
foo(10);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var_side_effects_2: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var print = console.log.bind(console);
|
||||||
|
function foo(x) {
|
||||||
|
var twice = x.y * 2;
|
||||||
|
print('Foo:', twice);
|
||||||
|
}
|
||||||
|
foo({ y: 10 });
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var print = console.log.bind(console);
|
||||||
|
function foo(x) {
|
||||||
|
var twice = 2 * x.y;
|
||||||
|
print('Foo:', twice);
|
||||||
|
}
|
||||||
|
foo({ y: 10 });
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var_side_effects_3: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var print = console.log.bind(console);
|
||||||
|
function foo(x) {
|
||||||
|
var twice = x.y * 2;
|
||||||
|
print('Foo:', twice);
|
||||||
|
}
|
||||||
|
foo({ y: 10 });
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var print = console.log.bind(console);
|
||||||
|
function foo(x) {
|
||||||
|
print('Foo:', 2 * x.y);
|
||||||
|
}
|
||||||
|
foo({ y: 10 });
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
@@ -893,3 +893,43 @@ equality_conditionals_true: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1645_1: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
(b = a) ? a++ + (b += a) ? b += a : b += a : b ^= a;
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
(b = a) ? (a++ + (b += a), b += a) : b ^= a;
|
||||||
|
console.log(a,b);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1645_2: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 0;
|
||||||
|
function f() {
|
||||||
|
return a++;
|
||||||
|
}
|
||||||
|
f() ? a += 2 : a += 4;
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 0;
|
||||||
|
function f(){
|
||||||
|
return a++;
|
||||||
|
}
|
||||||
|
f() ? a += 2 : a += 4;
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
@@ -791,3 +791,17 @@ issue_1583: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1656: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
for(var a=0;;);
|
||||||
|
}
|
||||||
|
expect_exact: "for (;;) ;"
|
||||||
|
}
|
||||||
|
|||||||
@@ -789,3 +789,16 @@ unsafe_charAt_noop: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1649: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(-1 + -1);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(-2);
|
||||||
|
}
|
||||||
|
expect_stdout: "-2";
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,22 +47,6 @@ html_comment_in_greater_than_or_equal: {
|
|||||||
expect_exact: "function f(a,b){return a-- >=b}";
|
expect_exact: "function f(a,b){return a-- >=b}";
|
||||||
}
|
}
|
||||||
|
|
||||||
html_comment_in_right_shift_assign: {
|
|
||||||
input: {
|
|
||||||
// Note: illegal javascript
|
|
||||||
function f(a, b) { return a-- >>= b; }
|
|
||||||
}
|
|
||||||
expect_exact: "function f(a,b){return a-- >>=b}";
|
|
||||||
}
|
|
||||||
|
|
||||||
html_comment_in_zero_fill_right_shift_assign: {
|
|
||||||
input: {
|
|
||||||
// Note: illegal javascript
|
|
||||||
function f(a, b) { return a-- >>>= b; }
|
|
||||||
}
|
|
||||||
expect_exact: "function f(a,b){return a-- >>>=b}";
|
|
||||||
}
|
|
||||||
|
|
||||||
html_comment_in_string_literal: {
|
html_comment_in_string_literal: {
|
||||||
input: {
|
input: {
|
||||||
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
|
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ non_hoisted_function_after_return_2a: {
|
|||||||
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
|
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
|
||||||
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
|
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
|
||||||
if_return: true, join_vars: true, cascade: true, side_effects: true,
|
if_return: true, join_vars: true, cascade: true, side_effects: true,
|
||||||
collapse_vars: false, passes: 2
|
collapse_vars: false, passes: 2, warnings: "verbose"
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
function foo(x) {
|
function foo(x) {
|
||||||
@@ -75,7 +75,7 @@ non_hoisted_function_after_return_2a: {
|
|||||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
|
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
|
||||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
|
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
|
||||||
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
|
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
|
||||||
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]"
|
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +114,5 @@ non_hoisted_function_after_return_2b: {
|
|||||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]",
|
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]",
|
||||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]",
|
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]",
|
||||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
|
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
|
||||||
"WARN: Dropping unused variable b [test/compress/issue-1034.js:95,20]",
|
|
||||||
"WARN: Dropping unused variable c [test/compress/issue-1034.js:97,16]"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
88
test/compress/issue-1639.js
Normal file
88
test/compress/issue-1639.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
issue_1639_1: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
cascade: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
join_vars: true,
|
||||||
|
loops: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
|
||||||
|
var L1 = 5;
|
||||||
|
while (--L1 > 0) {
|
||||||
|
if ((--b), false) {
|
||||||
|
if (b) {
|
||||||
|
var ignore = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (var a = 100, b = 10, L1 = 5; --L1 > 0;)
|
||||||
|
if (--b, !1) var ignore = 0;
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1639_2: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
cascade: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
join_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
|
||||||
|
function f19() {
|
||||||
|
if (++a, false)
|
||||||
|
if (a)
|
||||||
|
if (++a);
|
||||||
|
}
|
||||||
|
f19();
|
||||||
|
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
function f19() {
|
||||||
|
++a, 1;
|
||||||
|
}
|
||||||
|
f19(),
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1639_3: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
cascade: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
a++ && false && a ? 0 : 0;
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
a++,
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
45
test/compress/issue-1656.js
Normal file
45
test/compress/issue-1656.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
f7: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
cascade: true,
|
||||||
|
collapse_vars: true,
|
||||||
|
comparisons: true,
|
||||||
|
conditionals: true,
|
||||||
|
dead_code: true,
|
||||||
|
drop_debugger: true,
|
||||||
|
evaluate: true,
|
||||||
|
hoist_funs: true,
|
||||||
|
if_return: true,
|
||||||
|
join_vars: true,
|
||||||
|
loops: true,
|
||||||
|
negate_iife: true,
|
||||||
|
passes: 3,
|
||||||
|
properties: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 100, b = 10;
|
||||||
|
function f22464() {
|
||||||
|
var brake146670 = 5;
|
||||||
|
while (((b = a) ? !a : ~a ? null : b += a) && --brake146670 > 0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f22464();
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect_exact: [
|
||||||
|
"var a = 100, b = 10;",
|
||||||
|
"",
|
||||||
|
"!function() {",
|
||||||
|
" for (;b = a, !1; ) ;",
|
||||||
|
"}(), console.log(a, b);",
|
||||||
|
]
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
@@ -440,3 +440,21 @@ issue_186_beautify_bracketize_ie8: {
|
|||||||
'}',
|
'}',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1648: {
|
||||||
|
options = {
|
||||||
|
join_vars: true,
|
||||||
|
loops: true,
|
||||||
|
passes: 2,
|
||||||
|
sequences: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
x();
|
||||||
|
var b = 1;
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function f(){for(x();1;);}"
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ booleans_global_defs: {
|
|||||||
expect: {
|
expect: {
|
||||||
console.log(!0);
|
console.log(!0);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
condition_evaluate: {
|
condition_evaluate: {
|
||||||
|
|||||||
1
test/input/invalid/assign_1.js
Normal file
1
test/input/invalid/assign_1.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log(1 || 5--);
|
||||||
1
test/input/invalid/assign_2.js
Normal file
1
test/input/invalid/assign_2.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log(2 || (Math.random() /= 2));
|
||||||
1
test/input/invalid/assign_3.js
Normal file
1
test/input/invalid/assign_3.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log(3 || ++this);
|
||||||
1
test/input/issue-1632/^{foo}[bar](baz)+$.js
Normal file
1
test/input/issue-1632/^{foo}[bar](baz)+$.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log(x);
|
||||||
@@ -251,4 +251,59 @@ describe("bin/uglifyjs", function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("Should support hyphen as shorthand", function(done) {
|
||||||
|
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep-fnames=true';
|
||||||
|
|
||||||
|
exec(command, function (err, stdout) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should throw syntax error (5--)", function(done) {
|
||||||
|
var command = uglifyjscmd + ' test/input/invalid/assign_1.js';
|
||||||
|
|
||||||
|
exec(command, function (err, stdout, stderr) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(stdout, "");
|
||||||
|
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||||
|
"Parse error at test/input/invalid/assign_1.js:1,18",
|
||||||
|
"console.log(1 || 5--);",
|
||||||
|
" ^",
|
||||||
|
"SyntaxError: Invalid use of -- operator"
|
||||||
|
].join("\n"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should throw syntax error (Math.random() /= 2)", function(done) {
|
||||||
|
var command = uglifyjscmd + ' test/input/invalid/assign_2.js';
|
||||||
|
|
||||||
|
exec(command, function (err, stdout, stderr) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(stdout, "");
|
||||||
|
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||||
|
"Parse error at test/input/invalid/assign_2.js:1,32",
|
||||||
|
"console.log(2 || (Math.random() /= 2));",
|
||||||
|
" ^",
|
||||||
|
"SyntaxError: Invalid assignment"
|
||||||
|
].join("\n"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should throw syntax error (++this)", function(done) {
|
||||||
|
var command = uglifyjscmd + ' test/input/invalid/assign_3.js';
|
||||||
|
|
||||||
|
exec(command, function (err, stdout, stderr) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(stdout, "");
|
||||||
|
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||||
|
"Parse error at test/input/invalid/assign_3.js:1,18",
|
||||||
|
"console.log(3 || ++this);",
|
||||||
|
" ^",
|
||||||
|
"SyntaxError: Invalid use of ++ operator"
|
||||||
|
].join("\n"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var Uglify = require('../../');
|
var Uglify = require('../../');
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
|
var path = require("path");
|
||||||
|
|
||||||
describe("minify() with input file globs", function() {
|
describe("minify() with input file globs", function() {
|
||||||
it("minify() with one input file glob string.", function() {
|
it("minify() with one input file glob string.", function() {
|
||||||
@@ -19,6 +20,39 @@ describe("minify() with input file globs", function() {
|
|||||||
], {
|
], {
|
||||||
compress: { toplevel: true }
|
compress: { toplevel: true }
|
||||||
});
|
});
|
||||||
assert.strictEqual(result.code, 'var print=console.log.bind(console);print("qux",function(n){return 3*n}(3),function(n){return n/2}(12)),function(n){print("Foo:",2*n)}(11);');
|
assert.strictEqual(result.code, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);');
|
||||||
|
});
|
||||||
|
it("should throw with non-matching glob string", function() {
|
||||||
|
var glob = "test/input/issue-1242/blah.*";
|
||||||
|
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
|
||||||
|
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
|
||||||
|
assert.throws(function() {
|
||||||
|
Uglify.minify(glob);
|
||||||
|
}, "should throw file not found");
|
||||||
|
});
|
||||||
|
it('"?" in glob string should not match "/"', function() {
|
||||||
|
var glob = "test/input?issue-1242/foo.*";
|
||||||
|
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
|
||||||
|
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
|
||||||
|
assert.throws(function() {
|
||||||
|
Uglify.minify(glob);
|
||||||
|
}, "should throw file not found");
|
||||||
|
});
|
||||||
|
it("should handle special characters in glob string", function() {
|
||||||
|
var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
|
||||||
|
assert.strictEqual(result.code, "console.log(x);");
|
||||||
|
});
|
||||||
|
it("should handle array of glob strings - matching and otherwise", function() {
|
||||||
|
var dir = "test/input/issue-1242";
|
||||||
|
var matches = Uglify.simple_glob([
|
||||||
|
path.join(dir, "b*.es5"),
|
||||||
|
path.join(dir, "z*.es5"),
|
||||||
|
path.join(dir, "*.js"),
|
||||||
|
]);
|
||||||
|
assert.strictEqual(matches.length, 4);
|
||||||
|
assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
|
||||||
|
assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
|
||||||
|
assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
|
||||||
|
assert.strictEqual(matches[3], path.join(dir, "qux.js"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ var UglifyJS = require(".."),
|
|||||||
escodegen = require("escodegen"),
|
escodegen = require("escodegen"),
|
||||||
esfuzz = require("esfuzz"),
|
esfuzz = require("esfuzz"),
|
||||||
estraverse = require("estraverse"),
|
estraverse = require("estraverse"),
|
||||||
prefix = Array(20).join("\b") + " ";
|
prefix = "\r ";
|
||||||
|
|
||||||
// Normalizes input AST for UglifyJS in order to get correct comparison.
|
// Normalizes input AST for UglifyJS in order to get correct comparison.
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ module.exports = function(options) {
|
|||||||
var ast1 = normalizeInput(esfuzz.generate({
|
var ast1 = normalizeInput(esfuzz.generate({
|
||||||
maxDepth: options.maxDepth
|
maxDepth: options.maxDepth
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var ast2 =
|
var ast2 =
|
||||||
UglifyJS
|
UglifyJS
|
||||||
.AST_Node
|
.AST_Node
|
||||||
|
|||||||
@@ -105,6 +105,23 @@ function run_compress_tests() {
|
|||||||
function test_case(test) {
|
function test_case(test) {
|
||||||
log_test(test.name);
|
log_test(test.name);
|
||||||
U.base54.reset();
|
U.base54.reset();
|
||||||
|
var output_options = test.beautify || {};
|
||||||
|
var expect;
|
||||||
|
if (test.expect) {
|
||||||
|
expect = make_code(as_toplevel(test.expect, test.mangle), output_options);
|
||||||
|
} else {
|
||||||
|
expect = test.expect_exact;
|
||||||
|
}
|
||||||
|
var input = as_toplevel(test.input, test.mangle);
|
||||||
|
var input_code = make_code(input, output_options);
|
||||||
|
var input_formatted = make_code(test.input, {
|
||||||
|
beautify: true,
|
||||||
|
quote_style: 3,
|
||||||
|
keep_quoted_props: true
|
||||||
|
});
|
||||||
|
if (test.mangle_props) {
|
||||||
|
input = U.mangle_properties(input, test.mangle_props);
|
||||||
|
}
|
||||||
var options = U.defaults(test.options, {
|
var options = U.defaults(test.options, {
|
||||||
warnings: false
|
warnings: false
|
||||||
});
|
});
|
||||||
@@ -114,25 +131,9 @@ function run_compress_tests() {
|
|||||||
U.AST_Node.warn_function = function(text) {
|
U.AST_Node.warn_function = function(text) {
|
||||||
warnings_emitted.push("WARN: " + text);
|
warnings_emitted.push("WARN: " + text);
|
||||||
};
|
};
|
||||||
options.warnings = true;
|
if (!options.warnings) options.warnings = true;
|
||||||
}
|
}
|
||||||
var cmp = new U.Compressor(options, true);
|
var cmp = new U.Compressor(options, true);
|
||||||
var output_options = test.beautify || {};
|
|
||||||
var expect;
|
|
||||||
if (test.expect) {
|
|
||||||
expect = make_code(as_toplevel(test.expect, test.mangle), output_options);
|
|
||||||
} else {
|
|
||||||
expect = test.expect_exact;
|
|
||||||
}
|
|
||||||
var input = as_toplevel(test.input, test.mangle);
|
|
||||||
var input_code = make_code(test.input, {
|
|
||||||
beautify: true,
|
|
||||||
quote_style: 3,
|
|
||||||
keep_quoted_props: true
|
|
||||||
});
|
|
||||||
if (test.mangle_props) {
|
|
||||||
input = U.mangle_properties(input, test.mangle_props);
|
|
||||||
}
|
|
||||||
var output = cmp.compress(input);
|
var output = cmp.compress(input);
|
||||||
output.figure_out_scope(test.mangle);
|
output.figure_out_scope(test.mangle);
|
||||||
if (test.mangle) {
|
if (test.mangle) {
|
||||||
@@ -142,7 +143,7 @@ function run_compress_tests() {
|
|||||||
output = make_code(output, output_options);
|
output = make_code(output, output_options);
|
||||||
if (expect != output) {
|
if (expect != output) {
|
||||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||||
input: input_code,
|
input: input_formatted,
|
||||||
output: output,
|
output: output,
|
||||||
expected: expect
|
expected: expect
|
||||||
});
|
});
|
||||||
@@ -155,7 +156,7 @@ function run_compress_tests() {
|
|||||||
var reparsed_ast = U.parse(output);
|
var reparsed_ast = U.parse(output);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
|
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
|
||||||
input: input_code,
|
input: input_formatted,
|
||||||
output: output,
|
output: output,
|
||||||
error: ex.toString(),
|
error: ex.toString(),
|
||||||
});
|
});
|
||||||
@@ -174,7 +175,7 @@ function run_compress_tests() {
|
|||||||
var actual_warnings = JSON.stringify(warnings_emitted);
|
var actual_warnings = JSON.stringify(warnings_emitted);
|
||||||
if (expected_warnings != actual_warnings) {
|
if (expected_warnings != actual_warnings) {
|
||||||
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
|
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
|
||||||
input: input_code,
|
input: input_formatted,
|
||||||
expected_warnings: expected_warnings,
|
expected_warnings: expected_warnings,
|
||||||
actual_warnings: actual_warnings,
|
actual_warnings: actual_warnings,
|
||||||
});
|
});
|
||||||
@@ -183,13 +184,13 @@ function run_compress_tests() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (test.expect_stdout) {
|
if (test.expect_stdout) {
|
||||||
var stdout = run_code(make_code(input, output_options));
|
var stdout = run_code(input_code);
|
||||||
if (test.expect_stdout === true) {
|
if (test.expect_stdout === true) {
|
||||||
test.expect_stdout = stdout;
|
test.expect_stdout = stdout;
|
||||||
}
|
}
|
||||||
if (!same_stdout(test.expect_stdout, stdout)) {
|
if (!same_stdout(test.expect_stdout, stdout)) {
|
||||||
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||||
input: input_code,
|
input: input_formatted,
|
||||||
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
||||||
expected: test.expect_stdout,
|
expected: test.expect_stdout,
|
||||||
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
|
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
|
||||||
@@ -201,7 +202,7 @@ function run_compress_tests() {
|
|||||||
stdout = run_code(output);
|
stdout = run_code(output);
|
||||||
if (!same_stdout(test.expect_stdout, stdout)) {
|
if (!same_stdout(test.expect_stdout, stdout)) {
|
||||||
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||||
input: input_code,
|
input: input_formatted,
|
||||||
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
||||||
expected: test.expect_stdout,
|
expected: test.expect_stdout,
|
||||||
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
|
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
|
||||||
|
|||||||
225
test/ufuzz.js
Normal file
225
test/ufuzz.js
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
// ufuzz.js
|
||||||
|
// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// workaround for tty output truncation upon process.exit()
|
||||||
|
[process.stdout, process.stderr].forEach(function(stream){
|
||||||
|
if (stream._handle && stream._handle.setBlocking)
|
||||||
|
stream._handle.setBlocking(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
var vm = require("vm");
|
||||||
|
var minify = require("..").minify;
|
||||||
|
|
||||||
|
function run_code(code) {
|
||||||
|
var stdout = "";
|
||||||
|
var original_write = process.stdout.write;
|
||||||
|
process.stdout.write = function(chunk) {
|
||||||
|
stdout += chunk;
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 });
|
||||||
|
return stdout;
|
||||||
|
} catch (ex) {
|
||||||
|
return ex;
|
||||||
|
} finally {
|
||||||
|
process.stdout.write = original_write;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rng(max) {
|
||||||
|
return Math.floor(max * Math.random());
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFunctionDecls(n, recurmax) {
|
||||||
|
if (--recurmax < 0) { return ';'; }
|
||||||
|
var s = '';
|
||||||
|
while (--n > 0) {
|
||||||
|
s += createFunctionDecl(recurmax) + '\n';
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcs = 0;
|
||||||
|
function createFunctionDecl(recurmax) {
|
||||||
|
if (--recurmax < 0) { return ';'; }
|
||||||
|
var func = funcs++;
|
||||||
|
return 'function f' + func + '(){' + createStatements(3, recurmax) + '}\nf' + func + '();';
|
||||||
|
}
|
||||||
|
|
||||||
|
function createStatements(n, recurmax) {
|
||||||
|
if (--recurmax < 0) { return ';'; }
|
||||||
|
var s = '';
|
||||||
|
while (--n > 0) {
|
||||||
|
s += createStatement(recurmax);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loops = 0;
|
||||||
|
function createStatement(recurmax) {
|
||||||
|
var loop = ++loops;
|
||||||
|
if (--recurmax < 0) { return ';'; }
|
||||||
|
switch (rng(7)) {
|
||||||
|
case 0:
|
||||||
|
return '{' + createStatement(recurmax) + '}';
|
||||||
|
case 1:
|
||||||
|
return 'if (' + createExpression(recurmax) + ')' + createStatement(recurmax);
|
||||||
|
case 2:
|
||||||
|
return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax) + '} while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0);}';
|
||||||
|
case 3:
|
||||||
|
return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax) + '}';
|
||||||
|
case 4:
|
||||||
|
return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax);
|
||||||
|
case 5:
|
||||||
|
return ';';
|
||||||
|
case 6:
|
||||||
|
return createExpression() + ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createExpression(recurmax) {
|
||||||
|
if (--recurmax < 0) { return '0'; }
|
||||||
|
switch (rng(8)) {
|
||||||
|
case 0:
|
||||||
|
return '(' + createUnaryOp() + 'a)';
|
||||||
|
case 1:
|
||||||
|
return '(a' + (Math.random() > 0.5 ? '++' : '--') + ')';
|
||||||
|
case 2:
|
||||||
|
return '(b ' + createAssignment() + ' a)';
|
||||||
|
case 3:
|
||||||
|
return '(' + Math.random() + ' > 0.5 ? a : b)';
|
||||||
|
case 4:
|
||||||
|
return createExpression(recurmax) + createBinaryOp() + createExpression(recurmax);
|
||||||
|
case 5:
|
||||||
|
return createValue();
|
||||||
|
case 6:
|
||||||
|
return '(' + createExpression(recurmax) + ')';
|
||||||
|
case 7:
|
||||||
|
return createExpression(recurmax) + '?(' + createExpression(recurmax) + '):(' + createExpression(recurmax) + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createValue() {
|
||||||
|
var values = [
|
||||||
|
'true',
|
||||||
|
'false',
|
||||||
|
'22',
|
||||||
|
'0',
|
||||||
|
'(-1)',
|
||||||
|
'NaN',
|
||||||
|
'undefined',
|
||||||
|
'null',
|
||||||
|
'"foo"',
|
||||||
|
'"bar"' ];
|
||||||
|
return values[rng(values.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBinaryOp() {
|
||||||
|
switch (rng(6)) {
|
||||||
|
case 0:
|
||||||
|
return '+';
|
||||||
|
case 1:
|
||||||
|
return '-';
|
||||||
|
case 2:
|
||||||
|
return ',';
|
||||||
|
case 3:
|
||||||
|
return '&&';
|
||||||
|
case 4:
|
||||||
|
return '||';
|
||||||
|
case 5:
|
||||||
|
return '^';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAssignment() {
|
||||||
|
switch (rng(4)) {
|
||||||
|
case 0:
|
||||||
|
return '=';
|
||||||
|
case 1:
|
||||||
|
return '-=';
|
||||||
|
case 2:
|
||||||
|
return '^=';
|
||||||
|
case 3:
|
||||||
|
return '+=';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUnaryOp() {
|
||||||
|
switch (rng(4)) {
|
||||||
|
case 0:
|
||||||
|
return '--';
|
||||||
|
case 1:
|
||||||
|
return '++';
|
||||||
|
case 2:
|
||||||
|
return '~';
|
||||||
|
case 3:
|
||||||
|
return '!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
console.log("//=============================================================");
|
||||||
|
console.log("// original code");
|
||||||
|
console.log("//");
|
||||||
|
console.log(original_code);
|
||||||
|
console.log();
|
||||||
|
console.log();
|
||||||
|
console.log("//-------------------------------------------------------------");
|
||||||
|
console.log("// original code (beautify'd)");
|
||||||
|
console.log("//");
|
||||||
|
console.log(beautify_code);
|
||||||
|
console.log();
|
||||||
|
console.log();
|
||||||
|
console.log("//-------------------------------------------------------------");
|
||||||
|
console.log("// uglified code");
|
||||||
|
console.log("//");
|
||||||
|
console.log(uglify_code);
|
||||||
|
console.log();
|
||||||
|
console.log();
|
||||||
|
console.log("original result:");
|
||||||
|
console.log(original_result);
|
||||||
|
console.log("beautified result:");
|
||||||
|
console.log(beautify_result);
|
||||||
|
console.log("uglified result:");
|
||||||
|
console.log(uglify_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
var num_iterations = +process.argv[2] || 1/0;
|
||||||
|
var verbose = !!process.argv[3];
|
||||||
|
for (var round = 0; round < num_iterations; round++) {
|
||||||
|
process.stdout.write(round + " of " + num_iterations + "\r");
|
||||||
|
var original_code = [
|
||||||
|
"var a = 100, b = 10;",
|
||||||
|
createFunctionDecls(rng(3) + 1, 10),
|
||||||
|
"console.log(a, b);"
|
||||||
|
].join("\n");
|
||||||
|
var beautify_code = minify(original_code, {
|
||||||
|
fromString: true,
|
||||||
|
mangle: false,
|
||||||
|
compress: false,
|
||||||
|
output: {
|
||||||
|
beautify: true,
|
||||||
|
bracketize: true,
|
||||||
|
},
|
||||||
|
}).code;
|
||||||
|
|
||||||
|
var uglify_code = minify(beautify_code, {
|
||||||
|
fromString: true,
|
||||||
|
mangle: false,
|
||||||
|
compress: {
|
||||||
|
passes: 3,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
beautify: true,
|
||||||
|
bracketize: true,
|
||||||
|
},
|
||||||
|
}).code;
|
||||||
|
|
||||||
|
var original_result = run_code(original_code);
|
||||||
|
var beautify_result = run_code(beautify_code);
|
||||||
|
var uglify_result = run_code(uglify_code);
|
||||||
|
var ok = original_result == beautify_result && original_result == uglify_result;
|
||||||
|
if (verbose || !ok) log();
|
||||||
|
if (!ok) process.exit(1);
|
||||||
|
}
|
||||||
@@ -18,6 +18,6 @@ exports["tokenizer"] = tokenizer;
|
|||||||
exports["is_identifier"] = is_identifier;
|
exports["is_identifier"] = is_identifier;
|
||||||
exports["SymbolDef"] = SymbolDef;
|
exports["SymbolDef"] = SymbolDef;
|
||||||
|
|
||||||
if (typeof DEBUG !== "undefined" && DEBUG) {
|
if (global.UGLIFY_DEBUG) {
|
||||||
exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE;
|
exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
var path = require("path");
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
|
||||||
var FILES = exports.FILES = [
|
var UglifyJS = exports;
|
||||||
|
var FILES = UglifyJS.FILES = [
|
||||||
"../lib/utils.js",
|
"../lib/utils.js",
|
||||||
"../lib/ast.js",
|
"../lib/ast.js",
|
||||||
"../lib/parse.js",
|
"../lib/parse.js",
|
||||||
@@ -20,17 +21,14 @@ var FILES = exports.FILES = [
|
|||||||
"../lib/propmangle.js",
|
"../lib/propmangle.js",
|
||||||
"./exports.js",
|
"./exports.js",
|
||||||
].map(function(file){
|
].map(function(file){
|
||||||
return fs.realpathSync(path.join(path.dirname(__filename), file));
|
return require.resolve(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
var UglifyJS = exports;
|
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
|
||||||
|
|
||||||
new Function("MOZ_SourceMap", "exports", "DEBUG", FILES.map(function(file){
|
|
||||||
return fs.readFileSync(file, "utf8");
|
return fs.readFileSync(file, "utf8");
|
||||||
}).join("\n\n"))(
|
}).join("\n\n"))(
|
||||||
require("source-map"),
|
require("source-map"),
|
||||||
UglifyJS,
|
UglifyJS
|
||||||
!!global.UGLIFY_DEBUG
|
|
||||||
);
|
);
|
||||||
|
|
||||||
UglifyJS.AST_Node.warn_function = function(txt) {
|
UglifyJS.AST_Node.warn_function = function(txt) {
|
||||||
@@ -46,7 +44,7 @@ function read_source_map(code) {
|
|||||||
return JSON.parse(new Buffer(match[2], "base64"));
|
return JSON.parse(new Buffer(match[2], "base64"));
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.minify = function(files, options) {
|
UglifyJS.minify = function(files, options) {
|
||||||
options = UglifyJS.defaults(options, {
|
options = UglifyJS.defaults(options, {
|
||||||
spidermonkey : false,
|
spidermonkey : false,
|
||||||
outSourceMap : null,
|
outSourceMap : null,
|
||||||
@@ -181,7 +179,7 @@ exports.minify = function(files, options) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// exports.describe_ast = function() {
|
// UglifyJS.describe_ast = function() {
|
||||||
// function doitem(ctor) {
|
// function doitem(ctor) {
|
||||||
// var sub = {};
|
// var sub = {};
|
||||||
// ctor.SUBCLASSES.forEach(function(ctor){
|
// ctor.SUBCLASSES.forEach(function(ctor){
|
||||||
@@ -195,7 +193,7 @@ exports.minify = function(files, options) {
|
|||||||
// return doitem(UglifyJS.AST_Node).sub;
|
// return doitem(UglifyJS.AST_Node).sub;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
exports.describe_ast = function() {
|
UglifyJS.describe_ast = function() {
|
||||||
var out = UglifyJS.OutputStream({ beautify: true });
|
var out = UglifyJS.OutputStream({ beautify: true });
|
||||||
function doitem(ctor) {
|
function doitem(ctor) {
|
||||||
out.print("AST_" + ctor.TYPE);
|
out.print("AST_" + ctor.TYPE);
|
||||||
@@ -249,13 +247,13 @@ function readReservedFile(filename, reserved) {
|
|||||||
return reserved;
|
return reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.readReservedFile = readReservedFile;
|
UglifyJS.readReservedFile = readReservedFile;
|
||||||
|
|
||||||
exports.readDefaultReservedFile = function(reserved) {
|
UglifyJS.readDefaultReservedFile = function(reserved) {
|
||||||
return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
|
return readReservedFile(require.resolve("./domprops.json"), reserved);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.readNameCache = function(filename, key) {
|
UglifyJS.readNameCache = function(filename, key) {
|
||||||
var cache = null;
|
var cache = null;
|
||||||
if (filename) {
|
if (filename) {
|
||||||
try {
|
try {
|
||||||
@@ -273,7 +271,7 @@ exports.readNameCache = function(filename, key) {
|
|||||||
return cache;
|
return cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.writeNameCache = function(filename, key, cache) {
|
UglifyJS.writeNameCache = function(filename, key, cache) {
|
||||||
if (filename) {
|
if (filename) {
|
||||||
var data;
|
var data;
|
||||||
try {
|
try {
|
||||||
@@ -294,13 +292,9 @@ exports.writeNameCache = function(filename, key, cache) {
|
|||||||
// Example: "foo/bar/*baz??.*.js"
|
// Example: "foo/bar/*baz??.*.js"
|
||||||
// Argument `glob` may be a string or an array of strings.
|
// Argument `glob` may be a string or an array of strings.
|
||||||
// Returns an array of strings. Garbage in, garbage out.
|
// Returns an array of strings. Garbage in, garbage out.
|
||||||
exports.simple_glob = function simple_glob(glob) {
|
UglifyJS.simple_glob = function simple_glob(glob) {
|
||||||
var results = [];
|
|
||||||
if (Array.isArray(glob)) {
|
if (Array.isArray(glob)) {
|
||||||
glob.forEach(function(elem) {
|
return [].concat.apply([], glob.map(simple_glob));
|
||||||
results = results.concat(simple_glob(elem));
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
if (glob.match(/\*|\?/)) {
|
if (glob.match(/\*|\?/)) {
|
||||||
var dir = path.dirname(glob);
|
var dir = path.dirname(glob);
|
||||||
@@ -308,28 +302,19 @@ exports.simple_glob = function simple_glob(glob) {
|
|||||||
var entries = fs.readdirSync(dir);
|
var entries = fs.readdirSync(dir);
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
if (entries) {
|
if (entries) {
|
||||||
var pattern = "^" + (path.basename(glob)
|
var pattern = "^" + path.basename(glob)
|
||||||
.replace(/\(/g, "\\(")
|
.replace(/[.+^$[\]\\(){}]/g, "\\$&")
|
||||||
.replace(/\)/g, "\\)")
|
|
||||||
.replace(/\{/g, "\\{")
|
|
||||||
.replace(/\}/g, "\\}")
|
|
||||||
.replace(/\[/g, "\\[")
|
|
||||||
.replace(/\]/g, "\\]")
|
|
||||||
.replace(/\+/g, "\\+")
|
|
||||||
.replace(/\^/g, "\\^")
|
|
||||||
.replace(/\$/g, "\\$")
|
|
||||||
.replace(/\*/g, "[^/\\\\]*")
|
.replace(/\*/g, "[^/\\\\]*")
|
||||||
.replace(/\./g, "\\.")
|
.replace(/\?/g, "[^/\\\\]") + "$";
|
||||||
.replace(/\?/g, ".")) + "$";
|
|
||||||
var mod = process.platform === "win32" ? "i" : "";
|
var mod = process.platform === "win32" ? "i" : "";
|
||||||
var rx = new RegExp(pattern, mod);
|
var rx = new RegExp(pattern, mod);
|
||||||
for (var i in entries) {
|
var results = entries.filter(function(name) {
|
||||||
if (rx.test(entries[i]))
|
return rx.test(name);
|
||||||
results.push(dir + "/" + entries[i]);
|
}).map(function(name) {
|
||||||
}
|
return path.join(dir, name);
|
||||||
|
});
|
||||||
|
if (results.length) return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (results.length === 0)
|
return [ glob ];
|
||||||
results = [ glob ];
|
|
||||||
return results;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user