Compare commits

...

38 Commits

Author SHA1 Message Date
Alex Lam S.L
a3dfeea144 v3.3.11 2018-02-15 19:24:35 +00:00
Alex Lam S.L
d316fb139d fix unsafe evaluate on type-converting operators (#2917)
fixes #2916
2018-02-14 16:48:47 +08:00
Alex Lam S.L
83d8aa8b12 fix collapse_vars within loops (#2915)
fixes #2914
2018-02-14 05:15:52 +08:00
Alex Lam S.L
4f1c12b6fd report options upon reminify input error (#2911) 2018-02-13 07:29:39 +08:00
Alex Lam S.L
d8e0e34354 collapse within unary expressions (#2910) 2018-02-13 07:10:37 +08:00
Alex Lam S.L
0c4f315c02 fix corner case in collapse_vars (#2909)
fixes #2908
2018-02-13 01:41:22 +08:00
Alex Lam S.L
0809699bdc simplify do-while into for (#2907)
fixes #2904
2018-02-12 23:28:28 +08:00
Alex Lam S.L
2088e1c19d fix AST corruption due to collapse_vars & inline (#2899)
fixes #2898
2018-02-09 06:54:37 +08:00
Alex Lam S.L
bf1d47180c fix join_vars on property accessors (#2895)
fixes #2893
2018-02-09 01:52:39 +08:00
Alex Lam S.L
0cfbd79aa1 v3.3.10 2018-02-08 10:16:16 +00:00
Alex Lam S.L
d66d86f20b account for exceptions in AST_Assign.left (#2892)
fixes #2891
2018-02-08 03:31:51 +08:00
Alex Lam S.L
905325d3e2 update dependencies (#2889)
acorn 5.4.1
commander 2.14.1
semver 5.5.0
2018-02-07 18:13:18 +08:00
Alex Lam S.L
dea0cc0662 mention file encoding (#2887) 2018-02-06 16:48:49 +08:00
Alex Lam S.L
d69d8007d6 evaluate to{Low,Upp}erCase() under unsafe (#2886) 2018-02-06 15:57:15 +08:00
Dan
c0b8f2a16d add information on testing and code style (#2885)
fixes #2884
2018-02-06 14:19:28 +08:00
Dan
cb0257dbbf describe a few compiler assumptions (#2883) 2018-02-06 14:19:03 +08:00
Dan
9637f51b68 change undefined == x to null == x (#2882)
fixes #2871
2018-02-05 15:00:23 +08:00
Alex Lam S.L
3026bd8975 improve exceptional flow compression by collapse_vars (#2880) 2018-02-04 04:18:22 +08:00
Alex Lam S.L
78a44d5ab0 maintain order between side-effects and externally observable assignments (#2879)
fixes #2878
2018-02-04 03:58:49 +08:00
Alex Lam S.L
7e13c0db40 handle break & continue in collapse_vars (#2875)
fixes #2873
2018-02-03 07:58:43 +08:00
Alex Lam S.L
e6a2e9e4d0 allow collapse_vars across conditional branches (#2867) 2018-02-03 02:44:40 +08:00
Alex Lam S.L
e773f03927 fix assignment logic in reduce_vars (#2872)
fixes #2869
2018-02-03 01:33:09 +08:00
Alex Lam S.L
b16380d669 fix missing corner case in #2855 (#2868) 2018-02-02 18:08:56 +08:00
Ryan Gunn
334b07a3db Update License Copyright Year to 2018 (#2866) 2018-02-02 16:30:01 +08:00
Alex Lam S.L
3cc1527f00 always test for rename (#2865) 2018-02-01 21:42:55 +08:00
Alex Lam S.L
525a61fb55 better fix for #2858 (#2864) 2018-02-01 20:06:36 +08:00
Alex Lam S.L
c3a002ff97 account for side-effects in comparisons of null & undefined (#2863) 2018-02-01 19:15:17 +08:00
Alex Lam S.L
fad6766a90 simplify comparisons with undefined & null (#2862)
fixes #2857
2018-02-01 16:50:54 +08:00
Alex Lam S.L
aa664dea0a avoid evaluate of compound assignment after dead_code transform (#2861)
fixes #2860
2018-02-01 16:18:29 +08:00
Alex Lam S.L
102f994b9d account for declaration assignment in collapse_vars (#2859)
fixes #2858
2018-02-01 15:09:53 +08:00
Alex Lam S.L
2a4c68be4f relax collapse_vars on AST_Exit (#2855)
First introduced in #1862 to stop assignments to migrate beyond `return` or `throw`. Since then `collapse_vars` has been improved to handle various side-effect-related corner cases.
2018-01-31 23:49:59 +08:00
Alex Lam S.L
541e6011af improve symbol replacement heuristic (#2851) 2018-01-29 17:41:15 +08:00
Alex Lam S.L
6fa3fbeae8 compress chained compound assignments (#2850) 2018-01-29 15:13:25 +08:00
Alex Lam S.L
4eb4cb656c v3.3.9 2018-01-27 12:56:34 +00:00
Alex Lam S.L
193612ac67 fix accounting after conversion to assignment (#2847)
Missing reference to `AST_SymbolRef` created by `unused` causes `collapse_vars` to misbehave.

fixes #2846
2018-01-26 14:21:11 +08:00
Alex Lam S.L
95cfce68ea backport of #2835 (#2841) 2018-01-23 05:45:45 +08:00
Alex Lam S.L
ec4202590d drop assignments to constant expressions only (#2839)
fixes #2838
2018-01-23 02:49:54 +08:00
Alex Lam S.L
5e2cd07d6f handle duplicate function declarations correctly (#2837)
fixes #2836
2018-01-23 01:28:09 +08:00
19 changed files with 1609 additions and 148 deletions

61
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,61 @@
Contributing
============
## Documentation
Every new feature and API change should be accompanied by a README additon.
## Testing
All features and bugs should have tests that verify the fix. You can run all
tests using `npm test`.
The most common type of test are tests that verify input and output of the
Uglify transforms. These tests exist in `test/compress`. New tests can be added
either to an existing file or in a new file `issue-xxx.js`.
Tests that cannot be expressed as a simple AST can be found in `test/mocha`.
## Code style
- File encoding must be `UTF-8`.
- `LF` is always used as a line ending.
- Statements end with semicolons.
- Indentation uses 4 spaces, switch `case` 2 spaces.
- Identifiers use `snake_case`.
- Strings use double quotes (`"`).
- Use a trailing comma for multiline array and object literals to minimize diffs.
- The Uglify code only uses ES5, even in the `harmony` branch.
- Line length should be at most 80 cols, except when it is easier to read a
longer line.
- If both sides of a comparison are of the same type, `==` and `!=` are used.
- Multiline conditions place `&&` and `||` first on the line.
**Example feature**
```js
OPT(AST_Debugger, function(self, compressor) {
if (compressor.option("drop_debugger"))
return make_node(AST_EmptyStatement, self);
return self;
});
```
**Example test case**
```js
drop_debugger: {
options = {
drop_debugger: true,
}
input: {
debugger;
if (foo) debugger;
}
expect: {
if (foo);
}
}
```

View File

@@ -1,6 +1,6 @@
UglifyJS is released under the BSD license:
Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions

View File

@@ -1102,3 +1102,27 @@ To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```
#### Source maps and debugging
Various `compress` transforms that simplify, rearrange, inline and remove code
are known to have an adverse effect on debugging with source maps. This is
expected as code is optimized and mappings are often simply not possible as
some code no longer exists. For highest fidelity in source map debugging
disable the Uglify `compress` option and just use `mangle`.
### Compiler assumptions
To allow for better optimizations, the compiler makes various assumptions:
- `.toString()` and `.valueOf()` don't have side effects, and for built-in
objects they have not been overridden.
- `undefined`, `NaN` and `Infinity` have not been externally redefined.
- `arguments.callee`, `arguments.caller` and `Function.prototype.caller` are not used.
- The code doesn't expect the contents of `Function.prototype.toString()` or
`Error.prototype.stack` to be anything in particular.
- Getting and setting properties on a plain object does not cause other side effects
(using `.watch()` or `Proxy`).
- Object properties can be added, removed and modified (not prevented with
`Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`Object.preventExtensions()` or `Object.seal()`).

View File

@@ -320,6 +320,7 @@ merge(Compressor.prototype, {
function reset_def(compressor, def) {
def.assignments = 0;
def.chained = false;
def.direct_access = false;
def.escaped = false;
if (def.scope.uses_eval || def.scope.uses_with) {
@@ -485,19 +486,27 @@ merge(Compressor.prototype, {
});
def(AST_Assign, function(tw) {
var node = this;
if (node.operator != "=" || !(node.left instanceof AST_SymbolRef)) return;
if (!(node.left instanceof AST_SymbolRef)) return;
var d = node.left.definition();
if (safe_to_assign(tw, d, node.right)) {
d.references.push(node.left);
d.assignments++;
d.fixed = function() {
return node.right;
};
mark(tw, d, false);
node.right.walk(tw);
mark(tw, d, true);
return true;
}
var fixed = d.fixed;
if (!fixed && node.operator != "=") return;
if (!safe_to_assign(tw, d, node.right)) return;
d.references.push(node.left);
d.assignments++;
if (node.operator != "=") d.chained = true;
d.fixed = node.operator == "=" ? function() {
return node.right;
} : function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: fixed instanceof AST_Node ? fixed : fixed(),
right: node.right
});
};
mark(tw, d, false);
node.right.walk(tw);
mark(tw, d, true);
return true;
});
def(AST_Binary, function(tw) {
if (!lazy_op(this.operator)) return;
@@ -672,22 +681,48 @@ merge(Compressor.prototype, {
if (this.bfinally) this.bfinally.walk(tw);
return true;
});
def(AST_Unary, function(tw, descend) {
var node = this;
if (node.operator != "++" && node.operator != "--") return;
if (!(node.expression instanceof AST_SymbolRef)) return;
var d = node.expression.definition();
var fixed = d.fixed;
if (!fixed) return;
if (!safe_to_assign(tw, d, true)) return;
d.references.push(node.expression);
d.assignments++;
d.chained = true;
d.fixed = function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: make_node(AST_UnaryPrefix, node, {
operator: "+",
expression: fixed instanceof AST_Node ? fixed : fixed()
}),
right: make_node(AST_Number, node, {
value: 1
})
});
};
mark(tw, d, true);
return true;
});
def(AST_VarDef, function(tw, descend) {
var node = this;
var d = node.name.definition();
if (safe_to_assign(tw, d, node.value)) {
if (node.value) {
if (node.value) {
if (safe_to_assign(tw, d, node.value)) {
d.fixed = function() {
return node.value;
};
tw.loop_ids[d.id] = tw.in_loop;
mark(tw, d, false);
descend();
mark(tw, d, true);
return true;
} else {
d.fixed = false;
}
mark(tw, d, true);
return true;
} else if (node.value) {
d.fixed = false;
}
});
def(AST_While, function(tw) {
@@ -879,6 +914,7 @@ merge(Compressor.prototype, {
function tighten_body(statements, compressor) {
var scope = compressor.find_parent(AST_Scope);
var in_loop = is_in_loop();
var CHANGED, max_iter = 10;
do {
CHANGED = false;
@@ -901,6 +937,14 @@ merge(Compressor.prototype, {
}
} while (CHANGED && max_iter-- > 0);
function is_in_loop() {
for (var node, level = 0; node = compressor.parent(level); level++) {
if (node instanceof AST_IterationStatement) return true;
if (node instanceof AST_Scope) break;
}
return false;
}
// Search from right to left for assignment-like expressions:
// - `var a = x;`
// - `a = x;`
@@ -917,32 +961,11 @@ merge(Compressor.prototype, {
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
}
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
if (branch !== hit_stack[hit_index]) continue;
hit_index++;
}
branch.expression = branch.expression.transform(scanner);
if (side_effects || !replace_all) break;
}
}
abort = true;
return node;
}
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
if (hit_index < hit_stack.length) return handle_custom_scan_order(node);
hit = true;
stop_after = find_stop(node, 0);
if (stop_after === node) abort = true;
@@ -954,18 +977,30 @@ merge(Compressor.prototype, {
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_LoopControl
|| node instanceof AST_Try
|| node instanceof AST_With
|| parent instanceof AST_For && node !== parent.init
|| (side_effects || !replace_all)
|| !replace_all
&& (node instanceof AST_SymbolRef && !node.is_declared(compressor))) {
abort = true;
return node;
}
// Stop only if candidate is found within conditional branches
if (!stop_if_hit && (!lhs_local || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator) && parent.left !== node
|| parent instanceof AST_Conditional && parent.condition !== node
|| parent instanceof AST_If && parent.condition !== node)) {
stop_if_hit = parent;
}
// Replace variable with assignment when found
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) {
if (stop_if_hit) {
abort = true;
return node;
}
if (is_lhs(node, parent)) {
if (value_def) replaced++;
return node;
@@ -1010,26 +1045,25 @@ merge(Compressor.prototype, {
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
&& (side_effects || lhs instanceof AST_PropAccess || may_modify(lhs))
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (lvalues[node.name] || side_effects && may_modify(node))
|| node instanceof AST_VarDef && node.value
&& (node.name.name in lvalues || side_effects && may_modify(node.name))
|| (sym = is_lhs(node.left, node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
return handle_custom_scan_order(node);
}, function(node) {
if (!abort && stop_after === node) abort = true;
if (abort) return;
if (stop_after === node) abort = true;
if (stop_if_hit === node) stop_if_hit = null;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
@@ -1068,19 +1102,15 @@ merge(Compressor.prototype, {
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var stop_if_hit = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
replace_all = true;
}
}
var side_effects = value_has_side_effects(candidate);
var replace_all = replace_all_symbols();
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
@@ -1111,6 +1141,28 @@ merge(Compressor.prototype, {
}
}
function handle_custom_scan_order(node) {
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
if (branch !== hit_stack[hit_index]) continue;
hit_index++;
}
branch.expression = branch.expression.transform(scanner);
if (!replace_all) break;
}
}
abort = true;
return node;
}
}
function extract_args() {
var iife, fn = compressor.self();
if (fn instanceof AST_Function
@@ -1217,6 +1269,8 @@ merge(Compressor.prototype, {
} else if (expr instanceof AST_Unary) {
if (expr.operator == "++" || expr.operator == "--") {
candidates.push(hit_stack.slice());
} else {
extract_candidates(expr.expression);
}
} else if (expr instanceof AST_VarDef) {
if (expr.value) {
@@ -1227,19 +1281,51 @@ merge(Compressor.prototype, {
hit_stack.pop();
}
function find_stop(node, level) {
function find_stop(node, level, write_only) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Assign) {
if (write_only
&& !(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_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_Conditional) {
if (write_only && parent.condition === node) {
return find_stop(parent, level + 1, write_only);
}
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_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Sequence) {
return find_stop(parent, level + 1, parent.tail_node() !== node);
}
if (parent instanceof AST_SimpleStatement) {
return find_stop(parent, level + 1, true);
}
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_Unary) return node;
if (parent instanceof AST_VarDef) return node;
return null;
}
@@ -1286,11 +1372,6 @@ merge(Compressor.prototype, {
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var index = compressor.self().argnames.indexOf(expr.name);
@@ -1319,36 +1400,52 @@ merge(Compressor.prototype, {
}));
}
function is_lhs_local(lhs) {
while (lhs instanceof AST_PropAccess) lhs = lhs.expression;
return lhs instanceof AST_SymbolRef
&& lhs.definition().scope === scope
&& !(in_loop
&& (lhs.name in lvalues
|| candidate instanceof AST_Unary
|| candidate instanceof AST_Assign && candidate.operator != "="));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope !== scope) return false;
return def.references.every(function(ref) {
function replace_all_symbols() {
if (side_effects) return false;
if (value_def) return true;
if (lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
return true;
}
}
return false;
}
function may_modify(sym) {
var def = sym.definition();
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
if (def.scope !== scope) return true;
return !all(def.references, function(ref) {
return ref.scope === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Assign) return side_effects_external(node.left, true);
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
return false;
}
}
@@ -1642,7 +1739,7 @@ merge(Compressor.prototype, {
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
var line = block.body[i];
if (line instanceof AST_Definitions && declarations_only(line)) {
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat) {
return false;
@@ -1748,11 +1845,12 @@ merge(Compressor.prototype, {
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
var diff = compressor.has_directive("use strict") ? function(node) {
return node.key != prop && node.key.name != prop;
} : function(node) {
return node.key.name != prop;
};
if (!all(def.value.properties, diff)) break;
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
@@ -2156,6 +2254,8 @@ merge(Compressor.prototype, {
"split",
"substr",
"substring",
"toLowerCase",
"toUpperCase",
"trim",
].concat(object_fns),
};
@@ -2275,6 +2375,7 @@ merge(Compressor.prototype, {
}
return this;
});
var non_converting_unary = makePredicate("! typeof void");
def(AST_UnaryPrefix, function(compressor, depth) {
var e = this.expression;
// Function would be evaluated to an array and so typeof would
@@ -2286,6 +2387,7 @@ merge(Compressor.prototype, {
&& e.fixed_value() instanceof AST_Lambda)) {
return typeof function(){};
}
if (!non_converting_unary(this.operator)) depth++;
e = e._eval(compressor, depth);
if (e === this.expression) return this;
switch (this.operator) {
@@ -2302,7 +2404,9 @@ merge(Compressor.prototype, {
}
return this;
});
var non_converting_binary = makePredicate("&& || === !==");
def(AST_Binary, function(compressor, depth) {
if (!non_converting_binary(this.operator)) depth++;
var left = this.left._eval(compressor, depth);
if (left === this.left) return this;
var right = this.right._eval(compressor, depth);
@@ -2688,8 +2792,13 @@ merge(Compressor.prototype, {
return any(this.elements, compressor);
});
def(AST_Assign, function(compressor){
return this.operator != "=" && this.left.may_throw(compressor)
|| this.right.may_throw(compressor);
if (this.right.may_throw(compressor)) return true;
if (!compressor.has_directive("use strict")
&& this.operator == "="
&& this.left instanceof AST_SymbolRef) {
return false;
}
return this.left.may_throw(compressor);
});
def(AST_Binary, function(compressor){
return this.left.may_throw(compressor)
@@ -2946,7 +3055,7 @@ merge(Compressor.prototype, {
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
if (def.name.fixed_value() === def.value) {
if (!node_def.chained && def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
@@ -3038,9 +3147,11 @@ merge(Compressor.prototype, {
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
var ref = make_node(AST_SymbolRef, def.name, def.name);
sym.references.push(ref);
var assign = make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
@@ -3162,7 +3273,7 @@ merge(Compressor.prototype, {
&& self.variables.get(sym.name) === (node_def = sym.definition())) {
if (node instanceof AST_Assign) {
node.right.walk(tw);
if (node.left.fixed_value() === node.right) {
if (!node_def.chained && node.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
@@ -3469,8 +3580,10 @@ merge(Compressor.prototype, {
while (left instanceof AST_PropAccess) {
left = left.expression;
}
if (left instanceof AST_Symbol) return this;
return this.right.drop_side_effect_free(compressor);
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
}
return this;
});
def(AST_Conditional, function(compressor){
var consequent = this.consequent.drop_side_effect_free(compressor);
@@ -3592,6 +3705,13 @@ merge(Compressor.prototype, {
]
}).optimize(compressor);
}
if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
condition: make_sequence(self.condition, [
self.body.body,
self.condition
]),
body: make_node(AST_EmptyStatement, self)
}).optimize(compressor);
return self;
});
@@ -4299,6 +4419,7 @@ merge(Compressor.prototype, {
&& !self.pure
&& !fn.contains_this()
&& can_inject_symbols()) {
fn._squeezed = true;
return make_sequence(self, flatten_fn()).optimize(compressor);
}
if (compressor.option("side_effects") && all(fn.body, is_empty)) {
@@ -4718,6 +4839,7 @@ merge(Compressor.prototype, {
if (compressor.option("comparisons")) switch (self.operator) {
case "===":
case "!==":
var is_strict_comparison = true;
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
(self.left.is_boolean() && self.right.is_boolean()) ||
@@ -4727,8 +4849,12 @@ merge(Compressor.prototype, {
// XXX: intentionally falling down to the next case
case "==":
case "!=":
// void 0 == x => null == x
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
self.left = make_node(AST_Null, self.left);
}
// "undefined" == typeof x => undefined === x
if (compressor.option("typeofs")
else if (compressor.option("typeofs")
&& self.left instanceof AST_String
&& self.left.value == "undefined"
&& self.right instanceof AST_UnaryPrefix
@@ -4749,6 +4875,35 @@ merge(Compressor.prototype, {
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
}
break;
case "&&":
case "||":
var lhs = self.left;
if (lhs.operator == self.operator) {
lhs = lhs.right;
}
if (lhs instanceof AST_Binary
&& lhs.operator == (self.operator == "&&" ? "!==" : "===")
&& self.right instanceof AST_Binary
&& lhs.operator == self.right.operator
&& (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
|| lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
&& !lhs.right.has_side_effects(compressor)
&& lhs.right.equivalent_to(self.right.right)) {
var combined = make_node(AST_Binary, self, {
operator: lhs.operator.slice(0, -1),
left: make_node(AST_Null, self),
right: lhs.right
});
if (lhs !== self.left) {
combined = make_node(AST_Binary, self, {
operator: self.operator,
left: self.left.left,
right: combined
});
}
return combined;
}
break;
}
if (self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor);
@@ -5198,7 +5353,7 @@ merge(Compressor.prototype, {
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
overhead = (name_length + 2 + value_length) / (d.references.length - d.assignments);
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
@@ -5319,6 +5474,7 @@ merge(Compressor.prototype, {
if (in_try(level, parent instanceof AST_Throw)) break;
if (is_reachable(def.scope, [ def ])) break;
if (self.operator == "=") return self.right;
def.fixed = false;
return make_node(AST_Binary, self, {
operator: self.operator.slice(0, -1),
left: self.left,

View File

@@ -305,7 +305,7 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
AST_Scope.DEFMETHOD("def_function", function(symbol, init){
var def = this.def_variable(symbol, init);
if (!def.init) def.init = init;
if (!def.init || def.init instanceof AST_Defun) def.init = init;
this.functions.set(symbol.name, def);
return def;
});

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.3.8",
"version": "3.3.11",
"engines": {
"node": ">=0.8.0"
},
@@ -24,13 +24,13 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.13.0",
"commander": "~2.14.1",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.3.0",
"acorn": "~5.4.1",
"mocha": "~3.5.1",
"semver": "~5.4.1"
"semver": "~5.5.0"
},
"scripts": {
"test": "node test/run-tests.js"

View File

@@ -52,13 +52,8 @@ collapse_vars_side_effects_1: {
console.log.bind(console)(s.charAt(i++), s.charAt(i++), s.charAt(i++), 7);
}
function f2() {
var log = console.log.bind(console),
s = "abcdef",
i = 2,
x = s.charAt(i++),
y = s.charAt(i++),
z = s.charAt(i++);
log(x, i, y, z, 7);
var s = "abcdef", i = 2;
console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(i++), 7);
}
function f3() {
var s = "abcdef",
@@ -72,7 +67,7 @@ collapse_vars_side_effects_1: {
var i = 10,
x = i += 2,
y = i += 3;
console.log.bind(console)(x, i += 4, y, i);
console.log.bind(console)(x, i += 4, y, 19);
}
f1(), f2(), f3(), f4();
}
@@ -4112,3 +4107,515 @@ unsafe_builtin: {
}
expect_stdout: "1 4"
}
return_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var log = console.log;
function f(b, c) {
var a = c;
if (b) return b;
log(a);
}
f(false, 1);
f(true, 2);
}
expect: {
var log = console.log;
function f(b, c) {
if (b) return b;
log(c);
}
f(false, 1);
f(true, 2);
}
expect_stdout: "1"
}
return_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var log = console.log;
function f(b, c) {
var a = c();
if (b) return b;
log(a);
}
f(false, function() { return 1 });
f(true, function() { return 2 });
}
expect: {
var log = console.log;
function f(b, c) {
var a = c();
if (b) return b;
log(a);
}
f(false, function() { return 1 });
f(true, function() { return 2 });
}
expect_stdout: "1"
}
return_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var log = console.log;
function f(b, c) {
var a = b <<= c;
if (b) return b;
log(a);
}
f(false, 1);
f(true, 2);
}
expect: {
var log = console.log;
function f(b, c) {
var a = b <<= c;
if (b) return b;
log(a);
}
f(false, 1);
f(true, 2);
}
expect_stdout: "0"
}
return_4: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(function(b) {
a = "PASS";
return;
b(a);
})();
console.log(a);
}
expect: {
var a = "FAIL";
(function(b) {
a = "PASS";
return;
b(a);
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_2858: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var b;
(function() {
function f() {
a++;
}
f();
var c = f();
var a = void 0;
c || (b = a);
})();
console.log(b);
}
expect: {
var b;
(function() {
function f() {
a++;
}
f();
var c = f();
var a = void 0;
c || (b = a);
})();
console.log(b);
}
expect_stdout: "undefined"
}
cond_branch_1: {
options = {
collapse_vars: true,
sequences: true,
unused: true,
}
input: {
function f1(b, c) {
var log = console.log;
var a = ++c;
if (b) b++;
log(a, b);
}
function f2(b, c) {
var log = console.log;
var a = ++c;
b && b++;
log(a, b);
}
function f3(b, c) {
var log = console.log;
var a = ++c;
b ? b++ : b--;
log(a, b);
}
f1(1, 2);
f2(3, 4);
f3(5, 6);
}
expect: {
function f1(b, c) {
if (b) b++;
(0, console.log)(++c, b);
}
function f2(b, c) {
b && b++,
(0, console.log)(++c, b);
}
function f3(b, c) {
b ? b++ : b--,
(0, console.log)(++c, b);
}
f1(1, 2),
f2(3, 4),
f3(5, 6);
}
expect_stdout: [
"3 2",
"5 4",
"7 6",
]
}
cond_branch_2: {
options = {
collapse_vars: true,
sequences: true,
unused: true,
}
input: {
function f1(b, c) {
var log = console.log;
var a = ++c;
if (b) b += a;
log(a, b);
}
function f2(b, c) {
var log = console.log;
var a = ++c;
b && (b += a);
log(a, b);
}
function f3(b, c) {
var log = console.log;
var a = ++c;
b ? b += a : b--;
log(a, b);
}
f1(1, 2);
f2(3, 4);
f3(5, 6);
}
expect: {
function f1(b, c) {
var a = ++c;
if (b) b += a;
(0, console.log)(a, b);
}
function f2(b, c) {
var a = ++c;
b && (b += a),
(0, console.log)(a, b);
}
function f3(b, c) {
var a = ++c;
b ? b += a : b--,
(0, console.log)(a, b);
}
f1(1, 2),
f2(3, 4),
f3(5, 6);
}
expect_stdout: [
"3 4",
"5 8",
"7 12",
]
}
cond_branch_switch: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
if (c = 1 + c, 0) switch (c = 1 + c) {
}
console.log(c);
}
expect: {
var c = 0;
if (c = 1 + c, 0) switch (c = 1 + c) {
}
console.log(c);
}
expect_stdout: "1"
}
issue_2873_1: {
options = {
collapse_vars: true,
}
input: {
var b = 1, c = 0;
do {
c++;
if (!--b) break;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect: {
var b = 1, c = 0;
do {
c++;
if (!--b) break;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect_stdout: "0 1"
}
issue_2873_2: {
options = {
collapse_vars: true,
}
input: {
var b = 1, c = 0;
do {
c++;
if (!--b) continue;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect: {
var b = 1, c = 0;
do {
c++;
if (!--b) continue;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect_stdout: "0 1"
}
issue_2878: {
options = {
collapse_vars: true,
sequences: true,
}
input: {
var c = 0;
(function (a, b) {
function f2() {
if (a) c++;
}
b = f2();
a = 1;
b && b.b;
f2();
})();
console.log(c);
}
expect: {
var c = 0;
(function (a, b) {
function f2() {
if (a) c++;
}
b = f2(),
a = 1,
b && b.b,
f2();
})(),
console.log(c);
}
expect_stdout: "1"
}
issue_2891_1: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS", b;
try {
b = c.p = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect: {
var a = "PASS", b;
try {
b = c.p = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect_stdout: "PASS"
}
issue_2891_2: {
options = {
collapse_vars: true,
}
input: {
"use strict";
var a = "PASS", b;
try {
b = c = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect: {
"use strict";
var a = "PASS", b;
try {
b = c = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect_stdout: true
}
issue_2908: {
options = {
collapse_vars: true,
}
input: {
var a = 0, b = 0;
function f(c) {
if (1 == c) return;
a++;
if (2 == c) b = a;
}
f(0);
f(2);
console.log(b);
}
expect: {
var a = 0, b = 0;
function f(c) {
if (1 == c) return;
a++;
if (2 == c) b = a;
}
f(0);
f(2);
console.log(b);
}
expect_stdout: "2"
}
issue_2914_1: {
options = {
collapse_vars: true,
}
input: {
function read(input) {
var i = 0;
var e = 0;
var t = 0;
while (e < 32) {
var n = input[i++];
t |= (127 & n) << e;
if (0 === (128 & n))
return t;
e += 7;
}
}
console.log(read([129]));
}
expect: {
function read(input) {
var i = 0;
var e = 0;
var t = 0;
while (e < 32) {
var n = input[i++];
t |= (127 & n) << e;
if (0 === (128 & n))
return t;
e += 7;
}
}
console.log(read([129]));
}
expect_stdout: "1"
}
issue_2914_2: {
options = {
collapse_vars: true,
}
input: {
function read(input) {
var i = 0;
var e = 0;
var t = 0;
while (e < 32) {
var n = input[i++];
t = (127 & n) << e;
if (0 === (128 & n))
return t;
e += 7;
}
}
console.log(read([129]));
}
expect: {
function read(input) {
var i = 0;
var e = 0;
var t = 0;
while (e < 32) {
var n = input[i++];
if (0 === (128 & n))
return t = (127 & n) << e;
e += 7;
}
}
console.log(read([129]));
}
expect_stdout: "0"
}

View File

@@ -112,3 +112,206 @@ self_comparison_2: {
}
expect_stdout: "false true"
}
issue_2857_1: {
options = {
comparisons: true,
}
input: {
function f1(a) {
a === undefined || a === null;
a === undefined || a !== null;
a !== undefined || a === null;
a !== undefined || a !== null;
a === undefined && a === null;
a === undefined && a !== null;
a !== undefined && a === null;
a !== undefined && a !== null;
}
function f2(a) {
a === null || a === undefined;
a === null || a !== undefined;
a !== null || a === undefined;
a !== null || a !== undefined;
a === null && a === undefined;
a === null && a !== undefined;
a !== null && a === undefined;
a !== null && a !== undefined;
}
}
expect: {
function f1(a) {
null == a;
void 0 === a || null !== a;
void 0 !== a || null === a;
void 0 !== a || null !== a;
void 0 === a && null === a;
void 0 === a && null !== a;
void 0 !== a && null === a;
null != a;
}
function f2(a) {
null == a;
null === a || void 0 !== a;
null !== a || void 0 === a;
null !== a || void 0 !== a;
null === a && void 0 === a;
null === a && void 0 !== a;
null !== a && void 0 === a;
null != a;
}
}
}
issue_2857_2: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
a === undefined || a === null || p;
a === undefined || a !== null || p;
a !== undefined || a === null || p;
a !== undefined || a !== null || p;
a === undefined && a === null || p;
a === undefined && a !== null || p;
a !== undefined && a === null || p;
a !== undefined && a !== null || p;
}
}
expect: {
function f(a, p) {
null == a || p;
void 0 === a || null !== a || p;
void 0 !== a || null === a || p;
void 0 !== a || null !== a || p;
void 0 === a && null === a || p;
void 0 === a && null !== a || p;
void 0 !== a && null === a || p;
null != a || p;
}
}
}
issue_2857_3: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
a === undefined || a === null && p;
a === undefined || a !== null && p;
a !== undefined || a === null && p;
a !== undefined || a !== null && p;
a === undefined && a === null && p;
a === undefined && a !== null && p;
a !== undefined && a === null && p;
a !== undefined && a !== null && p;
}
}
expect: {
function f(a, p) {
void 0 === a || null === a && p;
void 0 === a || null !== a && p;
void 0 !== a || null === a && p;
void 0 !== a || null !== a && p;
void 0 === a && null === a && p;
void 0 === a && null !== a && p;
void 0 !== a && null === a && p;
null != a && p;
}
}
}
issue_2857_4: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
p || a === undefined || a === null;
p || a === undefined || a !== null;
p || a !== undefined || a === null;
p || a !== undefined || a !== null;
p || a === undefined && a === null;
p || a === undefined && a !== null;
p || a !== undefined && a === null;
p || a !== undefined && a !== null;
}
}
expect: {
function f(a, p) {
p || null == a;
p || void 0 === a || null !== a;
p || void 0 !== a || null === a;
p || void 0 !== a || null !== a;
p || void 0 === a && null === a;
p || void 0 === a && null !== a;
p || void 0 !== a && null === a;
p || null != a;
}
}
}
issue_2857_5: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
p && a === undefined || a === null;
p && a === undefined || a !== null;
p && a !== undefined || a === null;
p && a !== undefined || a !== null;
p && a === undefined && a === null;
p && a === undefined && a !== null;
p && a !== undefined && a === null;
p && a !== undefined && a !== null;
}
}
expect: {
function f(a, p) {
p && void 0 === a || null === a;
p && void 0 === a || null !== a;
p && void 0 !== a || null === a;
p && void 0 !== a || null !== a;
p && void 0 === a && null === a;
p && void 0 === a && null !== a;
p && void 0 !== a && null === a;
p && null != a;
}
}
}
issue_2857_6: {
options = {
comparisons: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
function f(a) {
if (({}).b === undefined || {}.b === null)
return a.b !== undefined && a.b !== null;
}
console.log(f({
a: [ null ],
get b() {
return this.a.shift();
}
}));
}
expect: {
function f(a) {
if (null == {}.b)
return void 0 !== a.b && null !== a.b;
}
console.log(f({
a: [ null ],
get b() {
return this.a.shift();
}
}));
}
expect_stdout: "true"
}

View File

@@ -879,3 +879,41 @@ unsafe_builtin: {
z;
}
}
issue_2860_1: {
options = {
dead_code: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
}());
}
expect: {
console.log(function(a) {
return 1 ^ a;
}());
}
expect_stdout: "1"
}
issue_2860_2: {
options = {
dead_code: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
}());
}
expect: {
console.log(1);
}
expect_stdout: "1"
}

View File

@@ -1692,3 +1692,30 @@ issue_2768: {
}
expect_stdout: "PASS undefined"
}
issue_2846: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
var a = 0;
b && b(a);
return a++;
}
var c = f();
console.log(c);
}
expect: {
var c = function(a, b) {
a = 0;
b && b(a);
return a++;
}();
console.log(c);
}
expect_stdout: "0"
}

View File

@@ -1374,3 +1374,118 @@ issue_2822: {
}
expect_stdout: "PASS"
}
string_case: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("İ".toLowerCase().charCodeAt(0));
console.log("I".toLowerCase().charCodeAt(0));
console.log("Ş".toLowerCase().charCodeAt(0));
console.log("Ğ".toLowerCase().charCodeAt(0));
console.log("Ü".toLowerCase().charCodeAt(0));
console.log("Ö".toLowerCase().charCodeAt(0));
console.log("Ç".toLowerCase().charCodeAt(0));
console.log("i".toUpperCase().charCodeAt(0));
console.log("ı".toUpperCase().charCodeAt(0));
console.log("ş".toUpperCase().charCodeAt(0));
console.log("ğ".toUpperCase().charCodeAt(0));
console.log("ü".toUpperCase().charCodeAt(0));
console.log("ö".toUpperCase().charCodeAt(0));
console.log("ç".toUpperCase().charCodeAt(0));
}
expect: {
console.log(105);
console.log(105);
console.log(351);
console.log(287);
console.log(252);
console.log(246);
console.log(231);
console.log(73);
console.log(73);
console.log(350);
console.log(286);
console.log(220);
console.log(214);
console.log(199);
}
expect_stdout: [
"105",
"105",
"351",
"287",
"252",
"246",
"231",
"73",
"73",
"350",
"286",
"220",
"214",
"199",
]
}
issue_2916_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
var c = "PASS";
(function(a, b) {
(function(d) {
d[0] = 1;
})(b);
a == b && (c = "FAIL");
})("", []);
console.log(c);
}
expect: {
var c = "PASS";
(function(a, b) {
(function(d) {
d[0] = 1;
})(b);
a == b && (c = "FAIL");
})("", []);
console.log(c);
}
expect_stdout: "PASS"
}
issue_2916_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var c = "FAIL";
(function(b) {
(function(d) {
d[0] = 1;
})(b);
+b && (c = "PASS");
})([]);
console.log(c);
}
expect: {
var c = "FAIL";
(function(b) {
b[0] = 1;
+b && (c = "PASS");
})([]);
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -1343,7 +1343,7 @@ issue_2630_4: {
var x = 3, a = 1, b = 2;
(function() {
(function() {
while (--x >= 0 && void (a++, b += a));
while (--x >= 0 && void (b += ++a));
})();
})();
console.log(a);
@@ -1989,3 +1989,33 @@ issue_2783: {
}
expect_stdout: "PASS"
}
issue_2898: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
var c = 0;
(function() {
while (f());
function f() {
var b = (c = 1 + c, void (c = 1 + c));
b && b[0];
}
})();
console.log(c);
}
expect: {
var c = 0;
(function() {
while (b = void 0, void ((b = void (c = 1 + (c = 1 + c))) && b[0]));
var b;
})(),
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -0,0 +1,37 @@
comparison_with_undefined: {
options = {
comparisons: true,
}
input: {
a == undefined;
a != undefined;
a === undefined;
a !== undefined;
undefined == a;
undefined != a;
undefined === a;
undefined !== a;
void 0 == a;
void 0 != a;
void 0 === a;
void 0 !== a;
}
expect: {
null == a;
null != a;
void 0 === a;
void 0 !== a;
null == a;
null != a;
void 0 === a;
void 0 !== a;
null == a;
null != a;
void 0 === a;
void 0 !== a;
}
}

View File

@@ -605,3 +605,20 @@ issue_2740_5: {
}
expect_stdout: "0 undefined"
}
issue_2904: {
options = {
join_vars: true,
loops: true,
}
input: {
var a = 1;
do {
console.log(a);
} while (--a);
}
expect: {
for (var a = 1; console.log(a), --a;);
}
expect_stdout: "1"
}

View File

@@ -1588,3 +1588,55 @@ issue_2816: {
}
expect_stdout: "3 2 4"
}
issue_2893_1: {
options = {
join_vars: true,
}
input: {
var o = {
get a() {
return "PASS";
},
};
o.a = "FAIL";
console.log(o.a);
}
expect: {
var o = {
get a() {
return "PASS";
},
};
o.a = "FAIL";
console.log(o.a);
}
expect_stdout: "PASS"
}
issue_2893_2: {
options = {
join_vars: true,
}
input: {
var o = {
set a(v) {
this.b = v;
},
b: "FAIL",
};
o.a = "PASS";
console.log(o.b);
}
expect: {
var o = {
set a(v) {
this.b = v;
},
b: "FAIL",
};
o.a = "PASS";
console.log(o.b);
}
expect_stdout: "PASS"
}

View File

@@ -694,3 +694,30 @@ issue_2678: {
}
expect_stdout: "PASS"
}
issue_2838: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
function f(a, b) {
(a || b).c = "PASS";
(function() {
return f(a, b);
}).prototype.foo = "bar";
}
var o = {};
f(null, o);
console.log(o.c);
}
expect: {
function f(a, b) {
(a || b).c = "PASS";
}
var o = {};
f(null, o);
console.log(o.c);
}
expect_stdout: "PASS"
}

View File

@@ -76,14 +76,12 @@ modified: {
console.log(a + 1);
console.log(b + 1);
}
function f1() {
var a = 1, b = 2;
--b;
console.log(a + 1);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
@@ -92,7 +90,6 @@ modified: {
console.log(a + c);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
@@ -101,7 +98,6 @@ modified: {
console.log(a + c);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
if (a) {
@@ -114,28 +110,26 @@ modified: {
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
console.log(typeof A ? "yes" : "no");
console.log(typeof B ? "yes" : "no");
}
f0(), f1(), f2(), f3(), f4(), f5();
}
expect: {
function f0() {
var b = 2;
b++;
console.log(2);
console.log(b + 1);
console.log(4);
}
function f1() {
var b = 2;
--b;
console.log(2);
console.log(b + 1);
console.log(2);
}
function f2() {
3;
console.log(4);
@@ -143,16 +137,14 @@ modified: {
console.log(4);
console.log(7);
}
function f3() {
var b = 2;
b *= 3;
console.log(1 + b);
console.log(b + 3);
console.log(7);
console.log(9);
console.log(4);
console.log(1 + b + 3);
console.log(10);
}
function f4() {
var b = 2, c = 3;
b = c;
@@ -161,13 +153,33 @@ modified: {
console.log(1 + c);
console.log(1 + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
console.log(typeof A ? "yes" : "no");
console.log(typeof B ? "yes" : "no");
}
f0(), f1(), f2(), f3(), f4(), f5();
}
expect_stdout: [
"2",
"4",
"2",
"2",
"4",
"6",
"4",
"7",
"7",
"9",
"4",
"10",
"4",
"6",
"4",
"7",
"yes",
"yes",
]
}
unsafe_evaluate: {
@@ -745,7 +757,7 @@ iife: {
expect: {
!function(a, b, c) {
b++;
console.log(0, 1 * b, 5);
console.log(0, 3, 5);
}(1, 2, 3);
}
expect_stdout: true
@@ -766,7 +778,7 @@ iife_new: {
expect: {
var A = new function(a, b, c) {
b++;
console.log(0, 1 * b, 5);
console.log(0, 3, 5);
}(1, 2, 3);
}
expect_stdout: true
@@ -1166,9 +1178,7 @@ toplevel_on_loops_1: {
console.log("bar:", --x);
}
var x = 3;
do
bar();
while (x);
for (;bar(), x;);
}
expect_stdout: true
}
@@ -1196,9 +1206,7 @@ toplevel_off_loops_1: {
console.log("bar:", --x);
}
var x = 3;
do
bar();
while (x);
for (;bar(), x;);
}
expect_stdout: true
}
@@ -1253,9 +1261,7 @@ toplevel_off_loops_2: {
console.log("bar:");
}
var x = 3;
do
bar();
while (x);
for (;bar(), x;);
}
}
@@ -5360,3 +5366,164 @@ issue_2799_2: {
}
expect_stdout: "PASS"
}
issue_2836: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return "FAIL";
}
console.log(f());
function f() {
return "PASS";
}
}
expect: {
console.log(function() {
return "PASS";
}());
}
expect_stdout: "PASS"
}
lvalues_def_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
var a = b++, b = NaN;
console.log(a, b);
}
expect: {
var b = 1;
var a = b++;
b = NaN;
console.log(a, b);
}
expect_stdout: "1 NaN"
}
lvalues_def_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
var a = b += 1, b = NaN;
console.log(a, b);
}
expect: {
var b = 1;
var a = b += 1;
b = NaN;
console.log(a, b);
}
expect_stdout: "2 NaN"
}
chained_assignments: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function f() {
var a = [0x5e, 0xad, 0xbe, 0xef];
var b = 0;
b |= a[0];
b <<= 8;
b |= a[1];
b <<= 8;
b |= a[2];
b <<= 8;
b |= a[3];
return b;
}
console.log(f().toString(16));
}
expect: {
console.log("5eadbeef");
}
expect_stdout: "5eadbeef"
}
issue_2860_1: {
options = {
dead_code: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
a ^= 2;
}());
}
expect: {
console.log(function(a) {
return 1 ^ a;
}());
}
expect_stdout: "1"
}
issue_2860_2: {
options = {
dead_code: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
a ^= 2;
}());
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_2869: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = "FAIL";
(function f(a) {
var a;
if (!f) a = 0;
if (a) c = "PASS";
})(1);
console.log(c);
}
expect: {
var c = "FAIL";
(function f(a) {
var a;
if (!f) a = 0;
if (a) c = "PASS";
})(1);
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -372,8 +372,9 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
var options_formatted = JSON.stringify(options, null, 4);
var result = U.minify(input_code, options);
if (result.error) {
log("!!! failed input reminify\n---INPUT---\n{input}\n--ERROR---\n{error}\n\n", {
log("!!! failed input reminify\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n--ERROR---\n{error}\n\n", {
input: input_formatted,
options: options_formatted,
error: result.error,
});
return false;

View File

@@ -1024,7 +1024,6 @@ function log_suspects(minify_options, component) {
}
function log_rename(options) {
if (!options.rename) return;
var m = JSON.parse(JSON.stringify(options));
m.rename = false;
var result = UglifyJS.minify(original_code, m);