Compare commits

...

53 Commits

Author SHA1 Message Date
Alex Lam S.L
370c8e0385 v3.6.4 2019-10-23 15:38:05 +08:00
Alex Lam S.L
4240fba9b8 fix corner cases in unused (#3519) 2019-10-23 06:46:05 +08:00
Alex Lam S.L
267bc70d33 fix corner case in unused (#3517)
fixes #3515
2019-10-23 01:58:40 +08:00
Alex Lam S.L
a53ab99378 fix corner case in side_effects (#3514)
fixes #3512
2019-10-23 01:04:00 +08:00
Alex Lam S.L
02308a7b56 fix corner case in reduce_vars (#3510)
fixes #3509
2019-10-22 20:36:05 +08:00
Alex Lam S.L
0b3705e82f fix corner cases in inline (#3507)
fixes #3506
2019-10-22 15:41:55 +08:00
Alex Lam S.L
da5a21b240 fix GitHub Actions script for fuzzing (#3504) 2019-10-21 04:30:00 +08:00
Alex Lam S.L
5bd0cf8633 enable GitHub Actions (#3503) 2019-10-21 04:11:14 +08:00
Alex Lam S.L
9199ab5846 minor tweaks (#3502) 2019-10-20 15:19:19 +08:00
Alex Lam S.L
ca6dce43fe fix corner case in collapse_vars (#3501) 2019-10-20 03:53:20 +08:00
Alex Lam S.L
543dd7d3d7 fix corner case in comments (#3500) 2019-10-20 03:21:30 +08:00
Alex Lam S.L
6b4886c908 v3.6.3 2019-10-19 14:28:11 +08:00
Alex Lam S.L
0201cb4b52 fix corner case in unused (#3499)
fixes #3497
2019-10-18 20:08:05 +08:00
Alex Lam S.L
cd072317d0 fix corner case in unused (#3496)
fixes #3495
2019-10-18 17:09:43 +08:00
Alex Lam S.L
0785a15ace fix corner case in dead_code & ie8 (#3494)
fixes #3493
2019-10-17 09:58:05 +08:00
Alex Lam S.L
b1279a46d9 fix corner case in sequences (#3491)
fixes #3490
2019-10-17 09:57:50 +08:00
Alex Lam S.L
b571619d31 handle throw of non-Errors gracefully (#3492) 2019-10-17 06:29:02 +08:00
Alex Lam S.L
7b5350b459 tweak Travis CI execution environment (#3489) 2019-10-16 15:47:06 +08:00
Alex Lam S.L
1549db70e6 fix corner case in ie8 (#3487)
fixes #3486
2019-10-16 12:18:27 +08:00
Alex Lam S.L
8ff9a3c8fb fix corner cases in ie8 (#3485)
fixes #3484
2019-10-16 06:37:40 +08:00
Alex Lam S.L
91cae51d8f fix corner case in evaluate & ie8 (#3483)
fixes #3482
2019-10-16 01:09:16 +08:00
Alex Lam S.L
8af2f5fbcf fix corner case in rename (#3481)
fixes #3480
2019-10-15 19:44:07 +08:00
Alex Lam S.L
86a8016323 fix corner case in ie8 & mangle (#3479)
fixes #3478
2019-10-15 17:14:48 +08:00
David xu
009dcdae01 avoid mangling of MutationObserver properties (#3477) 2019-10-15 16:16:43 +08:00
Alex Lam S.L
f86f615d83 fix corner case in ie8 & mangle (#3476)
fixes #3475
2019-10-15 14:18:12 +08:00
Alex Lam S.L
d3d1d11926 fix corner case in ie8 & rename (#3474)
fixes #3473
2019-10-15 07:27:02 +08:00
Alex Lam S.L
736019b767 fix corner cases in ie8 (#3472)
fixes #3471
2019-10-14 18:15:40 +08:00
Alex Lam S.L
a39bdb5840 fix corner case with collapse_vars & ie8 (#3469)
fixes #3468
2019-10-14 13:34:35 +08:00
Alex Lam S.L
e8ab0a44b2 update dependency (#3470) 2019-10-14 13:34:22 +08:00
Alex Lam S.L
c3ca293e6b v3.6.2 2019-10-12 20:19:05 +08:00
Alex Lam S.L
516b67a43b minor tweaks to CI test scripts (#3467) 2019-10-12 05:36:38 +08:00
Alex Lam S.L
eba3a37bb5 fix boolean context detection (#3466)
fixes #3465
2019-10-12 03:42:57 +08:00
Alex Lam S.L
6d57ca1a59 improve source map handling (#3464)
fixes #2947
fixes #3277
fixes #3411
2019-10-11 03:52:33 +08:00
Alex Lam S.L
3320251b4b update benchmark URLs (#3462) 2019-10-11 01:00:09 +08:00
Alex Lam S.L
33c94d3bd9 detect boolean context across IIFEs (#3461) 2019-10-10 09:37:02 +08:00
Alex Lam S.L
b18f717b46 improve readability of --help ast (#3460) 2019-10-10 04:32:32 +08:00
Alex Lam S.L
a0d4b648bb remove extraneous property (#3459)
fixes #3455
2019-10-10 01:36:58 +08:00
Alex Lam S.L
6db880e16d clean up AST_Binary optimisation logic (#3458) 2019-10-09 23:45:41 +08:00
Alex Lam S.L
a82003d6ac v3.6.1 2019-10-07 14:36:46 +08:00
Alex Lam S.L
da9f1622fc report errors correctly in ufuzz (#3456) 2019-10-07 14:36:00 +08:00
Alex Lam S.L
8a4c7077bb account for catch in constant lambda expressions (#3454) 2019-10-06 16:51:37 +08:00
Alex Lam S.L
0a63f2f2b0 workaround V8 RegExp bug (#3453)
fixes #3434
2019-10-06 11:49:39 +08:00
Alex Lam S.L
931ac66638 fix corner case in hoist_props (#3452)
fixes #3440
2019-10-06 10:29:13 +08:00
Alex Lam S.L
35338a100f handle function/variable name collisions correctly (#3451)
fixes #3439
2019-10-06 08:51:38 +08:00
David xu
d57b606e73 exclude mangling of addEventListener parameters (#3445) 2019-10-06 05:29:08 +08:00
Sampson Crowley
00ada04111 facilitate Webpack compatibility (#3435)
Verbose application of `require.resolve` instead of `[].map`
2019-10-06 05:20:47 +08:00
Alex Lam S.L
a31c477fea fix variable scope determination (#3449)
fixes #3444
2019-10-06 05:13:44 +08:00
Alex Lam S.L
bde7418ce1 update & fix dependencies (#3450) 2019-10-06 03:10:12 +08:00
Alex Lam S.L
70bb304a0a v3.6.0 2019-05-30 15:30:00 +08:00
Alex Lam S.L
9d3b1efd86 fix corner case in assignments (#3430)
fixes #3429
2019-05-30 05:01:53 +08:00
Alex Lam S.L
482e1baea3 enhance assignments & unused (#3428)
closes #3427
2019-05-29 01:21:08 +08:00
Alex Lam S.L
e4f5ba1d29 v3.5.15 2019-05-21 14:26:58 +08:00
Alex Lam S.L
b9053c7a25 fix corner case in keep_fargs (#3424)
fixes #3423
2019-05-21 12:55:34 +08:00
38 changed files with 2711 additions and 495 deletions

27
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: CI
on: [ push, pull_request ]
jobs:
test:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
node: [ "0.10", 0.12, 4, 6, 8, 10, latest ]
script: [ compress, mocha, release/benchmark, release/jetstream ]
name: ${{ matrix.os }} ${{ matrix.node }} ${{ matrix.script }}
runs-on: ${{ matrix.os }}
env:
NODE: ${{ matrix.node }}
TYPE: ${{ matrix.script }}
steps:
- uses: actions/checkout@v1
- shell: bash
run: |
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh
nvs --version
nvs add node/$NODE
nvs use node/$NODE
node --version
npm --version --no-update-notifier
npm install --no-audit --no-optional --no-save --no-update-notifier
node test/$TYPE

24
.github/workflows/ufuzz.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Fuzzing
on:
schedule:
- cron: "*/15 * * * *"
jobs:
ufuzz:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- shell: bash
run: |
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh
nvs --version
nvs add node
nvs use node
node --version
npm --version --no-update-notifier
npm install --no-audit --no-optional --no-save --no-update-notifier
node test/ufuzz/job 3600000

View File

@@ -1,6 +1,6 @@
cache: cache:
directories: tmp directories: tmp
language: generic language: shell
matrix: matrix:
fast_finish: true fast_finish: true
env: env:

View File

@@ -837,11 +837,10 @@ var AST_String = DEFNODE("String", "value quote", {
} }
}, AST_Constant); }, AST_Constant);
var AST_Number = DEFNODE("Number", "value literal", { var AST_Number = DEFNODE("Number", "value", {
$documentation: "A number literal", $documentation: "A number literal",
$propdoc: { $propdoc: {
value: "[number] the numeric value", value: "[number] the numeric value",
literal: "[string] numeric value as string (optional)"
} }
}, AST_Constant); }, AST_Constant);
@@ -980,6 +979,15 @@ TreeWalker.prototype = {
|| p instanceof AST_Conditional || p instanceof AST_Conditional
|| p.tail_node() === self) { || p.tail_node() === self) {
self = p; self = p;
} else if (p instanceof AST_Return) {
var fn;
do {
fn = this.parent(++i);
if (!fn) return false;
} while (!(fn instanceof AST_Lambda));
if (fn.name) return false;
self = this.parent(++i);
if (!self || self.TYPE != "Call" || self.expression !== fn) return false;
} else { } else {
return false; return false;
} }

View File

@@ -108,7 +108,7 @@ function Compressor(options, false_by_default) {
this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) { this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) {
if (lambda.length_read) return false; if (lambda.length_read) return false;
var name = lambda.name; var name = lambda.name;
if (!name) return parent && parent.TYPE == "Call"; if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
if (name.fixed_value() !== lambda) return false; if (name.fixed_value() !== lambda) return false;
var def = name.definition(); var def = name.definition();
if (def.direct_access) return false; if (def.direct_access) return false;
@@ -888,16 +888,17 @@ merge(Compressor.prototype, {
}); });
AST_SymbolRef.DEFMETHOD("is_immutable", function() { AST_SymbolRef.DEFMETHOD("is_immutable", function() {
var orig = this.definition().orig; var def = this.definition();
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda; if (def.orig.length != 1) return false;
var sym = def.orig[0];
return sym instanceof AST_SymbolLambda && def.scope.name === sym;
}); });
function is_lhs_read_only(lhs, compressor) { function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_This) return true; if (lhs instanceof AST_This) return true;
if (lhs instanceof AST_SymbolRef) { if (lhs instanceof AST_SymbolRef) {
var def = lhs.definition(); var def = lhs.definition();
return def.orig[0] instanceof AST_SymbolLambda return def.lambda || compressor.exposed(def) && identifier_atom[def.name];
|| compressor.exposed(def) && identifier_atom[def.name];
} }
if (lhs instanceof AST_PropAccess) { if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression; lhs = lhs.expression;
@@ -1293,6 +1294,7 @@ merge(Compressor.prototype, {
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression); return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
} }
if (node instanceof AST_Debugger) return true; if (node instanceof AST_Debugger) return true;
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
if (node instanceof AST_IterationStatement) return !(node instanceof AST_For); if (node instanceof AST_IterationStatement) return !(node instanceof AST_For);
if (node instanceof AST_LoopControl) return true; if (node instanceof AST_LoopControl) return true;
if (node instanceof AST_Try) return true; if (node instanceof AST_Try) return true;
@@ -1463,50 +1465,20 @@ merge(Compressor.prototype, {
hit_stack.pop(); hit_stack.pop();
} }
function find_stop(node, level, write_only) { function find_stop(node, level) {
var parent = scanner.parent(level); var parent = scanner.parent(level);
if (parent instanceof AST_Assign) { if (parent instanceof AST_Assign) return node;
if (write_only if (parent instanceof AST_Binary) return node;
&& !(parent.left instanceof AST_PropAccess
|| parent.left.name in lvalues)) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_Binary) {
if (write_only && (!lazy_op[parent.operator] || parent.left === node)) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_Call) return node; if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node; if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) { if (parent instanceof AST_Conditional) return node;
if (write_only && parent.condition === node) { if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
return find_stop(parent, level + 1, write_only); if (parent instanceof AST_Exit) return node;
} if (parent instanceof AST_If) return node;
return node;
}
if (parent instanceof AST_Definitions) {
return find_stop(parent, level + 1, true);
}
if (parent instanceof AST_Exit) {
return write_only ? find_stop(parent, level + 1, write_only) : node;
}
if (parent instanceof AST_If) {
if (write_only && parent.condition === node) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_IterationStatement) return node; if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_PropAccess) return node; if (parent instanceof AST_PropAccess) return node;
if (parent instanceof AST_Sequence) { if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
return find_stop(parent, level + 1, parent.tail_node() !== node); if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
}
if (parent instanceof AST_SimpleStatement) {
return find_stop(parent, level + 1, true);
}
if (parent instanceof AST_Switch) return node; if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_Unary) return node; if (parent instanceof AST_Unary) return node;
if (parent instanceof AST_VarDef) return node; if (parent instanceof AST_VarDef) return node;
@@ -2030,14 +2002,14 @@ merge(Compressor.prototype, {
n--; n--;
CHANGED = true; CHANGED = true;
var left = prev.body; var left = prev.body;
return make_sequence(left, [ left, right ]).transform(compressor); return make_sequence(left, [ left, right ]);
} }
var n = 0, prev; var n = 0, prev;
for (var i = 0; i < statements.length; i++) { for (var i = 0; i < statements.length; i++) {
var stat = statements[i]; var stat = statements[i];
if (prev) { if (prev) {
if (stat instanceof AST_Exit) { if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor)); stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).transform(compressor);
} else if (stat instanceof AST_For) { } else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) { if (!(stat.init instanceof AST_Definitions)) {
var abort = false; var abort = false;
@@ -2282,8 +2254,7 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor` // returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) { (function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) { AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
return !compressor.option("pure_getters") return !compressor.option("pure_getters") || this._dot_throw(compressor);
|| this._dot_throw(compressor);
}); });
function is_strict(compressor) { function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters")); return /strict/.test(compressor.option("pure_getters"));
@@ -2291,10 +2262,25 @@ merge(Compressor.prototype, {
def(AST_Node, is_strict); def(AST_Node, is_strict);
def(AST_Array, return_false); def(AST_Array, return_false);
def(AST_Assign, function(compressor) { def(AST_Assign, function(compressor) {
return this.operator == "=" && this.right._dot_throw(compressor); if (this.operator != "=") return false;
var rhs = this.right;
if (!rhs._dot_throw(compressor)) return false;
var sym = this.left;
if (!(sym instanceof AST_SymbolRef)) return true;
if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
return rhs.right._dot_throw(compressor);
}
return true;
}); });
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor)); switch (this.operator) {
case "&&":
return this.left._dot_throw(compressor) || this.right._dot_throw(compressor);
case "||":
return this.right._dot_throw(compressor);
default:
return false;
}
}); });
def(AST_Conditional, function(compressor) { def(AST_Conditional, function(compressor) {
return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor); return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
@@ -3392,16 +3378,28 @@ merge(Compressor.prototype, {
def(AST_Lambda, function(scope) { def(AST_Lambda, function(scope) {
var self = this; var self = this;
var result = true; var result = true;
self.walk(new TreeWalker(function(node) { var inner_scopes = [];
self.walk(new TreeWalker(function(node, descend) {
if (!result) return true; if (!result) return true;
if (node instanceof AST_Catch) {
inner_scopes.push(node.argname.scope);
descend();
inner_scopes.pop();
return true;
}
if (node instanceof AST_Scope && node !== self) {
inner_scopes.push(node);
descend();
inner_scopes.pop();
return true;
}
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
if (self.inlined) { if (self.inlined) {
result = false; result = false;
return true; return true;
} }
var def = node.definition(); var def = node.definition();
if (member(def, self.enclosed) if (!self.variables.has(def.name) && !member(def.scope, inner_scopes)) {
&& !self.variables.has(def.name)) {
if (scope) { if (scope) {
var scope_def = scope.find_variable(node); var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) { if (def.undeclared ? !scope_def : scope_def === def) {
@@ -3507,10 +3505,10 @@ merge(Compressor.prototype, {
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) { var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
var sym; var sym;
if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) { if (node instanceof AST_Assign) {
sym = node.left; if (node.write_only || node.operator == "=") sym = node.left;
} else if (node instanceof AST_Unary && node.write_only) { } else if (node instanceof AST_Unary) {
sym = node.expression; if (node.write_only) sym = node.expression;
} }
if (!/strict/.test(compressor.option("pure_getters"))) return sym instanceof AST_SymbolRef && sym; if (!/strict/.test(compressor.option("pure_getters"))) return sym instanceof AST_SymbolRef && sym;
while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) { while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
@@ -3608,6 +3606,7 @@ merge(Compressor.prototype, {
return !(def.id in in_use_ids) || def.orig.length > 1; return !(def.id in in_use_ids) || def.orig.length > 1;
}; };
// pass 3: we should drop declarations not in_use // pass 3: we should drop declarations not in_use
var unused_fn_names = [];
var tt = new TreeTransformer(function(node, descend, in_list) { var tt = new TreeTransformer(function(node, descend, in_list) {
var parent = tt.parent(); var parent = tt.parent();
if (drop_vars) { if (drop_vars) {
@@ -3615,10 +3614,10 @@ merge(Compressor.prototype, {
if (sym) { if (sym) {
var def = sym.definition(); var def = sym.definition();
var in_use = def.id in in_use_ids; var in_use = def.id in in_use_ids;
var value = null; var value;
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) { if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
value = node.right; value = get_rhs(node);
} }
} else if (!in_use) { } else if (!in_use) {
value = make_node(AST_Number, node, { value = make_node(AST_Number, node, {
@@ -3635,7 +3634,7 @@ merge(Compressor.prototype, {
} }
if (scope !== self) return; if (scope !== self) return;
if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) { if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) {
node.name = null; unused_fn_names.push(node);
} }
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = compressor.drop_fargs(node, parent); var trim = compressor.drop_fargs(node, parent);
@@ -3644,8 +3643,8 @@ merge(Compressor.prototype, {
if (!(sym.definition().id in in_use_ids)) { if (!(sym.definition().id in in_use_ids)) {
sym.__unused = true; sym.__unused = true;
if (trim) { if (trim) {
log(sym, "Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
a.pop(); a.pop();
AST_Node[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
} }
} else { } else {
trim = false; trim = false;
@@ -3655,7 +3654,7 @@ merge(Compressor.prototype, {
if (drop_funcs && node instanceof AST_Defun && node !== self) { if (drop_funcs && node instanceof AST_Defun && node !== self) {
var def = node.name.definition(); var def = node.name.definition();
if (!(def.id in in_use_ids)) { if (!(def.id in in_use_ids)) {
AST_Node[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); log(node.name, "Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++; def.eliminated++;
return make_node(AST_EmptyStatement, node); return make_node(AST_EmptyStatement, node);
} }
@@ -3697,6 +3696,7 @@ merge(Compressor.prototype, {
if (!def.value) { if (!def.value) {
head.push(def); head.push(def);
} else if (compressor.option("functions") } else if (compressor.option("functions")
&& !compressor.option("ie8")
&& def.value === def.name.fixed_value() && def.value === def.name.fixed_value()
&& def.value instanceof AST_Function && def.value instanceof AST_Function
&& !(def.value.name && def.value.name.definition().assignments) && !(def.value.name && def.value.name.definition().assignments)
@@ -3737,12 +3737,14 @@ merge(Compressor.prototype, {
def.value = null; def.value = null;
head.push(def); head.push(def);
} else { } else {
var value = def.value && def.value.drop_side_effect_free(compressor); var value = def.value
&& !def.value.single_use
&& def.value.drop_side_effect_free(compressor);
if (value) { if (value) {
AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value); side_effects.push(value);
} else { } else {
AST_Node[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); log(def.name, "Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
} }
sym.eliminated++; sym.eliminated++;
} }
@@ -3820,6 +3822,10 @@ merge(Compressor.prototype, {
return node; return node;
} }
function log(sym, text, props) {
AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
}
function template(sym) { function template(sym) {
return { return {
name : sym.name, name : sym.name,
@@ -3831,6 +3837,9 @@ merge(Compressor.prototype, {
}); });
tt.push(compressor.parent()); tt.push(compressor.parent());
self.transform(tt); self.transform(tt);
unused_fn_names.forEach(function(fn) {
fn.name = null;
});
function verify_safe_usage(def, read, modified) { function verify_safe_usage(def, read, modified) {
if (def.id in in_use_ids) return; if (def.id in in_use_ids) return;
@@ -3843,6 +3852,15 @@ merge(Compressor.prototype, {
} }
} }
function get_rhs(assign) {
var rhs = assign.right;
if (!assign.write_only) return rhs;
if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
var sym = assign.left;
if (!(sym instanceof AST_SymbolRef) || sym.name != rhs.left.name) return rhs;
return rhs.right.has_side_effects(compressor) ? rhs : rhs.right;
}
function scan_ref_scoped(node, descend) { function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props); var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) { if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
@@ -3850,9 +3868,11 @@ merge(Compressor.prototype, {
prop.walk(tw); prop.walk(tw);
}); });
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
node.right.walk(tw); if (node.write_only === "p" && node.right.may_throw_on_access(compressor)) return;
var right = get_rhs(node);
right.walk(tw);
if (node.left === sym) { if (node.left === sym) {
if (!node_def.chained && sym.fixed_value(true) === node.right) { if (!node_def.chained && sym.fixed_value(true) === right) {
fixed_ids[node_def.id] = node; fixed_ids[node_def.id] = node;
} }
if (!node.write_only) { if (!node.write_only) {
@@ -4038,10 +4058,11 @@ merge(Compressor.prototype, {
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null); var defs_by_id = Object.create(null);
self.transform(new TreeTransformer(function(node, descend) { self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_Assign if (node instanceof AST_Assign) {
&& node.operator == "=" if (node.operator != "=") return;
&& node.write_only if (!node.write_only) return;
&& can_hoist(node.left, node.right, 1)) { if (node.left.scope !== self) return;
if (!can_hoist(node.left, node.right, 1)) return;
descend(node, this); descend(node, this);
var defs = new Dictionary(); var defs = new Dictionary();
var assignments = []; var assignments = [];
@@ -4070,17 +4091,9 @@ merge(Compressor.prototype, {
})); }));
return make_sequence(node, assignments); return make_sequence(node, assignments);
} }
if (node instanceof AST_Unary if (node instanceof AST_Scope) return node === self ? undefined : node;
&& !unary_side_effects[node.operator] if (node instanceof AST_VarDef) {
&& node.expression instanceof AST_SymbolRef if (!can_hoist(node.name, node.value, 0)) return;
&& node.expression.definition().id in defs_by_id) {
node = node.clone();
node.expression = make_node(AST_Object, node, {
properties: []
});
return node;
}
if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) {
descend(node, this); descend(node, this);
var defs = new Dictionary(); var defs = new Dictionary();
var var_defs = []; var var_defs = [];
@@ -4093,32 +4106,6 @@ merge(Compressor.prototype, {
defs_by_id[node.name.definition().id] = defs; defs_by_id[node.name.definition().id] = defs;
return MAP.splice(var_defs); return MAP.splice(var_defs);
} }
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var def = defs.get(node.getProperty());
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function can_hoist(sym, right, count) {
if (sym.scope !== self) return;
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped.depth == 1) return;
if (def.references.length == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
return right instanceof AST_Object;
}
function make_sym(sym, key) { function make_sym(sym, key) {
var new_var = make_node(AST_SymbolVar, sym, { var new_var = make_node(AST_SymbolVar, sym, {
@@ -4131,7 +4118,53 @@ merge(Compressor.prototype, {
return new_var; return new_var;
} }
})); }));
self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_PropAccess) {
if (!(node.expression instanceof AST_SymbolRef)) return;
var defs = defs_by_id[node.expression.definition().id];
if (!defs) return;
var def = defs.get(node.getProperty());
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
}); });
sym.reference({});
return sym;
}
if (node instanceof AST_Unary) {
if (unary_side_effects[node.operator]) return;
if (!(node.expression instanceof AST_SymbolRef)) return;
if (!(node.expression.definition().id in defs_by_id)) return;
var opt = node.clone();
opt.expression = make_node(AST_Object, node, {
properties: []
});
return opt;
}
}));
function can_hoist(sym, right, count) {
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped.depth == 1) return;
if (def.references.length == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
return right instanceof AST_Object;
}
});
function safe_to_drop(fn, compressor) {
if (!fn.name || !compressor.option("ie8")) return true;
var def = fn.name.definition();
if (compressor.exposed(def)) return false;
return all(def.references, function(sym) {
return !(sym instanceof AST_SymbolRef);
});
}
// drop_side_effect_free() // drop_side_effect_free()
// remove side-effect-free parts which only affects return value // remove side-effect-free parts which only affects return value
@@ -4163,12 +4196,14 @@ merge(Compressor.prototype, {
}); });
def(AST_Assign, function(compressor) { def(AST_Assign, function(compressor) {
var left = this.left; var left = this.left;
if (left.has_side_effects(compressor) if (left instanceof AST_PropAccess) {
|| compressor.has_directive("use strict") var expr = left.expression;
&& left instanceof AST_PropAccess if (expr instanceof AST_Assign && expr.operator == "=" && !expr.may_throw_on_access(compressor)) {
&& left.expression.is_constant()) { expr.write_only = "p";
return this;
} }
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
this.write_only = true; this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) { if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor); return this.right.drop_side_effect_free(compressor);
@@ -4243,11 +4278,12 @@ merge(Compressor.prototype, {
}); });
def(AST_Constant, return_null); def(AST_Constant, return_null);
def(AST_Dot, function(compressor, first_in_statement) { def(AST_Dot, function(compressor, first_in_statement) {
if (this.expression.may_throw_on_access(compressor)) return this; var expr = this.expression;
return this.expression.drop_side_effect_free(compressor, first_in_statement); if (expr.may_throw_on_access(compressor)) return this;
return expr.drop_side_effect_free(compressor, first_in_statement);
}); });
def(AST_Function, function(compressor) { def(AST_Function, function(compressor) {
return this.name && compressor.option("ie8") ? this : null; return safe_to_drop(this, compressor) ? null : this;
}); });
def(AST_Object, function(compressor, first_in_statement) { def(AST_Object, function(compressor, first_in_statement) {
var values = trim(this.properties, compressor, first_in_statement); var values = trim(this.properties, compressor, first_in_statement);
@@ -5133,7 +5169,9 @@ merge(Compressor.prototype, {
fn._squeezed = true; fn._squeezed = true;
return make_sequence(self, flatten_fn()).optimize(compressor); return make_sequence(self, flatten_fn()).optimize(compressor);
} }
if (compressor.option("side_effects") && all(fn.body, is_empty)) { if (compressor.option("side_effects")
&& all(fn.body, is_empty)
&& (fn !== exp || safe_to_drop(fn, compressor))) {
var args = self.args.concat(make_node(AST_Undefined, self)); var args = self.args.concat(make_node(AST_Undefined, self));
return make_sequence(self, args).optimize(compressor); return make_sequence(self, args).optimize(compressor);
} }
@@ -5170,7 +5208,7 @@ merge(Compressor.prototype, {
if (stat instanceof AST_SimpleStatement) { if (stat instanceof AST_SimpleStatement) {
return make_node(AST_UnaryPrefix, stat, { return make_node(AST_UnaryPrefix, stat, {
operator: "void", operator: "void",
expression: stat.body.clone(true) expression: stat.body
}); });
} }
} }
@@ -5200,25 +5238,26 @@ merge(Compressor.prototype, {
return return_value(stat); return return_value(stat);
} }
function var_exists(catches, name) { function var_exists(defined, name) {
return catches[name] || identifier_atom[name] || scope.var_names()[name]; return defined[name] || identifier_atom[name] || scope.var_names()[name];
} }
function can_inject_args(catches, safe_to_inject) { function can_inject_args(catches, used, safe_to_inject) {
for (var i = 0; i < fn.argnames.length; i++) { for (var i = 0; i < fn.argnames.length; i++) {
var arg = fn.argnames[i]; var arg = fn.argnames[i];
if (arg.__unused) continue; if (arg.__unused) continue;
if (!safe_to_inject || var_exists(catches, arg.name)) return false; if (!safe_to_inject || var_exists(catches, arg.name)) return false;
used[arg.name] = true;
if (in_loop) in_loop.push(arg.definition()); if (in_loop) in_loop.push(arg.definition());
} }
return true; return true;
} }
function can_inject_vars(catches, safe_to_inject) { function can_inject_vars(catches, used, safe_to_inject) {
for (var i = 0; i < fn.body.length; i++) { for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i]; var stat = fn.body[i];
if (stat instanceof AST_Defun) { if (stat instanceof AST_Defun) {
if (!safe_to_inject || var_exists(catches, stat.name.name)) return false; if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
continue; continue;
} }
if (!(stat instanceof AST_Var)) continue; if (!(stat instanceof AST_Var)) continue;
@@ -5247,8 +5286,9 @@ merge(Compressor.prototype, {
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars) var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
&& (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope)); && (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope));
var inline = compressor.option("inline"); var inline = compressor.option("inline");
if (!can_inject_vars(catches, inline >= 3 && safe_to_inject)) return false; var used = Object.create(catches);
if (!can_inject_args(catches, inline >= 2 && safe_to_inject)) return false; if (!can_inject_args(catches, used, inline >= 2 && safe_to_inject)) return false;
if (!can_inject_vars(catches, used, inline >= 3 && safe_to_inject)) return false;
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop); return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
} }
@@ -5556,20 +5596,29 @@ merge(Compressor.prototype, {
self.right = tmp; self.right = tmp;
} }
} }
if (commutativeOperators[self.operator]) { if (commutativeOperators[self.operator] && self.right.is_constant() && !self.left.is_constant()) {
if (self.right.is_constant()
&& !self.left.is_constant()) {
// if right is a constant, whatever side effects the // if right is a constant, whatever side effects the
// left side might have could not influence the // left side might have could not influence the
// result. hence, force switch. // result. hence, force switch.
if (!(self.left instanceof AST_Binary if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse(); reverse();
} }
} }
}
self = self.lift_sequences(compressor); self = self.lift_sequences(compressor);
if (compressor.option("assignments") && lazy_op[self.operator]) {
var assign = self.right;
// a || (a = x) => a = a || x
// a && (a = x) => a = a && x
if (self.left instanceof AST_SymbolRef
&& assign instanceof AST_Assign
&& assign.operator == "="
&& self.left.equivalent_to(assign.left)) {
self.right = assign.right;
assign.right = self;
return assign;
}
}
if (compressor.option("comparisons")) switch (self.operator) { if (compressor.option("comparisons")) switch (self.operator) {
case "===": case "===":
case "!==": case "!==":
@@ -5648,7 +5697,8 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) { var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (in_bool) switch (self.operator) {
case "+": case "+":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
@@ -5681,9 +5731,9 @@ merge(Compressor.prototype, {
} }
break; break;
} }
var parent = compressor.parent();
if (compressor.option("comparisons") && self.is_boolean(compressor)) { if (compressor.option("comparisons") && self.is_boolean(compressor)) {
if (!(compressor.parent() instanceof AST_Binary) if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
|| compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, { var negated = make_node(AST_UnaryPrefix, self, {
operator: "!", operator: "!",
expression: self.negate(compressor, first_in_statement(compressor)) expression: self.negate(compressor, first_in_statement(compressor))
@@ -5721,14 +5771,14 @@ merge(Compressor.prototype, {
var ll = fuzzy_eval(self.left); var ll = fuzzy_eval(self.left);
if (!ll) { if (!ll) {
AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
} else if (!(ll instanceof AST_Node)) { } else if (!(ll instanceof AST_Node)) {
AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (in_bool) {
AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start); AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
@@ -5736,9 +5786,7 @@ merge(Compressor.prototype, {
]).optimize(compressor); ]).optimize(compressor);
} else self.falsy = true; } else self.falsy = true;
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent(); if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start); AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
@@ -5760,18 +5808,16 @@ merge(Compressor.prototype, {
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} else if (!(ll instanceof AST_Node)) { } else if (!(ll instanceof AST_Node)) {
AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
var parent = compressor.parent(); if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start); AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (in_bool) {
AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start); AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
@@ -5969,12 +6015,11 @@ merge(Compressor.prototype, {
} }
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
var indexRight = is_indexFn(self.right); var indexRight = is_indexFn(self.right);
if (compressor.option("booleans") if (in_bool
&& indexRight && indexRight
&& (self.operator == "==" || self.operator == "!=") && (self.operator == "==" || self.operator == "!=")
&& self.left instanceof AST_Number && self.left instanceof AST_Number
&& self.left.getValue() == 0 && self.left.getValue() == 0) {
&& compressor.in_boolean_context()) {
return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, { return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
operator: "!", operator: "!",
expression: self.right expression: self.right
@@ -6113,6 +6158,7 @@ merge(Compressor.prototype, {
if (single_use && fixed) { if (single_use && fixed) {
def.single_use = false; def.single_use = false;
fixed._squeezed = true; fixed._squeezed = true;
fixed.single_use = true;
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Defun) {
fixed = make_node(AST_Function, fixed, fixed); fixed = make_node(AST_Function, fixed, fixed);
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name); fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
@@ -6140,7 +6186,9 @@ merge(Compressor.prototype, {
var fn = node.fixed_value(); var fn = node.fixed_value();
if (!(fn instanceof AST_Lambda)) return; if (!(fn instanceof AST_Lambda)) return;
if (!fn.name) return; if (!fn.name) return;
if (fixed.variables.get(fn.name.name) !== fn.name.definition()) return; var fn_def = fn.name.definition();
if (fn_def.scope !== fn.name.scope) return;
if (fixed.variables.get(fn.name.name) !== fn_def) return;
fn.name = fn.name.clone(); fn.name = fn.name.clone();
var value_def = value.variables.get(fn.name.name) || value.def_function(fn.name); var value_def = value.variables.get(fn.name.name) || value.def_function(fn.name);
node.thedef = value_def; node.thedef = value_def;

View File

@@ -1,19 +1,39 @@
"use strict"; "use strict";
var to_ascii = typeof atob == "undefined" ? function(b64) { var to_ascii, to_base64;
if (typeof Buffer == "undefined") {
to_ascii = atob;
to_base64 = btoa;
} else if (typeof Buffer.alloc == "undefined") {
to_ascii = function(b64) {
return new Buffer(b64, "base64").toString(); return new Buffer(b64, "base64").toString();
} : atob; };
var to_base64 = typeof btoa == "undefined" ? function(str) { to_base64 = function(str) {
return new Buffer(str).toString("base64"); return new Buffer(str).toString("base64");
} : btoa; };
} else {
to_ascii = function(b64) {
return Buffer.from(b64, "base64").toString();
};
to_base64 = function(str) {
return Buffer.from(str).toString("base64");
};
}
function read_source_map(name, code) { function read_source_map(name, toplevel) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(\S+)\s*$/.exec(code); var comments = toplevel.end.comments_after;
if (!match) { for (var i = comments.length; --i >= 0;) {
AST_Node.warn("inline source map not found: " + name); var comment = comments[i];
return null; if (comment.type != "comment1") break;
} var match = /^# ([^\s=]+)=(\S+)\s*$/.exec(comment.value);
if (!match) break;
if (match[1] == "sourceMappingURL") {
match = /^data:application\/json(;.*?)?;base64,(\S+)$/.exec(match[2]);
if (!match) break;
return to_ascii(match[2]); return to_ascii(match[2]);
}
}
AST_Node.warn("inline source map not found: " + name);
} }
function parse_source_map(content) { function parse_source_map(content) {
@@ -134,10 +154,10 @@ function minify(files, options) {
source_maps = source_map_content && Object.create(null); source_maps = source_map_content && Object.create(null);
for (var name in files) if (HOP(files, name)) { for (var name in files) if (HOP(files, name)) {
options.parse.filename = name; options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse); options.parse.toplevel = toplevel = parse(files[name], options.parse);
if (source_maps) { if (source_maps) {
if (source_map_content == "inline") { if (source_map_content == "inline") {
var inlined_content = read_source_map(name, files[name]); var inlined_content = read_source_map(name, toplevel);
if (inlined_content) { if (inlined_content) {
source_maps[name] = parse_source_map(inlined_content); source_maps[name] = parse_source_map(inlined_content);
} }
@@ -146,7 +166,6 @@ function minify(files, options) {
} }
} }
} }
toplevel = options.parse.toplevel;
} }
if (quoted_props) { if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props); reserve_quoted_keys(toplevel, quoted_props);

View File

@@ -403,7 +403,7 @@
var def = M.definition(); var def = M.definition();
return { return {
type: "Identifier", type: "Identifier",
name: def ? def.mangled_name || def.name : M.name name: def && def.mangled_name || M.name
}; };
}); });

View File

@@ -91,13 +91,11 @@ function OutputStream(options) {
comment_filter = function(comment) { comment_filter = function(comment) {
return comment.type != "comment5" && comments.test(comment.value); return comment.type != "comment5" && comments.test(comment.value);
}; };
} } else if (typeof comments === "function") {
else if (typeof comments === "function") {
comment_filter = function(comment) { comment_filter = function(comment) {
return comment.type != "comment5" && comments(this, comment); return comment.type != "comment5" && comments(this, comment);
}; };
} } else if (comments === "some") {
else if (comments === "some") {
comment_filter = is_some_comments; comment_filter = is_some_comments;
} else { // NOTE includes "all" option } else { // NOTE includes "all" option
comment_filter = return_true; comment_filter = return_true;
@@ -136,8 +134,7 @@ function OutputStream(options) {
function make_string(str, quote) { function make_string(str, quote) {
var dq = 0, sq = 0; var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
function(s, i) {
switch (s) { switch (s) {
case '"': ++dq; return '"'; case '"': ++dq; return '"';
case "'": ++sq; return "'"; case "'": ++sq; return "'";
@@ -453,7 +450,7 @@ function OutputStream(options) {
var self = this; var self = this;
var scan = node instanceof AST_Exit && node.value; var scan = node instanceof AST_Exit && node.value;
var comments = dump(node); var comments = dump(node);
if (!comments) return; if (!comments) comments = [];
if (scan) { if (scan) {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
@@ -599,7 +596,6 @@ function OutputStream(options) {
} }
print(encoded); print(encoded);
}, },
encode_string : encode_string,
next_indent : next_indent, next_indent : next_indent,
with_indent : with_indent, with_indent : with_indent,
with_block : with_block, with_block : with_block,
@@ -645,8 +641,7 @@ function OutputStream(options) {
var self = this, generator = self._codegen; var self = this, generator = self._codegen;
if (self instanceof AST_Scope) { if (self instanceof AST_Scope) {
active_scope = self; active_scope = self;
} } else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope; use_asm = active_scope;
} }
function doit() { function doit() {
@@ -1045,11 +1040,9 @@ function OutputStream(options) {
return; return;
} }
b = b.alternative; b = b.alternative;
} } else if (b instanceof AST_StatementWithBody) {
else if (b instanceof AST_StatementWithBody) {
b = b.body; b = b.body;
} } else break;
else break;
} }
force_statement(self.body, output); force_statement(self.body, output);
} }
@@ -1357,7 +1350,7 @@ function OutputStream(options) {
}); });
DEFPRINT(AST_Symbol, function(self, output) { DEFPRINT(AST_Symbol, function(self, output) {
var def = self.definition(); var def = self.definition();
output.print_name(def ? def.mangled_name || def.name : self.name); output.print_name(def && def.mangled_name || self.name);
}); });
DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output) { DEFPRINT(AST_This, function(self, output) {
@@ -1383,8 +1376,27 @@ function OutputStream(options) {
if (regexp.raw_source) { if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/")); str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
} }
str = output.to_utf8(str); output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(seq) {
output.print(str); switch (seq[1]) {
case "\n": return "\\n";
case "\r": return "\\r";
case "\t": return "\t";
case "\b": return "\b";
case "\f": return "\f";
case "\0": return "\0";
case "\x0B": return "\v";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
default: return seq;
}
}).replace(/[\n\r\u2028\u2029]/g, function(c) {
switch (c) {
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
}
}));
var p = output.parent(); var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
output.print(" "); output.print(" ");

View File

@@ -234,6 +234,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
directives : {}, directives : {},
directive_stack : [] directive_stack : []
}; };
var prev_was_dot = false;
function peek() { function peek() {
return S.text.charAt(S.pos); return S.text.charAt(S.pos);
@@ -286,16 +287,12 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
S.tokpos = S.pos; S.tokpos = S.pos;
} }
var prev_was_dot = false;
function token(type, value, is_comment) { function token(type, value, is_comment) {
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX[value]) || S.regex_allowed = type == "operator" && !UNARY_POSTFIX[value]
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION[value]) || || type == "keyword" && KEYWORDS_BEFORE_EXPRESSION[value]
(type == "punc" && PUNC_BEFORE_EXPRESSION[value])); || type == "punc" && PUNC_BEFORE_EXPRESSION[value];
if (type == "punc" && value == ".") { if (type == "punc" && value == ".") prev_was_dot = true;
prev_was_dot = true; else if (!is_comment) prev_was_dot = false;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = { var ret = {
type : type, type : type,
value : value, value : value,
@@ -358,12 +355,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
parse_error("Legacy octal literals are not allowed in strict mode"); parse_error("Legacy octal literals are not allowed in strict mode");
} }
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) { if (!isNaN(valid)) return token("num", valid);
return token("num", valid);
} else {
parse_error("Invalid syntax: " + num); parse_error("Invalid syntax: " + num);
} }
}
function read_escaped_char(in_string) { function read_escaped_char(in_string) {
var ch = next(true, in_string); var ch = next(true, in_string);
@@ -463,8 +457,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if (ch == "\\") escaped = backslash = true, next(); if (ch == "\\") escaped = backslash = true, next();
else if (is_identifier_char(ch)) name += next(); else if (is_identifier_char(ch)) name += next();
else break; else break;
} } else {
else {
if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX"); if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
ch = read_escaped_char(); ch = read_escaped_char();
if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
@@ -538,9 +531,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function handle_dot() { function handle_dot() {
next(); next();
return is_digit(peek().charCodeAt(0)) return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", ".");
? read_num(".")
: token("punc", ".");
} }
function read_word() { function read_word() {
@@ -592,12 +583,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
switch (code) { switch (code) {
case 34: case 39: return read_string(ch); case 34: case 39: return read_string(ch);
case 46: return handle_dot(); case 46: return handle_dot();
case 47: { case 47:
var tok = handle_slash(); var tok = handle_slash();
if (tok === next_token) continue; if (tok === next_token) continue;
return tok; return tok;
} }
}
if (is_digit(code)) return read_num(); if (is_digit(code)) return read_num();
if (PUNC_CHARS[ch]) return token("punc", next()); if (PUNC_CHARS[ch]) return token("punc", next());
if (OPERATOR_CHARS[ch]) return read_operator(); if (OPERATOR_CHARS[ch]) return read_operator();
@@ -614,12 +604,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
next_token.add_directive = function(directive) { next_token.add_directive = function(directive) {
S.directive_stack[S.directive_stack.length - 1].push(directive); S.directive_stack[S.directive_stack.length - 1].push(directive);
if (S.directives[directive]) S.directives[directive]++;
if (S.directives[directive] === undefined) { else S.directives[directive] = 1;
S.directives[directive] = 1;
} else {
S.directives[directive]++;
}
} }
next_token.push_directives_stack = function() { next_token.push_directives_stack = function() {
@@ -627,13 +613,10 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
next_token.pop_directives_stack = function() { next_token.pop_directives_stack = function() {
var directives = S.directive_stack[S.directive_stack.length - 1]; var directives = S.directive_stack.pop();
for (var i = directives.length; --i >= 0;) {
for (var i = 0; i < directives.length; i++) {
S.directives[directives[i]]--; S.directives[directives[i]]--;
} }
S.directive_stack.pop();
} }
next_token.has_directive = function(directive) { next_token.has_directive = function(directive) {
@@ -645,27 +628,17 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
/* -----[ Parser (constants) ]----- */ /* -----[ Parser (constants) ]----- */
var UNARY_PREFIX = makePredicate([ var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
"typeof",
"void",
"delete",
"--",
"++",
"!",
"~",
"-",
"+"
]);
var UNARY_POSTFIX = makePredicate([ "--", "++" ]); var UNARY_POSTFIX = makePredicate("-- ++");
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); var ASSIGNMENT = makePredicate("= += -= /= *= %= >>= <<= >>>= |= ^= &=");
var PRECEDENCE = function(a, ret) { var PRECEDENCE = function(a, ret) {
for (var i = 0; i < a.length; ++i) { for (var i = 0; i < a.length;) {
var b = a[i]; var b = a[i++];
for (var j = 0; j < b.length; ++j) { for (var j = 0; j < b.length; j++) {
ret[b[j]] = i + 1; ret[b[j]] = i;
} }
} }
return ret; return ret;
@@ -682,7 +655,7 @@ var PRECEDENCE = function(a, ret) {
["*", "/", "%"] ["*", "/", "%"]
], {}); ], {});
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]); var ATOMIC_START_TOKEN = makePredicate("atom num string regexp name");
/* -----[ Parser ]----- */ /* -----[ Parser ]----- */
@@ -698,10 +671,9 @@ function parse($TEXT, options) {
}, true); }, true);
var S = { var S = {
input : (typeof $TEXT == "string" input : typeof $TEXT == "string"
? tokenizer($TEXT, options.filename, ? tokenizer($TEXT, options.filename, options.html5_comments, options.shebang)
options.html5_comments, options.shebang) : $TEXT,
: $TEXT),
token : null, token : null,
prev : null, prev : null,
peeked : null, peeked : null,
@@ -757,15 +729,12 @@ function parse($TEXT, options) {
} }
function unexpected(token) { function unexpected(token) {
if (token == null) if (token == null) token = S.token;
token = S.token;
token_error(token, "Unexpected token: " + token_to_string(token.type, token.value)); token_error(token, "Unexpected token: " + token_to_string(token.type, token.value));
} }
function expect_token(type, val) { function expect_token(type, val) {
if (is(type, val)) { if (is(type, val)) return next();
return next();
}
token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val)); token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val));
} }

View File

@@ -55,6 +55,7 @@ function SymbolDef(scope, orig, init) {
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
this.lambda = orig instanceof AST_SymbolLambda;
} }
SymbolDef.next_id = 1; SymbolDef.next_id = 1;
@@ -191,13 +192,21 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 3: fix up any scoping issue with IE8 // pass 3: fix up any scoping issue with IE8
if (options.ie8) self.walk(new TreeWalker(function(node) { if (options.ie8) self.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
redefine(node, node.thedef.defun); var scope = node.thedef.defun;
if (scope.name instanceof AST_SymbolLambda && scope.name.name == node.name) {
scope = scope.parent_scope.resolve();
}
redefine(node, scope);
return true; return true;
} }
if (node instanceof AST_SymbolLambda) { if (node instanceof AST_SymbolLambda) {
var def = node.thedef; var def = node.thedef;
redefine(node, node.scope.parent_scope); redefine(node, node.scope.parent_scope.resolve());
if (typeof node.thedef.init !== "undefined") {
node.thedef.init = false;
} else if (def.init) {
node.thedef.init = def.init; node.thedef.init = def.init;
}
return true; return true;
} }
})); }));
@@ -210,6 +219,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = new_def; node.thedef = new_def;
node.reference(options); node.reference(options);
}); });
if (old_def.lambda) new_def.lambda = true;
if (new_def.undeclared) self.variables.set(name, new_def);
} }
}); });
@@ -281,9 +292,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name); var def = this.variables.get(symbol.name);
if (def) { if (def) {
def.orig.push(symbol); def.orig.push(symbol);
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) { if (def.init instanceof AST_Function) def.init = init;
def.init = init;
}
} else { } else {
def = new SymbolDef(this, symbol, init); def = new SymbolDef(this, symbol, init);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
@@ -413,7 +422,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
if (options.cache && node instanceof AST_Toplevel) { if (options.cache && node instanceof AST_Toplevel) {
node.globals.each(mangle); node.globals.each(mangle);
} }
node.variables.each(mangle); node.variables.each(function(def) {
if (!defer_redef(def)) mangle(def);
});
return true; return true;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
@@ -426,22 +437,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
} }
if (!options.ie8 && node instanceof AST_Catch) { if (!options.ie8 && node instanceof AST_Catch) {
var def = node.argname.definition(); var def = node.argname.definition();
var redef = def.redefined(); var redef = defer_redef(def, node.argname);
if (redef) {
redefined.push(def);
reference(node.argname);
def.references.forEach(reference);
}
descend(); descend();
if (!redef) mangle(def); if (!redef) mangle(def);
return true; return true;
} }
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
}); });
this.walk(tw); this.walk(tw);
redefined.forEach(mangle); redefined.forEach(mangle);
@@ -450,6 +450,21 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
if (options.reserved.has[def.name]) return; if (options.reserved.has[def.name]) return;
def.mangle(options); def.mangle(options);
} }
function defer_redef(def, node) {
var redef = def.redefined();
if (!redef) return false;
redefined.push(def);
def.references.forEach(reference);
if (node) reference(node);
return true;
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
}
}); });
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
@@ -499,13 +514,14 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
if (def.global && options.cache) return; if (def.global && options.cache) return;
if (def.unmangleable(options)) return; if (def.unmangleable(options)) return;
if (options.reserved.has[def.name]) return; if (options.reserved.has[def.name]) return;
var d = def.redefined(); var redef = def.redefined();
def.name = d ? d.name : next_name(); var name = redef ? redef.rename || redef.name : next_name();
def.rename = name;
def.orig.forEach(function(sym) { def.orig.forEach(function(sym) {
sym.name = def.name; sym.name = name;
}); });
def.references.forEach(function(sym) { def.references.forEach(function(sym) {
sym.name = def.name; sym.name = name;
}); });
} }
}); });

View File

@@ -127,8 +127,7 @@ var MAP = (function() {
} else { } else {
top.push(val); top.push(val);
} }
} } else if (val !== skip) {
else if (val !== skip) {
if (val instanceof Splice) { if (val instanceof Splice) {
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
} else { } else {
@@ -145,8 +144,7 @@ var MAP = (function() {
} else { } else {
for (i = 0; i < a.length; ++i) if (doit()) break; for (i = 0; i < a.length; ++i) if (doit()) break;
} }
} } else {
else {
for (i in a) if (HOP(a, i)) if (doit()) break; for (i in a) if (HOP(a, i)) if (doit()) break;
} }
return top.concat(ret); return top.concat(ret);

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"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": "3.5.14", "version": "3.6.4",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -23,12 +23,12 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.20.0", "commander": "~2.20.3",
"source-map": "~0.6.1" "source-map": "~0.6.1"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~6.1.1", "acorn": "~7.1.0",
"semver": "~6.0.0" "semver": "~6.3.0"
}, },
"scripts": { "scripts": {
"test": "node test/compress.js && node test/mocha.js" "test": "node test/compress.js && node test/mocha.js"

View File

@@ -13,15 +13,15 @@ if (!args.length) {
} }
args.push("--timings"); args.push("--timings");
var urls = [ var urls = [
"https://code.jquery.com/jquery-3.2.1.js", "https://code.jquery.com/jquery-3.4.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js", "https://code.angularjs.org/1.7.8/angular.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js", "https://unpkg.com/mathjs@6.2.3/dist/math.js",
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://unpkg.com/react@15.3.2/dist/react.js", "https://unpkg.com/react@15.3.2/dist/react.js",
"http://builds.emberjs.com/tags/v2.11.0/ember.prod.js", "https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.js",
"https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js", "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js",
"https://raw.githubusercontent.com/kangax/html-minifier/v3.5.7/dist/htmlminifier.js", "https://cdnjs.cloudflare.com/ajax/libs/ember.js/2.12.2/ember.prod.js",
"https://raw.githubusercontent.com/kangax/html-minifier/v4.0.0/dist/htmlminifier.js",
]; ];
var results = {}; var results = {};
var remaining = 2 * urls.length; var remaining = 2 * urls.length;
@@ -94,3 +94,6 @@ urls.forEach(function(url) {
}); });
}); });
}); });
setInterval(function() {
process.stderr.write("\0");
}, 5 * 60 * 1000).unref();

View File

@@ -9,7 +9,7 @@ var U = require("./node");
var file = process.argv[2]; var file = process.argv[2];
var dir = path.resolve(path.dirname(module.filename), "compress"); var dir = path.resolve(path.dirname(module.filename), "compress");
if (file) { if (file) {
var minify_options = require("./ufuzz.json").map(JSON.stringify); var minify_options = require("./ufuzz/options.json").map(JSON.stringify);
log("--- {file}", { file: file }); log("--- {file}", { file: file });
var tests = parse_test(path.resolve(dir, file)); var tests = parse_test(path.resolve(dir, file));
process.exit(Object.keys(tests).filter(function(name) { process.exit(Object.keys(tests).filter(function(name) {

View File

@@ -311,3 +311,65 @@ issue_3375: {
} }
expect_stdout: "string" expect_stdout: "string"
} }
issue_3427: {
options = {
assignments: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a || (a = {});
})();
}
expect: {}
}
issue_3429_1: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a = "PASS";
(function(b) {
b && (b = a = "FAIL");
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(b) {
b = b && (a = "FAIL");
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_3429_2: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a;
(function(b) {
b || (b = a = "FAIL");
})(42);
console.log(a);
}
expect: {
var a;
(function(b) {
b = b || (a = "FAIL");
})(42);
console.log(a);
}
expect_stdout: "undefined"
}

88
test/compress/booleans.js Normal file
View File

@@ -0,0 +1,88 @@
iife_boolean_context: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(function() {
return Object(1) || false;
}() ? "PASS" : "FAIL");
console.log(function() {
return [].length || true;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function() {
return Object(1);
}() ? "PASS" : "FAIL");
console.log(function() {
return [].length, 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: [
"PASS",
"PASS",
]
expect_warnings: [
"WARN: Dropping side-effect-free || [test/compress/booleans.js:2,19]",
"WARN: Boolean || always true [test/compress/booleans.js:5,19]",
]
}
issue_3465_1: {
options = {
booleans: true,
}
input: {
console.log(function(a) {
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function(a) {
return 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_3465_2: {
options = {
booleans: true,
}
input: {
console.log(function f(a) {
if (!a) console.log(f(42));
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function f(a) {
if (!a) console.log(f(42));
return typeof a;
}() ? "PASS" : "FAIL");
}
expect_stdout: [
"number",
"PASS",
]
}
issue_3465_3: {
options = {
booleans: true,
passes: 2,
unused: true,
}
input: {
console.log(function f(a) {
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function(a) {
return 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}

View File

@@ -3511,7 +3511,7 @@ issue_2437_2: {
conditionals: true, conditionals: true,
inline: true, inline: true,
join_vars: true, join_vars: true,
passes: 2, passes: 3,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
sequences: true, sequences: true,
@@ -4388,7 +4388,7 @@ replace_all_var: {
} }
replace_all_var_scope: { replace_all_var_scope: {
rename = true; rename = true
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true, unused: true,
@@ -6197,3 +6197,64 @@ Infinity_assignment: {
} }
expect_stdout: true expect_stdout: true
} }
issue_3439_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect_stdout: "function"
}
issue_3439_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
var a = 42;
function a() {}
return a;
}());
}
expect: {
console.log(typeof function() {
return 42;
}());
}
expect_stdout: "number"
}
cond_sequence_return: {
options = {
collapse_vars: true,
}
input: {
console.log(function(n) {
var c = 0;
for (var k in [0, 1])
if (c++, k == n) return c;
}(1));
}
expect: {
console.log(function(n) {
var c = 0;
for (var k in [0, 1])
if (c++, k == n) return c;
}(1));
}
expect_stdout: "2"
}

View File

@@ -2028,3 +2028,161 @@ issue_3375: {
} }
expect_stdout: "0 0" expect_stdout: "0 0"
} }
issue_3427_1: {
options = {
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a = a || {};
})();
}
expect: {}
}
issue_3427_2: {
options = {
unused: true,
}
input: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect_stdout: "PASS"
}
issue_3495: {
options = {
dead_code: true,
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
console.log(function f() {
f = 0;
var a = f.p;
}());
}
expect: {
console.log(void 0);
}
expect_stdout: "undefined"
}
issue_3497: {
options = {
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
var a;
console.log(function(b) {
(b += a).p = 0;
}());
}
expect: {
var a;
console.log(function(b) {
(b += a).p = 0;
}());
}
expect_stdout: "undefined"
}
issue_3515_1: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
var c = 0;
(function() {
this[c++] = 0;
var expr20 = !0;
for (var key20 in expr20);
})();
console.log(c);
}
expect: {
var c = 0;
(function() {
for (var key20 in !(this[c++] = 0));
})();
console.log(c);
}
expect_stdout: "1"
}
issue_3515_2: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL";
function f() {
typeof b === "number";
delete a;
}
var b = f(a = "PASS");
console.log(a);
}
expect: {
var a = "FAIL";
function f() {
delete a;
}
f(a = "PASS");
console.log(a);
}
expect_stdout: "PASS"
}
issue_3515_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var c = "FAIL";
(function() {
function f() {
c = "PASS";
}
var a = f();
var a = function g(b) {
b && (b.p = this);
}(a);
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
function f() {
c = "PASS";
}
(function(b) {
b && (b.p = this);
})(f());
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -1150,7 +1150,7 @@ issue_2620_3: {
} }
issue_2620_4: { issue_2620_4: {
rename = true, rename = true
options = { options = {
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
@@ -3066,7 +3066,7 @@ class_iife: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3400: { issue_3400_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
inline: true, inline: true,
@@ -3096,16 +3096,70 @@ issue_3400: {
}); });
} }
expect: { expect: {
void console.log(function g() { void console.log(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() { function e() {
return [42].map(function(v) { return [ 42 ].map(function(v) {
return o = { return h(v);
p: v
}, console.log(o[g]) , o;
var o;
}); });
} }
return e(); return e();
}
return g;
}()()[0].p);
}
expect_stdout: [
"undefined",
"42",
]
}
issue_3400_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(f) {
console.log(f()()[0].p);
})(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
}
return g;
});
}
expect: {
void console.log(function g() {
return [ 42 ].map(function(v) {
return function(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}(v);
});
}()[0].p); }()[0].p);
} }
expect_stdout: [ expect_stdout: [
@@ -3148,3 +3202,171 @@ issue_3402: {
"function", "function",
] ]
} }
issue_3439: {
options = {
inline: true,
}
input: {
console.log(typeof function() {
return function(a) {
function a() {}
return a;
}(42);
}());
}
expect: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect_stdout: "function"
}
issue_3444: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(h) {
return f;
function f() {
g();
}
function g() {
h("PASS");
}
})(console.log)();
}
expect: {
(function(h) {
return function() {
void h("PASS");
};
})(console.log)();
}
expect_stdout: "PASS"
}
issue_3506_1: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = "FAIL";
(function(b) {
(function(b) {
b && (a = "PASS");
})(b);
})(a);
console.log(a);
}
expect: {
var a = "FAIL";
!function(b) {
b && (a = "PASS");
}(a);
console.log(a);
}
expect_stdout: "PASS"
}
issue_3506_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = "FAIL";
(function(b) {
(function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
})(b);
})(a);
console.log(a);
}
expect: {
var a = "FAIL";
!function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
}(a);
console.log(a);
}
expect_stdout: "PASS"
}
issue_3506_3: {
options = {
collapse_vars: true,
dead_code: true,
evaluate: true,
inline: true,
loops: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = "FAIL";
(function(b) {
(function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
})(b);
})(a);
console.log(a);
}
expect: {
var a = "FAIL";
!function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
}(a);
console.log(a);
}
expect_stdout: "PASS"
}
issue_3512: {
options = {
collapse_vars: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
unused: true,
}
input: {
var a = "PASS";
(function(b) {
(function() {
b <<= this || 1;
b.a = "FAIL";
})();
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(b) {
(function() {
(b <<= this || 1).a = "FAIL";
})();
})(),
console.log(a);
}
expect_stdout: "PASS"
}

View File

@@ -886,3 +886,31 @@ issue_3411: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3440: {
options = {
hoist_props: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
function f() {
console.log(o.p);
}
var o = {
p: "PASS",
};
return f;
})()();
}
expect: {
(function() {
var o_p = "PASS";
return function() {
console.log(o_p);
};
})()();
}
expect_stdout: "PASS"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1117,3 +1117,41 @@ issue_3420_3: {
} }
expect_stdout: "4" expect_stdout: "4"
} }
issue_3423_1: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
function f(g) {
console.log(g.length);
}
f(function(a) {});
}
expect: {
function f(g) {
console.log(g.length);
}
f(function(a) {});
}
expect_stdout: "1"
}
issue_3423_2: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
new function(a) {
console.log(this.constructor.length);
}();
}
expect: {
new function(a) {
console.log(this.constructor.length);
}();
}
expect_stdout: "1"
}

View File

@@ -1187,3 +1187,25 @@ drop_arguments: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
passes: 2,
pure_getters: "strict",
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
(function(b) {
b.p = 42;
})(a || (a = {}));
}
expect: {}
expect_stdout: true
}

View File

@@ -4947,7 +4947,7 @@ defun_single_use_loop: {
unused: true, unused: true,
} }
input: { input: {
for (var x, i = 2; --i >= 0; ) { for (var x, i = 2; --i >= 0;) {
var y = x; var y = x;
x = f; x = f;
console.log(x === y); console.log(x === y);
@@ -4955,7 +4955,7 @@ defun_single_use_loop: {
function f() {}; function f() {};
} }
expect: { expect: {
for (var x, i = 2; --i >= 0; ) { for (var x, i = 2; --i >= 0;) {
var y = x; var y = x;
x = f; x = f;
console.log(x === y); console.log(x === y);
@@ -6609,10 +6609,10 @@ issues_3267_1: {
} }
expect: { expect: {
!function(i) { !function(i) {
if (i) if (Object())
return console.log("PASS"); return console.log("PASS");
throw "FAIL"; throw "FAIL";
}(Object()); }();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
@@ -6752,3 +6752,31 @@ issue_3377: {
} }
expect_stdout: "42" expect_stdout: "42"
} }
issue_3509: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function a() {
console.log("PASS");
}
try {
} catch (a) {
var a;
}
a();
}
expect: {
try {
} catch (a) {
var a;
}
(function() {
console.log("PASS");
})();
}
expect_stdout: "PASS"
}

View File

@@ -35,3 +35,140 @@ regexp_2: {
} }
expect_stdout: '["PASS","pass"]' expect_stdout: '["PASS","pass"]'
} }
issue_3434_1: {
options = {
evaluate: true,
unsafe: true,
}
beautify = {
beautify: true,
}
input: {
var o = {
"\n": RegExp("\n"),
"\r": RegExp("\r"),
"\t": RegExp("\t"),
"\b": RegExp("\b"),
"\f": RegExp("\f"),
"\0": RegExp("\0"),
"\x0B": RegExp("\x0B"),
"\u2028": RegExp("\u2028"),
"\u2029": RegExp("\u2029"),
};
for (var c in o)
console.log(o[c].test("\\"), o[c].test(c));
}
expect_exact: [
"var o = {",
' "\\n": /\\n/,',
' "\\r": /\\r/,',
' "\\t": /\t/,',
' "\\b": /\b/,',
' "\\f": /\f/,',
' "\\0": /\0/,',
' "\\v": /\v/,',
' "\\u2028": /\\u2028/,',
' "\\u2029": /\\u2029/',
"};",
"",
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
]
expect_stdout: [
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
]
}
issue_3434_2: {
options = {
evaluate: true,
unsafe: true,
}
beautify = {
beautify: true,
}
input: {
var o = {
"\n": RegExp("\\\n"),
"\r": RegExp("\\\r"),
"\t": RegExp("\\\t"),
"\b": RegExp("\\\b"),
"\f": RegExp("\\\f"),
"\0": RegExp("\\\0"),
"\x0B": RegExp("\\\x0B"),
"\u2028": RegExp("\\\u2028"),
"\u2029": RegExp("\\\u2029"),
};
for (var c in o)
console.log(o[c].test("\\"), o[c].test(c));
}
expect_exact: [
"var o = {",
' "\\n": /\\n/,',
' "\\r": /\\r/,',
' "\\t": /\t/,',
' "\\b": /\b/,',
' "\\f": /\f/,',
' "\\0": /\0/,',
' "\\v": /\v/,',
' "\\u2028": /\\u2028/,',
' "\\u2029": /\\u2029/',
"};",
"",
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
]
expect_stdout: [
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
]
}
issue_3434_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
RegExp("\n");
RegExp("\r");
RegExp("\\n");
RegExp("\\\n");
RegExp("\\\\n");
RegExp("\\\\\n");
RegExp("\\\\\\n");
RegExp("\\\\\\\n");
RegExp("\u2028");
RegExp("\u2029");
RegExp("\n\r\u2028\u2029");
RegExp("\\\nfo\n[\n]o\\bbb");
}
expect: {
/\n/;
/\r/;
/\n/;
/\n/;
/\\n/;
/\\\n/;
/\\\n/;
/\\\n/;
/\u2028/;
/\u2029/;
/\n\r\u2028\u2029/;
/\nfo\n[\n]o\bbb/;
}
}

View File

@@ -579,7 +579,7 @@ function_do_catch_ie8: {
console.log(b, c); console.log(b, c);
} }
expect: { expect: {
var t = 1, u = 1, y = 0; var u = 1, y = 1, a = 0;
function c(c) { function c(c) {
var d; var d;
do { do {
@@ -587,7 +587,7 @@ function_do_catch_ie8: {
try { try {
var e = void 0; var e = void 0;
} catch (i) { } catch (i) {
--t && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy"); --u && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy");
0; 0;
0; 0;
0; 0;
@@ -596,18 +596,146 @@ function_do_catch_ie8: {
d[1]; d[1];
} catch (l) { } catch (l) {
var g; var g;
switch(function x() { switch (function n() {
y++; a++;
}()) { }()) {
case e + --g: case e + --g:
} }
} }
} catch (n) {} } catch (t) {}
} while (--d); } while (--d);
u--; y--;
} }
c(); c();
console.log(u, y); console.log(y, a);
} }
expect_stdout: "0 1" expect_stdout: "0 1"
} }
issue_3480: {
rename = true,
mangle = {
ie8: false,
toplevel: false,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var d, a, b, c = "FAIL";
(function n() {
(function() {
try {
c = "PASS";
} catch (c) {}
})();
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3480_ie8: {
rename = true,
mangle = {
ie8: true,
toplevel: false,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {}
})();
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3480_toplevel: {
rename = true,
mangle = {
ie8: false,
toplevel: true,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var c, n, o, t = "FAIL";
(function c() {
(function() {
try {
t = "PASS";
} catch (c) {}
})();
})();
console.log(t);
}
expect_stdout: "PASS"
}
issue_3480_ie8_toplevel: {
rename = true,
mangle = {
ie8: true,
toplevel: true,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var c, n, o, t = "FAIL";
(function o() {
(function() {
try {
t = "PASS";
} catch (o) {}
})();
})();
console.log(t);
}
expect_stdout: "PASS"
}

View File

@@ -1006,3 +1006,66 @@ angularjs_chain: {
} }
} }
} }
issue_3490_1: {
options = {
conditionals: true,
dead_code: true,
inline: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 42, c = "FAIL";
if ({
3: function() {
var a;
return (a && a.p) < this;
}(),
}) c = "PASS";
if (b) while ("" == typeof d);
console.log(c, b);
}
expect: {
var b = 42, c = "FAIL";
if (function() {
var a;
a && a.p;
}(), c = "PASS", b) while ("" == typeof d);
console.log(c, b);
}
expect_stdout: "PASS 42"
}
issue_3490_2: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 42, c = "FAIL";
if ({
3: function() {
var a;
return (a && a.p) < this;
}(),
}) c = "PASS";
if (b) for (; "" == typeof d;);
console.log(c, b);
}
expect: {
var b = 42, c = "FAIL";
for (function() {
var a;
}(), c = "PASS", b; "" == typeof d;);
console.log(c, b);
}
expect_stdout: "PASS 42"
}

View File

@@ -8,6 +8,7 @@ exports["parse"] = parse;
exports["push_uniq"] = push_uniq; exports["push_uniq"] = push_uniq;
exports["reserve_quoted_keys"] = reserve_quoted_keys; exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["string_template"] = string_template; exports["string_template"] = string_template;
exports["to_ascii"] = to_ascii;
exports["tokenizer"] = tokenizer; exports["tokenizer"] = tokenizer;
exports["TreeTransformer"] = TreeTransformer; exports["TreeTransformer"] = TreeTransformer;
exports["TreeWalker"] = TreeWalker; exports["TreeWalker"] = TreeWalker;

View File

@@ -0,0 +1,8 @@
// Generated by CoffeeScript 2.4.1
(function() {
console.log('hello');
}).call(this);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4uY29mZmVlIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTtFQUFBLE9BQU8sQ0FBQyxHQUFSLENBQVksT0FBWjtBQUFBIiwic291cmNlc0NvbnRlbnQiOlsiY29uc29sZS5sb2cgJ2hlbGxvJ1xuIl19
//# sourceURL=/Users/mohamed/Downloads/main.coffee

View File

@@ -48,7 +48,8 @@ describe("comments", function() {
} }
}); });
it("Should handle comment within return correctly", function() { describe("comment within return", function() {
it("Should handle leading return", function() {
var result = UglifyJS.minify([ var result = UglifyJS.minify([
"function unequal(x, y) {", "function unequal(x, y) {",
" return (", " return (",
@@ -75,7 +76,36 @@ describe("comments", function() {
].join("\n")); ].join("\n"));
}); });
it("Should handle comment folded into return correctly", function() { it("Should handle trailing return", function() {
var result = UglifyJS.minify([
"function unequal(x) {",
" var y;",
" return (",
" // Either one",
" x < y",
" ||",
" y < x",
" );",
"}",
].join("\n"), {
compress: false,
mangle: false,
output: {
beautify: true,
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"function unequal(x) {",
" var y;",
" // Either one",
" return x < y || y < x;",
"}",
].join("\n"));
});
it("Should handle comment folded into return", function() {
var result = UglifyJS.minify([ var result = UglifyJS.minify([
"function f() {", "function f() {",
" /* boo */ x();", " /* boo */ x();",
@@ -96,6 +126,7 @@ describe("comments", function() {
"}", "}",
].join("\n")); ].join("\n"));
}); });
});
it("Should not drop comments after first OutputStream", function() { it("Should not drop comments after first OutputStream", function() {
var code = "/* boo */\nx();"; var code = "/* boo */\nx();";

View File

@@ -1,7 +1,7 @@
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
var SourceMapConsumer = require("source-map").SourceMapConsumer; var SourceMapConsumer = require("source-map").SourceMapConsumer;
var UglifyJS = require("../.."); var UglifyJS = require("../node");
function read(path) { function read(path) {
return readFileSync(path, "utf8"); return readFileSync(path, "utf8");
@@ -181,6 +181,18 @@ describe("sourcemaps", function() {
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8")); assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8"));
}); });
it("Should work in presence of unrecognised annotations", function() {
var result = UglifyJS.minify(read("./test/input/issue-3441/input.js"), {
compress: false,
mangle: false,
sourceMap: {
content: "inline",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, '(function(){console.log("hello")}).call(this);');
assert.strictEqual(result.map, '{"version":3,"sources":["main.coffee"],"names":["console","log"],"mappings":"CAAA,WAAAA,QAAQC,IAAI"}');
});
}); });
describe("sourceMapInline", function() { describe("sourceMapInline", function() {
@@ -232,7 +244,7 @@ describe("sourcemaps", function() {
assert.strictEqual(map.sourcesContent.length, 1); assert.strictEqual(map.sourcesContent.length, 1);
assert.strictEqual(map.sourcesContent[0], code); assert.strictEqual(map.sourcesContent[0], code);
var encoded = result.code.slice(result.code.lastIndexOf(",") + 1); var encoded = result.code.slice(result.code.lastIndexOf(",") + 1);
map = JSON.parse(new Buffer(encoded, "base64").toString()); map = JSON.parse(UglifyJS.to_ascii(encoded));
assert.strictEqual(map.sourcesContent.length, 1); assert.strictEqual(map.sourcesContent.length, 1);
assert.strictEqual(map.sourcesContent[0], code); assert.strictEqual(map.sourcesContent[0], code);
result = UglifyJS.minify(result.code, { result = UglifyJS.minify(result.code, {

View File

@@ -1,81 +0,0 @@
"use strict";
var child_process = require("child_process");
var https = require("https");
var url = require("url");
var period = 45 * 60 * 1000;
var wait = 2 * 60 * 1000;
var ping = 5 * 60 * 1000;
if (process.argv[2] == "run") {
var endTime = Date.now() + period;
for (var i = 0; i < 2; i++) spawn(endTime);
} else if (process.argv.length > 2) {
var token = process.argv[2];
var branch = process.argv[3] || "v" + require("../package.json").version;
var repository = encodeURIComponent(process.argv[4] || "mishoo/UglifyJS2");
var concurrency = process.argv[5] || 1;
var platform = process.argv[6] || "node/latest";
(function request() {
setTimeout(request, (period + wait) / concurrency);
var options = url.parse("https://api.travis-ci.org/repo/" + repository + "/requests");
options.method = "POST";
options.headers = {
"Content-Type": "application/json",
"Travis-API-Version": 3,
"Authorization": "token " + token
};
https.request(options, function(res) {
console.log("HTTP", res.statusCode);
console.log(JSON.stringify(res.headers, null, 2));
console.log();
res.setEncoding("utf8");
res.on("data", console.log);
}).on("error", console.error).end(JSON.stringify({
request: {
message: "ufuzz testing",
branch: branch,
config: {
cache: false,
env: "NODEJS_VER=" + platform,
script: "node test/travis-ufuzz run"
}
}
}));
})();
} else {
console.log("Usage: test/travis-ufuzz.js <token> [branch] [repository] [concurrency] [platform]");
}
function spawn(endTime) {
var child = child_process.spawn("node", [
"--max-old-space-size=2048",
"test/ufuzz"
], {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
var line = "";
child.stdout.on("data", function(data) {
line += data;
});
child.stderr.on("data", function() {
process.exitCode = 1;
}).pipe(process.stdout);
var keepAlive = setInterval(function() {
var end = line.lastIndexOf("\r");
console.log(line.slice(line.lastIndexOf("\r", end - 1) + 1, end));
line = line.slice(end + 1);
}, ping);
var timer = setTimeout(function() {
clearInterval(keepAlive);
child.removeListener("exit", respawn);
child.kill();
}, endTime - Date.now());
function respawn() {
console.log(line);
clearInterval(keepAlive);
clearTimeout(timer);
spawn(endTime);
}
}

View File

@@ -1,4 +1,3 @@
// ufuzz.js
// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee // derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
"use strict"; "use strict";
@@ -6,11 +5,11 @@
// bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m // bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m
// cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node // cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node
require("../tools/exit"); require("../../tools/exit");
var UglifyJS = require(".."); var UglifyJS = require("../..");
var randomBytes = require("crypto").randomBytes; var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox"); var sandbox = require("../sandbox");
var MAX_GENERATED_TOPLEVELS_PER_RUN = 1; var MAX_GENERATED_TOPLEVELS_PER_RUN = 1;
var MAX_GENERATION_RECURSION_DEPTH = 12; var MAX_GENERATION_RECURSION_DEPTH = 12;
@@ -959,14 +958,19 @@ if (require.main !== module) {
return; return;
} }
function writeln(stream, msg) {
if (typeof msg != "undefined") {
stream.write(typeof msg == "string" ? msg : msg.stack || "" + msg);
}
stream.write("\n");
}
function println(msg) { function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg); writeln(process.stdout, msg);
process.stdout.write("\n");
} }
function errorln(msg) { function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg); writeln(process.stderr, msg);
process.stderr.write("\n");
} }
function try_beautify(code, toplevel, result, printfn) { function try_beautify(code, toplevel, result, printfn) {
@@ -980,7 +984,7 @@ function try_beautify(code, toplevel, result, printfn) {
}); });
if (beautified.error) { if (beautified.error) {
printfn("// !!! beautify failed !!!"); printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack); printfn(beautified.error);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code, toplevel), result)) { } else if (sandbox.same_stdout(sandbox.run_code(beautified.code, toplevel), result)) {
printfn("// (beautified)"); printfn("// (beautified)");
printfn(beautified.code); printfn(beautified.code);
@@ -1007,7 +1011,7 @@ function log_suspects(minify_options, component) {
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (result.error) {
errorln("Error testing options." + component + "." + name); errorln("Error testing options." + component + "." + name);
errorln(result.error.stack); errorln(result.error);
} else { } else {
var r = sandbox.run_code(result.code, m.toplevel); var r = sandbox.run_code(result.code, m.toplevel);
return sandbox.same_stdout(original_result, r); return sandbox.same_stdout(original_result, r);
@@ -1029,7 +1033,7 @@ function log_rename(options) {
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (result.error) {
errorln("Error testing options.rename"); errorln("Error testing options.rename");
errorln(result.error.stack); errorln(result.error);
} else { } else {
var r = sandbox.run_code(result.code, m.toplevel); var r = sandbox.run_code(result.code, m.toplevel);
if (sandbox.same_stdout(original_result, r)) { if (sandbox.same_stdout(original_result, r)) {
@@ -1056,17 +1060,17 @@ function log(options) {
errorln(); errorln();
errorln(); errorln();
errorln("original result:"); errorln("original result:");
errorln(errored ? original_result.stack : original_result); errorln(original_result);
errorln("uglified result:"); errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack); errorln(uglify_result);
} else { } else {
errorln("// !!! uglify failed !!!"); errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack); errorln(uglify_code);
if (errored) { if (errored) {
errorln(); errorln();
errorln(); errorln();
errorln("original stacktrace:"); errorln("original stacktrace:");
errorln(original_result.stack); errorln(original_result);
} }
} }
errorln("minify(options):"); errorln("minify(options):");
@@ -1083,7 +1087,7 @@ var fallback_options = [ JSON.stringify({
compress: false, compress: false,
mangle: false mangle: false
}) ]; }) ];
var minify_options = require("./ufuzz.json").map(JSON.stringify); var minify_options = require("./options.json").map(JSON.stringify);
var original_code, original_result, errored; var original_code, original_result, errored;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
@@ -1115,7 +1119,7 @@ for (var round = 1; round <= num_iterations; round++) {
println(); println();
println(); println();
println("original result:"); println("original result:");
println(original_result.stack); println(original_result);
println(); println();
} }
if (!ok && isFinite(num_iterations)) { if (!ok && isFinite(num_iterations)) {

39
test/ufuzz/job.js Normal file
View File

@@ -0,0 +1,39 @@
var child_process = require("child_process");
var ping = 5 * 60 * 1000;
var period = +process.argv[2];
var endTime = Date.now() + period;
for (var i = 0; i < 2; i++) spawn(endTime);
function spawn(endTime) {
var child = child_process.spawn("node", [
"--max-old-space-size=2048",
"test/ufuzz"
], {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
var line = "";
child.stdout.on("data", function(data) {
line += data;
});
child.stderr.once("data", function() {
process.exitCode = 1;
}).pipe(process.stdout);
var keepAlive = setInterval(function() {
var end = line.lastIndexOf("\r");
console.log(line.slice(line.lastIndexOf("\r", end - 1) + 1, end));
line = line.slice(end + 1);
}, ping);
var timer = setTimeout(function() {
clearInterval(keepAlive);
child.removeListener("exit", respawn);
child.kill();
}, endTime - Date.now());
function respawn() {
console.log(line);
clearInterval(keepAlive);
clearTimeout(timer);
spawn(endTime);
}
}

44
test/ufuzz/travis.js Normal file
View File

@@ -0,0 +1,44 @@
"use strict";
var child_process = require("child_process");
var https = require("https");
var url = require("url");
var period = 45 * 60 * 1000;
var wait = 2 * 60 * 1000;
if (process.argv.length > 2) {
var token = process.argv[2];
var branch = process.argv[3] || "v" + require("../../package.json").version;
var repository = encodeURIComponent(process.argv[4] || "mishoo/UglifyJS2");
var concurrency = process.argv[5] || 1;
var platform = process.argv[6] || "latest";
(function request() {
setTimeout(request, (period + wait) / concurrency);
var options = url.parse("https://api.travis-ci.org/repo/" + repository + "/requests");
options.method = "POST";
options.headers = {
"Content-Type": "application/json",
"Travis-API-Version": 3,
"Authorization": "token " + token
};
https.request(options, function(res) {
console.log("HTTP", res.statusCode);
console.log(JSON.stringify(res.headers, null, 2));
console.log();
res.setEncoding("utf8");
res.on("data", console.log);
}).on("error", console.error).end(JSON.stringify({
request: {
message: "ufuzz testing",
branch: branch,
config: {
cache: false,
env: "NODE=" + platform,
script: "node test/ufuzz/job " + period
}
}
}));
})();
} else {
console.log("Usage: test/ufuzz/travis.js <token> [branch] [repository] [concurrency] [platform]");
}

View File

@@ -2702,8 +2702,10 @@
"attrChange", "attrChange",
"attrName", "attrName",
"attributeChangedCallback", "attributeChangedCallback",
"attributeFilter",
"attributeName", "attributeName",
"attributeNamespace", "attributeNamespace",
"attributeOldValue",
"attributeStyleMap", "attributeStyleMap",
"attributes", "attributes",
"audioTracks", "audioTracks",
@@ -2993,6 +2995,7 @@
"caption", "caption",
"caption-side", "caption-side",
"captionSide", "captionSide",
"capture",
"captureEvents", "captureEvents",
"captureStackTrace", "captureStackTrace",
"captureStream", "captureStream",
@@ -3024,6 +3027,8 @@
"charCode", "charCode",
"charCodeAt", "charCodeAt",
"charIndex", "charIndex",
"characterData",
"characterDataOldValue",
"characterSet", "characterSet",
"charging", "charging",
"chargingTime", "chargingTime",
@@ -3035,6 +3040,7 @@
"checkValidity", "checkValidity",
"checked", "checked",
"childElementCount", "childElementCount",
"childList",
"childNodes", "childNodes",
"children", "children",
"chrome", "chrome",
@@ -4957,6 +4963,7 @@
"oncandidatewindowupdate", "oncandidatewindowupdate",
"oncanplay", "oncanplay",
"oncanplaythrough", "oncanplaythrough",
"once",
"oncellchange", "oncellchange",
"onchange", "onchange",
"onchargingchange", "onchargingchange",
@@ -5343,6 +5350,7 @@
"parseInt", "parseInt",
"part", "part",
"participants", "participants",
"passive",
"password", "password",
"pasteHTML", "pasteHTML",
"path", "path",
@@ -6147,6 +6155,7 @@
"substring", "substring",
"substringData", "substringData",
"subtle", "subtle",
"subtree",
"suffix", "suffix",
"suffixes", "suffixes",
"summary", "summary",

View File

@@ -1,21 +1,19 @@
var fs = require("fs"); var fs = require("fs");
exports.FILES = [ exports.FILES = [
"../lib/utils.js", require.resolve("../lib/utils.js"),
"../lib/ast.js", require.resolve("../lib/ast.js"),
"../lib/parse.js", require.resolve("../lib/parse.js"),
"../lib/transform.js", require.resolve("../lib/transform.js"),
"../lib/scope.js", require.resolve("../lib/scope.js"),
"../lib/output.js", require.resolve("../lib/output.js"),
"../lib/compress.js", require.resolve("../lib/compress.js"),
"../lib/sourcemap.js", require.resolve("../lib/sourcemap.js"),
"../lib/mozilla-ast.js", require.resolve("../lib/mozilla-ast.js"),
"../lib/propmangle.js", require.resolve("../lib/propmangle.js"),
"../lib/minify.js", require.resolve("../lib/minify.js"),
"./exports.js", require.resolve("./exports.js"),
].map(function(file) { ];
return require.resolve(file);
});
new Function("MOZ_SourceMap", "exports", function() { new Function("MOZ_SourceMap", "exports", function() {
var code = exports.FILES.map(function(file) { var code = exports.FILES.map(function(file) {
@@ -48,7 +46,9 @@ function describe_ast() {
if (ctor.SUBCLASSES.length > 0) { if (ctor.SUBCLASSES.length > 0) {
out.space(); out.space();
out.with_block(function() { out.with_block(function() {
ctor.SUBCLASSES.forEach(function(ctor, i) { ctor.SUBCLASSES.sort(function(a, b) {
return a.TYPE < b.TYPE ? -1 : 1;
}).forEach(function(ctor, i) {
out.indent(); out.indent();
doitem(ctor); doitem(ctor);
out.newline(); out.newline();