Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f8a484e6 | ||
|
|
c736834aa4 | ||
|
|
9a98513981 | ||
|
|
f631d6437a | ||
|
|
aa7e8783f8 | ||
|
|
13e5e33448 | ||
|
|
487ae8e3be | ||
|
|
5dfda6e212 | ||
|
|
d08c772eb3 | ||
|
|
90ed54401b | ||
|
|
d8106b6c63 | ||
|
|
dda4eb96e1 | ||
|
|
7305ba0296 | ||
|
|
2c21dc5e8e | ||
|
|
d0faa471db | ||
|
|
6ad823d1e8 | ||
|
|
43ad4e9775 | ||
|
|
04b8964505 | ||
|
|
d6fbc365e2 | ||
|
|
9a978843f5 | ||
|
|
0479ff0c54 | ||
|
|
cf72fe552f | ||
|
|
a1532eb076 | ||
|
|
c2a1bceb77 | ||
|
|
e3c9c22c75 | ||
|
|
0f4cd73dcc | ||
|
|
281e882d27 | ||
|
|
cc6aa3e5ac | ||
|
|
e869779a98 | ||
|
|
06cdb74279 | ||
|
|
ff289b90a9 | ||
|
|
9b6bc67c33 | ||
|
|
4b90dc1fdb | ||
|
|
951770fc68 | ||
|
|
48b3fe9952 | ||
|
|
a400741868 | ||
|
|
59a4e56bc8 | ||
|
|
1f1fccc45d |
40
README.md
40
README.md
@@ -11,9 +11,10 @@ There's also an
|
|||||||
Chrome and probably Safari).
|
Chrome and probably Safari).
|
||||||
|
|
||||||
#### Note:
|
#### Note:
|
||||||
- release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
|
- `uglify-js` only supports ECMAScript 5 (ES5).
|
||||||
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
|
- Support for `const` is [present but incomplete](#support-for-const), and may not be
|
||||||
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
|
transformed properly.
|
||||||
|
- Those wishing to minify ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
|
||||||
|
|
||||||
Install
|
Install
|
||||||
-------
|
-------
|
||||||
@@ -29,12 +30,6 @@ From NPM for programmatic use:
|
|||||||
|
|
||||||
npm install uglify-js
|
npm install uglify-js
|
||||||
|
|
||||||
From Git:
|
|
||||||
|
|
||||||
git clone git://github.com/mishoo/UglifyJS2.git
|
|
||||||
cd UglifyJS2
|
|
||||||
npm link .
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@@ -411,6 +406,8 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
|||||||
- `pure_getters` -- the default is `false`. If you pass `true` for
|
- `pure_getters` -- the default is `false`. If you pass `true` for
|
||||||
this, UglifyJS will assume that object property access
|
this, UglifyJS will assume that object property access
|
||||||
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
|
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
|
||||||
|
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
|
||||||
|
`foo` is certain to not throw, i.e. not `null` or `undefined`.
|
||||||
|
|
||||||
- `pure_funcs` -- default `null`. You can pass an array of names and
|
- `pure_funcs` -- default `null`. You can pass an array of names and
|
||||||
UglifyJS will assume that those functions do not produce side
|
UglifyJS will assume that those functions do not produce side
|
||||||
@@ -446,6 +443,11 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
|||||||
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
|
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
|
||||||
being compressed into `1/0`, which may cause performance issues on Chrome.
|
being compressed into `1/0`, which may cause performance issues on Chrome.
|
||||||
|
|
||||||
|
- `side_effects` -- default `false`. Pass `true` to potentially drop functions
|
||||||
|
marked as "pure". (A function is marked as "pure" via the comment annotation
|
||||||
|
`/* @__PURE__ */` or `/* #__PURE__ */`)
|
||||||
|
|
||||||
|
|
||||||
### The `unsafe` option
|
### The `unsafe` option
|
||||||
|
|
||||||
It enables some transformations that *might* break code logic in certain
|
It enables some transformations that *might* break code logic in certain
|
||||||
@@ -981,19 +983,9 @@ The `source_map_options` (optional) can contain the following properties:
|
|||||||
[compressor]: http://lisperator.net/uglifyjs/compress
|
[compressor]: http://lisperator.net/uglifyjs/compress
|
||||||
[parser]: http://lisperator.net/uglifyjs/parser
|
[parser]: http://lisperator.net/uglifyjs/parser
|
||||||
|
|
||||||
#### Harmony
|
#### Support for `const`
|
||||||
|
|
||||||
If you wish to use the experimental [harmony](https://github.com/mishoo/UglifyJS2/commits/harmony)
|
`const` in `uglify-js@2.x` has function scope and as such behaves much like
|
||||||
branch to minify ES2015+ (ES6+) code please use the following in your `package.json` file:
|
`var` - unlike `const` in ES2015 (ES6) which has block scope. It is recommended
|
||||||
|
to avoid using `const` for this reason as it will have undefined behavior when
|
||||||
```
|
run on an ES2015 compatible browser.
|
||||||
"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony"
|
|
||||||
```
|
|
||||||
|
|
||||||
or to directly install the experimental harmony version of uglify:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install --save-dev uglify-js@github:mishoo/UglifyJS2#harmony
|
|
||||||
```
|
|
||||||
|
|
||||||
See [#448](https://github.com/mishoo/UglifyJS2/issues/448) for additional details.
|
|
||||||
|
|||||||
19
lib/ast.js
19
lib/ast.js
@@ -214,12 +214,13 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
|||||||
clone: function(deep) {
|
clone: function(deep) {
|
||||||
var node = this._clone(deep);
|
var node = this._clone(deep);
|
||||||
if (deep) {
|
if (deep) {
|
||||||
var refs = node.label.references;
|
var label = node.label;
|
||||||
var label = this.label;
|
var def = this.label;
|
||||||
node.walk(new TreeWalker(function(node) {
|
node.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_LoopControl
|
if (node instanceof AST_LoopControl
|
||||||
&& node.label && node.label.thedef === label) {
|
&& node.label && node.label.thedef === def) {
|
||||||
refs.push(node);
|
node.label.thedef = label;
|
||||||
|
label.references.push(node);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -1035,16 +1036,16 @@ TreeWalker.prototype = {
|
|||||||
self = p;
|
self = p;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loopcontrol_target: function(label) {
|
loopcontrol_target: function(node) {
|
||||||
var stack = this.stack;
|
var stack = this.stack;
|
||||||
if (label) for (var i = stack.length; --i >= 0;) {
|
if (node.label) for (var i = stack.length; --i >= 0;) {
|
||||||
var x = stack[i];
|
var x = stack[i];
|
||||||
if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
|
if (x instanceof AST_LabeledStatement && x.label.name == node.label.name)
|
||||||
return x.body;
|
return x.body;
|
||||||
}
|
|
||||||
} else for (var i = stack.length; --i >= 0;) {
|
} else for (var i = stack.length; --i >= 0;) {
|
||||||
var x = stack[i];
|
var x = stack[i];
|
||||||
if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
|
if (x instanceof AST_IterationStatement
|
||||||
|
|| node instanceof AST_Break && x instanceof AST_Switch)
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
461
lib/compress.js
461
lib/compress.js
@@ -71,7 +71,7 @@ function Compressor(options, false_by_default) {
|
|||||||
negate_iife : !false_by_default,
|
negate_iife : !false_by_default,
|
||||||
passes : 1,
|
passes : 1,
|
||||||
properties : !false_by_default,
|
properties : !false_by_default,
|
||||||
pure_getters : false,
|
pure_getters : !false_by_default && "strict",
|
||||||
pure_funcs : null,
|
pure_funcs : null,
|
||||||
reduce_vars : !false_by_default,
|
reduce_vars : !false_by_default,
|
||||||
screw_ie8 : true,
|
screw_ie8 : true,
|
||||||
@@ -251,9 +251,7 @@ merge(Compressor.prototype, {
|
|||||||
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
|
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
|
||||||
var reduce_vars = rescan && compressor.option("reduce_vars");
|
var reduce_vars = rescan && compressor.option("reduce_vars");
|
||||||
var toplevel = compressor.option("toplevel");
|
var toplevel = compressor.option("toplevel");
|
||||||
var ie8 = !compressor.option("screw_ie8");
|
var safe_ids = Object.create(null);
|
||||||
var safe_ids = [];
|
|
||||||
push();
|
|
||||||
var suppressor = new TreeWalker(function(node) {
|
var suppressor = new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
var d = node.definition();
|
var d = node.definition();
|
||||||
@@ -262,10 +260,8 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
|
node._squeezed = false;
|
||||||
node._squeezed = false;
|
node._optimized = false;
|
||||||
node._optimized = false;
|
|
||||||
}
|
|
||||||
if (reduce_vars) {
|
if (reduce_vars) {
|
||||||
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
|
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
|
||||||
if (node instanceof AST_Scope) node.variables.each(reset_def);
|
if (node instanceof AST_Scope) node.variables.each(reset_def);
|
||||||
@@ -273,18 +269,35 @@ merge(Compressor.prototype, {
|
|||||||
var d = node.definition();
|
var d = node.definition();
|
||||||
d.references.push(node);
|
d.references.push(node);
|
||||||
if (d.fixed === undefined || !is_safe(d)
|
if (d.fixed === undefined || !is_safe(d)
|
||||||
|| is_modified(node, 0, d.fixed instanceof AST_Lambda)) {
|
|| is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
|
} else {
|
||||||
|
var parent = tw.parent();
|
||||||
|
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|
||||||
|
|| parent instanceof AST_Call && node !== parent.expression
|
||||||
|
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|
||||||
|
|| parent instanceof AST_VarDef && node === parent.value) {
|
||||||
|
d.escaped = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ie8 && node instanceof AST_SymbolCatch) {
|
if (node instanceof AST_SymbolCatch) {
|
||||||
node.definition().fixed = false;
|
node.definition().fixed = false;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_VarDef) {
|
if (node instanceof AST_VarDef) {
|
||||||
var d = node.name.definition();
|
var d = node.name.definition();
|
||||||
if (d.fixed == null) {
|
if (d.fixed == null) {
|
||||||
d.fixed = node.value;
|
if (node.value) {
|
||||||
mark_as_safe(d);
|
d.fixed = function() {
|
||||||
|
return node.value;
|
||||||
|
};
|
||||||
|
mark(d, false);
|
||||||
|
descend();
|
||||||
|
} else {
|
||||||
|
d.fixed = null;
|
||||||
|
}
|
||||||
|
mark(d, true);
|
||||||
|
return true;
|
||||||
} else if (node.value) {
|
} else if (node.value) {
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
}
|
}
|
||||||
@@ -295,11 +308,10 @@ merge(Compressor.prototype, {
|
|||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
} else {
|
} else {
|
||||||
d.fixed = node;
|
d.fixed = node;
|
||||||
mark_as_safe(d);
|
mark(d, true);
|
||||||
}
|
}
|
||||||
var save_ids = safe_ids;
|
var save_ids = safe_ids;
|
||||||
safe_ids = [];
|
safe_ids = Object.create(null);
|
||||||
push();
|
|
||||||
descend();
|
descend();
|
||||||
safe_ids = save_ids;
|
safe_ids = save_ids;
|
||||||
return true;
|
return true;
|
||||||
@@ -314,8 +326,14 @@ merge(Compressor.prototype, {
|
|||||||
// So existing transformation rules can work on them.
|
// So existing transformation rules can work on them.
|
||||||
node.argnames.forEach(function(arg, i) {
|
node.argnames.forEach(function(arg, i) {
|
||||||
var d = arg.definition();
|
var d = arg.definition();
|
||||||
d.fixed = iife.args[i] || make_node(AST_Undefined, iife);
|
if (!node.uses_arguments && d.fixed === undefined) {
|
||||||
mark_as_safe(d);
|
d.fixed = function() {
|
||||||
|
return iife.args[i] || make_node(AST_Undefined, iife);
|
||||||
|
};
|
||||||
|
mark(d, true);
|
||||||
|
} else {
|
||||||
|
d.fixed = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (node instanceof AST_If || node instanceof AST_DWLoop) {
|
if (node instanceof AST_If || node instanceof AST_DWLoop) {
|
||||||
@@ -353,7 +371,19 @@ merge(Compressor.prototype, {
|
|||||||
pop();
|
pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Catch || node instanceof AST_SwitchBranch) {
|
if (node instanceof AST_Try) {
|
||||||
|
push();
|
||||||
|
walk_body(node, tw);
|
||||||
|
pop();
|
||||||
|
if (node.bcatch) {
|
||||||
|
push();
|
||||||
|
node.bcatch.walk(tw);
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
if (node.bfinally) node.bfinally.walk(tw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_SwitchBranch) {
|
||||||
push();
|
push();
|
||||||
descend();
|
descend();
|
||||||
pop();
|
pop();
|
||||||
@@ -363,33 +393,34 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
|
|
||||||
function mark_as_safe(def) {
|
function mark(def, safe) {
|
||||||
safe_ids[safe_ids.length - 1][def.id] = true;
|
safe_ids[def.id] = safe;
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_safe(def) {
|
function is_safe(def) {
|
||||||
for (var i = safe_ids.length, id = def.id; --i >= 0;) {
|
if (safe_ids[def.id]) {
|
||||||
if (safe_ids[i][id]) {
|
if (def.fixed == null) {
|
||||||
if (def.fixed == null) {
|
var orig = def.orig[0];
|
||||||
var orig = def.orig[0];
|
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
|
||||||
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
|
def.fixed = make_node(AST_Undefined, orig);
|
||||||
def.fixed = make_node(AST_Undefined, orig);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function push() {
|
function push() {
|
||||||
safe_ids.push(Object.create(null));
|
safe_ids = Object.create(safe_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pop() {
|
function pop() {
|
||||||
safe_ids.pop();
|
safe_ids = Object.getPrototypeOf(safe_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset_def(def) {
|
function reset_def(def) {
|
||||||
if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) {
|
def.escaped = false;
|
||||||
|
if (def.scope.uses_eval) {
|
||||||
|
def.fixed = false;
|
||||||
|
} else if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) {
|
||||||
def.fixed = undefined;
|
def.fixed = undefined;
|
||||||
} else {
|
} else {
|
||||||
def.fixed = false;
|
def.fixed = false;
|
||||||
@@ -400,7 +431,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function is_modified(node, level, func) {
|
function is_modified(node, level, func) {
|
||||||
var parent = tw.parent(level);
|
var parent = tw.parent(level);
|
||||||
if (isLHS(node, parent)
|
if (is_lhs(node, parent)
|
||||||
|| !func && parent instanceof AST_Call && parent.expression === node) {
|
|| !func && parent instanceof AST_Call && parent.expression === node) {
|
||||||
return true;
|
return true;
|
||||||
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
||||||
@@ -409,6 +440,20 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_SymbolRef.DEFMETHOD("fixed_value", function() {
|
||||||
|
var fixed = this.definition().fixed;
|
||||||
|
if (!fixed || fixed instanceof AST_Node) return fixed;
|
||||||
|
return fixed();
|
||||||
|
});
|
||||||
|
|
||||||
|
function is_reference_const(ref) {
|
||||||
|
if (!(ref instanceof AST_SymbolRef)) return false;
|
||||||
|
var orig = ref.definition().orig;
|
||||||
|
for (var i = orig.length; --i >= 0;) {
|
||||||
|
if (orig[i] instanceof AST_SymbolConst) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function find_variable(compressor, name) {
|
function find_variable(compressor, name) {
|
||||||
var scope, i = 0;
|
var scope, i = 0;
|
||||||
while (scope = compressor.parent(i++)) {
|
while (scope = compressor.parent(i++)) {
|
||||||
@@ -469,15 +514,15 @@ merge(Compressor.prototype, {
|
|||||||
// func(something) because that changes the meaning of
|
// func(something) because that changes the meaning of
|
||||||
// the func (becomes lexical instead of global).
|
// the func (becomes lexical instead of global).
|
||||||
function maintain_this_binding(parent, orig, val) {
|
function maintain_this_binding(parent, orig, val) {
|
||||||
if (parent instanceof AST_Call && parent.expression === orig) {
|
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
||||||
if (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name === "eval") {
|
|| parent instanceof AST_Call && parent.expression === orig
|
||||||
return make_node(AST_Seq, orig, {
|
&& (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
|
||||||
car: make_node(AST_Number, orig, {
|
return make_node(AST_Seq, orig, {
|
||||||
value: 0
|
car: make_node(AST_Number, orig, {
|
||||||
}),
|
value: 0
|
||||||
cdr: val
|
}),
|
||||||
});
|
cdr: val
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@@ -687,7 +732,7 @@ merge(Compressor.prototype, {
|
|||||||
return statements;
|
return statements;
|
||||||
|
|
||||||
function is_lvalue(node, parent) {
|
function is_lvalue(node, parent) {
|
||||||
return node instanceof AST_SymbolRef && isLHS(node, parent);
|
return node instanceof AST_SymbolRef && is_lhs(node, parent);
|
||||||
}
|
}
|
||||||
function replace_var(node, parent, is_constant) {
|
function replace_var(node, parent, is_constant) {
|
||||||
if (is_lvalue(node, parent)) return node;
|
if (is_lvalue(node, parent)) return node;
|
||||||
@@ -893,7 +938,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ab = aborts(stat.body);
|
var ab = aborts(stat.body);
|
||||||
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
|
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
|
||||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||||
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||||
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||||
@@ -915,7 +960,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ab = aborts(stat.alternative);
|
var ab = aborts(stat.alternative);
|
||||||
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
|
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
|
||||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||||
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||||
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||||
@@ -964,7 +1009,7 @@ merge(Compressor.prototype, {
|
|||||||
extract_declarations_from_unreachable_code(compressor, stat, a);
|
extract_declarations_from_unreachable_code(compressor, stat, a);
|
||||||
} else {
|
} else {
|
||||||
if (stat instanceof AST_LoopControl) {
|
if (stat instanceof AST_LoopControl) {
|
||||||
var lct = compressor.loopcontrol_target(stat.label);
|
var lct = compressor.loopcontrol_target(stat);
|
||||||
if ((stat instanceof AST_Break
|
if ((stat instanceof AST_Break
|
||||||
&& !(lct instanceof AST_IterationStatement)
|
&& !(lct instanceof AST_IterationStatement)
|
||||||
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
||||||
@@ -1150,6 +1195,61 @@ merge(Compressor.prototype, {
|
|||||||
&& !node.expression.has_side_effects(compressor);
|
&& !node.expression.has_side_effects(compressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// may_eq_null()
|
||||||
|
// returns true if this node may evaluate to null or undefined
|
||||||
|
(function(def) {
|
||||||
|
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
|
||||||
|
var pure_getters = compressor.option("pure_getters");
|
||||||
|
return !pure_getters || this._eq_null(pure_getters);
|
||||||
|
});
|
||||||
|
|
||||||
|
function is_strict(pure_getters) {
|
||||||
|
return /strict/.test(pure_getters);
|
||||||
|
}
|
||||||
|
|
||||||
|
def(AST_Node, is_strict);
|
||||||
|
def(AST_Null, return_true);
|
||||||
|
def(AST_Undefined, return_true);
|
||||||
|
def(AST_Constant, return_false);
|
||||||
|
def(AST_Array, return_false);
|
||||||
|
def(AST_Object, return_false);
|
||||||
|
def(AST_Function, return_false);
|
||||||
|
def(AST_UnaryPostfix, return_false);
|
||||||
|
def(AST_UnaryPrefix, function() {
|
||||||
|
return this.operator == "void";
|
||||||
|
});
|
||||||
|
def(AST_Binary, function(pure_getters) {
|
||||||
|
switch (this.operator) {
|
||||||
|
case "&&":
|
||||||
|
return this.left._eq_null(pure_getters);
|
||||||
|
case "||":
|
||||||
|
return this.left._eq_null(pure_getters)
|
||||||
|
&& this.right._eq_null(pure_getters);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
def(AST_Assign, function(pure_getters) {
|
||||||
|
return this.operator == "="
|
||||||
|
&& this.right._eq_null(pure_getters);
|
||||||
|
})
|
||||||
|
def(AST_Conditional, function(pure_getters) {
|
||||||
|
return this.consequent._eq_null(pure_getters)
|
||||||
|
|| this.alternative._eq_null(pure_getters);
|
||||||
|
})
|
||||||
|
def(AST_Seq, function(pure_getters) {
|
||||||
|
return this.cdr._eq_null(pure_getters);
|
||||||
|
});
|
||||||
|
def(AST_SymbolRef, function(pure_getters) {
|
||||||
|
if (this.is_undefined) return true;
|
||||||
|
if (!is_strict(pure_getters)) return false;
|
||||||
|
var fixed = this.fixed_value();
|
||||||
|
return !fixed || fixed._eq_null(pure_getters);
|
||||||
|
});
|
||||||
|
})(function(node, func) {
|
||||||
|
node.DEFMETHOD("_eq_null", func);
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ boolean/negation helpers ]----- */
|
/* -----[ boolean/negation helpers ]----- */
|
||||||
|
|
||||||
// methods to determine whether an expression has a boolean result type
|
// methods to determine whether an expression has a boolean result type
|
||||||
@@ -1234,9 +1334,9 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
var unary_side_effects = makePredicate("delete ++ --");
|
var unary_side_effects = makePredicate("delete ++ --");
|
||||||
|
|
||||||
function isLHS(node, parent) {
|
function is_lhs(node, parent) {
|
||||||
return parent instanceof AST_Unary && unary_side_effects(parent.operator)
|
if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
|
||||||
|| parent instanceof AST_Assign && parent.left === node;
|
if (parent instanceof AST_Assign && parent.left === node) return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
(function (def){
|
(function (def){
|
||||||
@@ -1249,7 +1349,7 @@ merge(Compressor.prototype, {
|
|||||||
node = parent;
|
node = parent;
|
||||||
parent = compressor.parent(level++);
|
parent = compressor.parent(level++);
|
||||||
} while (parent instanceof AST_PropAccess && parent.expression === node);
|
} while (parent instanceof AST_PropAccess && parent.expression === node);
|
||||||
if (isLHS(node, parent)) {
|
if (is_lhs(node, parent)) {
|
||||||
compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
|
compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
|
||||||
} else {
|
} else {
|
||||||
return def;
|
return def;
|
||||||
@@ -1279,7 +1379,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
def(AST_Node, noop);
|
def(AST_Node, noop);
|
||||||
def(AST_Dot, function(compressor, suffix){
|
def(AST_Dot, function(compressor, suffix){
|
||||||
return this.expression._find_defs(compressor, suffix + "." + this.property);
|
return this.expression._find_defs(compressor, "." + this.property + suffix);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor, suffix){
|
def(AST_SymbolRef, function(compressor, suffix){
|
||||||
if (!this.global()) return;
|
if (!this.global()) return;
|
||||||
@@ -1484,23 +1584,20 @@ merge(Compressor.prototype, {
|
|||||||
: ev(this.alternative, compressor);
|
: ev(this.alternative, compressor);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor){
|
def(AST_SymbolRef, function(compressor){
|
||||||
if (this._evaluating) throw def;
|
if (!compressor.option("reduce_vars") || this._evaluating) throw def;
|
||||||
this._evaluating = true;
|
this._evaluating = true;
|
||||||
try {
|
try {
|
||||||
var d = this.definition();
|
var fixed = this.fixed_value();
|
||||||
if (compressor.option("reduce_vars") && d.fixed) {
|
if (!fixed) throw def;
|
||||||
if (compressor.option("unsafe")) {
|
var value = ev(fixed, compressor);
|
||||||
if (!HOP(d.fixed, "_evaluated")) {
|
if (!HOP(fixed, "_eval")) fixed._eval = function() {
|
||||||
d.fixed._evaluated = ev(d.fixed, compressor);
|
return value;
|
||||||
}
|
};
|
||||||
return d.fixed._evaluated;
|
if (value && typeof value == "object" && this.definition().escaped) throw def;
|
||||||
}
|
return value;
|
||||||
return ev(d.fixed, compressor);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
this._evaluating = false;
|
this._evaluating = false;
|
||||||
}
|
}
|
||||||
throw def;
|
|
||||||
});
|
});
|
||||||
def(AST_PropAccess, function(compressor){
|
def(AST_PropAccess, function(compressor){
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
@@ -1678,7 +1775,7 @@ merge(Compressor.prototype, {
|
|||||||
|| this.expression.has_side_effects(compressor);
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor){
|
def(AST_SymbolRef, function(compressor){
|
||||||
return this.global() && this.undeclared();
|
return this.undeclared();
|
||||||
});
|
});
|
||||||
def(AST_Object, function(compressor){
|
def(AST_Object, function(compressor){
|
||||||
return any(this.properties, compressor);
|
return any(this.properties, compressor);
|
||||||
@@ -1690,17 +1787,14 @@ merge(Compressor.prototype, {
|
|||||||
return any(this.elements, compressor);
|
return any(this.elements, compressor);
|
||||||
});
|
});
|
||||||
def(AST_Dot, function(compressor){
|
def(AST_Dot, function(compressor){
|
||||||
if (!compressor.option("pure_getters")) return true;
|
return this.expression.may_eq_null(compressor)
|
||||||
return this.expression.has_side_effects(compressor);
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Sub, function(compressor){
|
def(AST_Sub, function(compressor){
|
||||||
if (!compressor.option("pure_getters")) return true;
|
return this.expression.may_eq_null(compressor)
|
||||||
return this.expression.has_side_effects(compressor)
|
|| this.expression.has_side_effects(compressor)
|
||||||
|| this.property.has_side_effects(compressor);
|
|| this.property.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_PropAccess, function(compressor){
|
|
||||||
return !compressor.option("pure_getters");
|
|
||||||
});
|
|
||||||
def(AST_Seq, function(compressor){
|
def(AST_Seq, function(compressor){
|
||||||
return this.car.has_side_effects(compressor)
|
return this.car.has_side_effects(compressor)
|
||||||
|| this.cdr.has_side_effects(compressor);
|
|| this.cdr.has_side_effects(compressor);
|
||||||
@@ -1746,7 +1840,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_LabeledStatement, function(self, compressor){
|
OPT(AST_LabeledStatement, function(self, compressor){
|
||||||
if (self.body instanceof AST_Break
|
if (self.body instanceof AST_Break
|
||||||
&& compressor.loopcontrol_target(self.body.label) === self.body) {
|
&& compressor.loopcontrol_target(self.body) === self.body) {
|
||||||
return make_node(AST_EmptyStatement, self);
|
return make_node(AST_EmptyStatement, self);
|
||||||
}
|
}
|
||||||
return self.label.references.length == 0 ? self.body : self;
|
return self.label.references.length == 0 ? self.body : self;
|
||||||
@@ -1829,6 +1923,7 @@ merge(Compressor.prototype, {
|
|||||||
&& node instanceof AST_Assign
|
&& node instanceof AST_Assign
|
||||||
&& node.operator == "="
|
&& node.operator == "="
|
||||||
&& node.left instanceof AST_SymbolRef
|
&& node.left instanceof AST_SymbolRef
|
||||||
|
&& !is_reference_const(node.left)
|
||||||
&& scope === self) {
|
&& scope === self) {
|
||||||
node.right.walk(tw);
|
node.right.walk(tw);
|
||||||
return true;
|
return true;
|
||||||
@@ -1918,7 +2013,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
|
||||||
var def = node.definitions.filter(function(def){
|
var def = node.definitions.filter(function(def){
|
||||||
if (def.value) def.value = def.value.transform(tt);
|
if (def.value) def.value = def.value.transform(tt);
|
||||||
var sym = def.name.definition();
|
var sym = def.name.definition();
|
||||||
@@ -1996,26 +2091,32 @@ merge(Compressor.prototype, {
|
|||||||
return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
|
return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// certain combination of unused name + side effect leads to:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/44
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/1830
|
||||||
|
// that's an invalid AST.
|
||||||
|
// We fix it at this stage by moving the `var` outside the `for`.
|
||||||
if (node instanceof AST_For) {
|
if (node instanceof AST_For) {
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
|
|
||||||
if (node.init instanceof AST_BlockStatement) {
|
if (node.init instanceof AST_BlockStatement) {
|
||||||
// certain combination of unused name + side effect leads to:
|
var block = node.init;
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/44
|
node.init = block.body.pop();
|
||||||
// that's an invalid AST.
|
block.body.push(node);
|
||||||
// We fix it at this stage by moving the `var` outside the `for`.
|
return in_list ? MAP.splice(block.body) : block;
|
||||||
|
|
||||||
var body = node.init.body.slice(0, -1);
|
|
||||||
node.init = node.init.body.slice(-1)[0].body;
|
|
||||||
body.push(node);
|
|
||||||
|
|
||||||
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
} else if (is_empty(node.init)) {
|
} else if (is_empty(node.init)) {
|
||||||
node.init = null;
|
node.init = null;
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
|
||||||
|
descend(node, this);
|
||||||
|
if (node.body instanceof AST_BlockStatement) {
|
||||||
|
var block = node.body;
|
||||||
|
node.body = block.body.pop();
|
||||||
|
block.body.push(node);
|
||||||
|
return in_list ? MAP.splice(block.body) : block;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== self)
|
if (node instanceof AST_Scope && node !== self)
|
||||||
return node;
|
return node;
|
||||||
@@ -2264,11 +2365,11 @@ merge(Compressor.prototype, {
|
|||||||
return values && AST_Seq.from_array(values);
|
return values && AST_Seq.from_array(values);
|
||||||
});
|
});
|
||||||
def(AST_Dot, function(compressor, first_in_statement){
|
def(AST_Dot, function(compressor, first_in_statement){
|
||||||
if (!compressor.option("pure_getters")) return this;
|
if (this.expression.may_eq_null(compressor)) return this;
|
||||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
});
|
});
|
||||||
def(AST_Sub, function(compressor, first_in_statement){
|
def(AST_Sub, function(compressor, first_in_statement){
|
||||||
if (!compressor.option("pure_getters")) return this;
|
if (this.expression.may_eq_null(compressor)) return this;
|
||||||
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
|
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
|
||||||
var property = this.property.drop_side_effect_free(compressor);
|
var property = this.property.drop_side_effect_free(compressor);
|
||||||
@@ -2314,13 +2415,22 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_For, self, {
|
return make_node(AST_For, self, {
|
||||||
body: self.body
|
body: self.body
|
||||||
});
|
});
|
||||||
} else if (compressor.option("dead_code") && self instanceof AST_While) {
|
}
|
||||||
|
if (compressor.option("dead_code") && self instanceof AST_While) {
|
||||||
var a = [];
|
var a = [];
|
||||||
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
||||||
return make_node(AST_BlockStatement, self, { body: a });
|
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
|
||||||
} else {
|
}
|
||||||
cond = make_node_from_constant(cond, self.condition).transform(compressor);
|
if (self instanceof AST_Do) {
|
||||||
self.condition = best_of_expression(cond, self.condition);
|
var has_loop_control = false;
|
||||||
|
var tw = new TreeWalker(function(node) {
|
||||||
|
if (node instanceof AST_Scope || has_loop_control) return true;
|
||||||
|
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self)
|
||||||
|
return has_loop_control = true;
|
||||||
|
});
|
||||||
|
var parent = compressor.parent();
|
||||||
|
(parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
|
||||||
|
if (!has_loop_control) return self.body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self instanceof AST_While) {
|
if (self instanceof AST_While) {
|
||||||
@@ -2346,7 +2456,7 @@ merge(Compressor.prototype, {
|
|||||||
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
|
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
|
||||||
if (first instanceof AST_If) {
|
if (first instanceof AST_If) {
|
||||||
if (first.body instanceof AST_Break
|
if (first.body instanceof AST_Break
|
||||||
&& compressor.loopcontrol_target(first.body.label) === compressor.self()) {
|
&& compressor.loopcontrol_target(first.body) === compressor.self()) {
|
||||||
if (self.condition) {
|
if (self.condition) {
|
||||||
self.condition = make_node(AST_Binary, self.condition, {
|
self.condition = make_node(AST_Binary, self.condition, {
|
||||||
left: self.condition,
|
left: self.condition,
|
||||||
@@ -2359,7 +2469,7 @@ merge(Compressor.prototype, {
|
|||||||
drop_it(first.alternative);
|
drop_it(first.alternative);
|
||||||
}
|
}
|
||||||
else if (first.alternative instanceof AST_Break
|
else if (first.alternative instanceof AST_Break
|
||||||
&& compressor.loopcontrol_target(first.alternative.label) === compressor.self()) {
|
&& compressor.loopcontrol_target(first.alternative) === compressor.self()) {
|
||||||
if (self.condition) {
|
if (self.condition) {
|
||||||
self.condition = make_node(AST_Binary, self.condition, {
|
self.condition = make_node(AST_Binary, self.condition, {
|
||||||
left: self.condition,
|
left: self.condition,
|
||||||
@@ -2389,7 +2499,7 @@ merge(Compressor.prototype, {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
||||||
return make_node(AST_BlockStatement, self, { body: a });
|
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
|
||||||
}
|
}
|
||||||
if (cond !== self.condition) {
|
if (cond !== self.condition) {
|
||||||
cond = make_node_from_constant(cond, self.condition).transform(compressor);
|
cond = make_node_from_constant(cond, self.condition).transform(compressor);
|
||||||
@@ -2590,7 +2700,7 @@ merge(Compressor.prototype, {
|
|||||||
self.body = body;
|
self.body = body;
|
||||||
while (branch = body[body.length - 1]) {
|
while (branch = body[body.length - 1]) {
|
||||||
var stat = branch.body[branch.body.length - 1];
|
var stat = branch.body[branch.body.length - 1];
|
||||||
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self)
|
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
|
||||||
branch.body.pop();
|
branch.body.pop();
|
||||||
if (branch.body.length || branch instanceof AST_Case
|
if (branch.body.length || branch instanceof AST_Case
|
||||||
&& (default_branch || branch.expression.has_side_effects(compressor))) break;
|
&& (default_branch || branch.expression.has_side_effects(compressor))) break;
|
||||||
@@ -2609,7 +2719,7 @@ merge(Compressor.prototype, {
|
|||||||
if (has_break
|
if (has_break
|
||||||
|| node instanceof AST_Lambda
|
|| node instanceof AST_Lambda
|
||||||
|| node instanceof AST_SimpleStatement) return true;
|
|| node instanceof AST_SimpleStatement) return true;
|
||||||
if (node instanceof AST_Break && tw.loopcontrol_target(node.label) === self)
|
if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
|
||||||
has_break = true;
|
has_break = true;
|
||||||
});
|
});
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
@@ -2641,9 +2751,9 @@ merge(Compressor.prototype, {
|
|||||||
var body = [];
|
var body = [];
|
||||||
if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
|
if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
|
||||||
if (self.bfinally) body = body.concat(self.bfinally.body);
|
if (self.bfinally) body = body.concat(self.bfinally.body);
|
||||||
return body.length > 0 ? make_node(AST_BlockStatement, self, {
|
return make_node(AST_BlockStatement, self, {
|
||||||
body: body
|
body: body
|
||||||
}).optimize(compressor) : make_node(AST_EmptyStatement, self);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
@@ -2681,11 +2791,12 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("reduce_vars")
|
if (compressor.option("reduce_vars")
|
||||||
&& exp instanceof AST_SymbolRef) {
|
&& exp instanceof AST_SymbolRef) {
|
||||||
var def = exp.definition();
|
var def = exp.definition();
|
||||||
if (def.fixed instanceof AST_Defun) {
|
var fixed = exp.fixed_value();
|
||||||
def.fixed = make_node(AST_Function, def.fixed, def.fixed).clone(true);
|
if (fixed instanceof AST_Defun) {
|
||||||
|
def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
|
||||||
}
|
}
|
||||||
if (def.fixed instanceof AST_Function) {
|
if (fixed instanceof AST_Function) {
|
||||||
exp = def.fixed;
|
exp = fixed;
|
||||||
if (compressor.option("unused")
|
if (compressor.option("unused")
|
||||||
&& def.references.length == 1
|
&& def.references.length == 1
|
||||||
&& !(def.scope.uses_arguments
|
&& !(def.scope.uses_arguments
|
||||||
@@ -2970,7 +3081,8 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (left
|
if (left
|
||||||
&& !(left instanceof AST_SymbolRef
|
&& !(left instanceof AST_SymbolRef
|
||||||
&& left.definition().orig[0] instanceof AST_SymbolLambda)) {
|
&& (left.definition().orig[0] instanceof AST_SymbolLambda
|
||||||
|
|| is_reference_const(left)))) {
|
||||||
var parent, field;
|
var parent, field;
|
||||||
var cdr = self.cdr;
|
var cdr = self.cdr;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -3015,8 +3127,9 @@ merge(Compressor.prototype, {
|
|||||||
if (this.expression instanceof AST_Seq) {
|
if (this.expression instanceof AST_Seq) {
|
||||||
var seq = this.expression;
|
var seq = this.expression;
|
||||||
var x = seq.to_array();
|
var x = seq.to_array();
|
||||||
this.expression = x.pop();
|
var e = this.clone();
|
||||||
x.push(this);
|
e.expression = x.pop();
|
||||||
|
x.push(e);
|
||||||
seq = AST_Seq.from_array(x).transform(compressor);
|
seq = AST_Seq.from_array(x).transform(compressor);
|
||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
@@ -3029,11 +3142,27 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_UnaryPrefix, function(self, compressor){
|
OPT(AST_UnaryPrefix, function(self, compressor){
|
||||||
|
var e = self.expression;
|
||||||
|
if (self.operator == "delete"
|
||||||
|
&& !(e instanceof AST_SymbolRef
|
||||||
|
|| e instanceof AST_PropAccess
|
||||||
|
|| e instanceof AST_NaN
|
||||||
|
|| e instanceof AST_Infinity
|
||||||
|
|| e instanceof AST_Undefined)) {
|
||||||
|
if (e instanceof AST_Seq) {
|
||||||
|
e = e.to_array();
|
||||||
|
e.push(make_node(AST_True, self));
|
||||||
|
return AST_Seq.from_array(e).optimize(compressor);
|
||||||
|
}
|
||||||
|
return make_node(AST_Seq, self, {
|
||||||
|
car: e,
|
||||||
|
cdr: make_node(AST_True, self)
|
||||||
|
}).optimize(compressor);
|
||||||
|
}
|
||||||
var seq = self.lift_sequences(compressor);
|
var seq = self.lift_sequences(compressor);
|
||||||
if (seq !== self) {
|
if (seq !== self) {
|
||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
var e = self.expression;
|
|
||||||
if (compressor.option("side_effects") && self.operator == "void") {
|
if (compressor.option("side_effects") && self.operator == "void") {
|
||||||
e = e.drop_side_effect_free(compressor);
|
e = e.drop_side_effect_free(compressor);
|
||||||
if (e) {
|
if (e) {
|
||||||
@@ -3070,9 +3199,14 @@ merge(Compressor.prototype, {
|
|||||||
if (e instanceof AST_Binary
|
if (e instanceof AST_Binary
|
||||||
&& (self.operator == "+" || self.operator == "-")
|
&& (self.operator == "+" || self.operator == "-")
|
||||||
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
|
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
|
||||||
self.expression = e.left;
|
return make_node(AST_Binary, self, {
|
||||||
e.left = self;
|
operator: e.operator,
|
||||||
return e.optimize(compressor);
|
left: make_node(AST_UnaryPrefix, e.left, {
|
||||||
|
operator: self.operator,
|
||||||
|
expression: e.left
|
||||||
|
}),
|
||||||
|
right: e.right
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// avoids infinite recursion of numerals
|
// avoids infinite recursion of numerals
|
||||||
if (self.operator != "-"
|
if (self.operator != "-"
|
||||||
@@ -3091,23 +3225,25 @@ merge(Compressor.prototype, {
|
|||||||
if (this.left instanceof AST_Seq) {
|
if (this.left instanceof AST_Seq) {
|
||||||
var seq = this.left;
|
var seq = this.left;
|
||||||
var x = seq.to_array();
|
var x = seq.to_array();
|
||||||
this.left = x.pop();
|
var e = this.clone();
|
||||||
x.push(this);
|
e.left = x.pop();
|
||||||
|
x.push(e);
|
||||||
return AST_Seq.from_array(x).optimize(compressor);
|
return AST_Seq.from_array(x).optimize(compressor);
|
||||||
}
|
}
|
||||||
if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
|
if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
|
||||||
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
|
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
|
||||||
var root = this.right;
|
var root = this.right.clone();
|
||||||
var cursor, seq = root;
|
var cursor, seq = root;
|
||||||
while (assign || !seq.car.has_side_effects(compressor)) {
|
while (assign || !seq.car.has_side_effects(compressor)) {
|
||||||
cursor = seq;
|
cursor = seq;
|
||||||
if (seq.cdr instanceof AST_Seq) {
|
if (seq.cdr instanceof AST_Seq) {
|
||||||
seq = seq.cdr;
|
seq = seq.cdr = seq.cdr.clone();
|
||||||
} else break;
|
} else break;
|
||||||
}
|
}
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
this.right = cursor.cdr;
|
var e = this.clone();
|
||||||
cursor.cdr = this;
|
e.right = cursor.cdr;
|
||||||
|
cursor.cdr = e;
|
||||||
return root.optimize(compressor);
|
return root.optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3485,12 +3621,11 @@ merge(Compressor.prototype, {
|
|||||||
OPT(AST_SymbolRef, function(self, compressor){
|
OPT(AST_SymbolRef, function(self, compressor){
|
||||||
var def = self.resolve_defines(compressor);
|
var def = self.resolve_defines(compressor);
|
||||||
if (def) {
|
if (def) {
|
||||||
return def;
|
return def.optimize(compressor);
|
||||||
}
|
}
|
||||||
// testing against !self.scope.uses_with first is an optimization
|
// testing against !self.scope.uses_with first is an optimization
|
||||||
if (compressor.option("screw_ie8")
|
if (compressor.option("screw_ie8")
|
||||||
&& self.undeclared()
|
&& self.undeclared()
|
||||||
&& !isLHS(self, compressor.parent())
|
|
||||||
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
|
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
|
||||||
switch (self.name) {
|
switch (self.name) {
|
||||||
case "undefined":
|
case "undefined":
|
||||||
@@ -3501,31 +3636,61 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Infinity, self).optimize(compressor);
|
return make_node(AST_Infinity, self).optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
if (compressor.option("evaluate")
|
||||||
|
&& compressor.option("reduce_vars")
|
||||||
|
&& is_lhs(self, compressor.parent()) !== self) {
|
||||||
var d = self.definition();
|
var d = self.definition();
|
||||||
if (d.fixed) {
|
var fixed = self.fixed_value();
|
||||||
|
if (fixed) {
|
||||||
if (d.should_replace === undefined) {
|
if (d.should_replace === undefined) {
|
||||||
var init = d.fixed.evaluate(compressor);
|
var init = fixed.evaluate(compressor);
|
||||||
if (init !== d.fixed) {
|
if (init !== fixed) {
|
||||||
init = make_node_from_constant(init, d.fixed).optimize(compressor);
|
init = make_node_from_constant(init, fixed);
|
||||||
init = best_of_expression(init, d.fixed);
|
var value = init.optimize(compressor).print_to_string().length;
|
||||||
var value = init.print_to_string().length;
|
var fn;
|
||||||
|
if (has_symbol_ref(fixed)) {
|
||||||
|
fn = function() {
|
||||||
|
var result = init.optimize(compressor);
|
||||||
|
return result === init ? result.clone(true) : result;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
value = Math.min(value, fixed.print_to_string().length);
|
||||||
|
fn = function() {
|
||||||
|
var result = best_of_expression(init.optimize(compressor), fixed);
|
||||||
|
return result === init || result === fixed ? result.clone(true) : result;
|
||||||
|
};
|
||||||
|
}
|
||||||
var name = d.name.length;
|
var name = d.name.length;
|
||||||
var freq = d.references.length;
|
var overhead = 0;
|
||||||
var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
|
if (compressor.option("unused") && (!d.global || compressor.option("toplevel"))) {
|
||||||
d.should_replace = value <= name + overhead ? init : false;
|
overhead = (name + 2 + value) / d.references.length;
|
||||||
|
}
|
||||||
|
d.should_replace = value <= name + overhead ? fn : false;
|
||||||
} else {
|
} else {
|
||||||
d.should_replace = false;
|
d.should_replace = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (d.should_replace) {
|
if (d.should_replace) {
|
||||||
return d.should_replace.clone(true);
|
return d.should_replace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
|
function has_symbol_ref(value) {
|
||||||
|
var found;
|
||||||
|
value.walk(new TreeWalker(function(node) {
|
||||||
|
if (node instanceof AST_SymbolRef) found = true;
|
||||||
|
if (found) return true;
|
||||||
|
}));
|
||||||
|
return found;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function is_atomic(lhs, self) {
|
||||||
|
return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
OPT(AST_Undefined, function(self, compressor){
|
OPT(AST_Undefined, function(self, compressor){
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var undef = find_variable(compressor, "undefined");
|
var undef = find_variable(compressor, "undefined");
|
||||||
@@ -3539,6 +3704,8 @@ merge(Compressor.prototype, {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var lhs = is_lhs(compressor.self(), compressor.parent());
|
||||||
|
if (lhs && is_atomic(lhs, self)) return self;
|
||||||
return make_node(AST_UnaryPrefix, self, {
|
return make_node(AST_UnaryPrefix, self, {
|
||||||
operator: "void",
|
operator: "void",
|
||||||
expression: make_node(AST_Number, self, {
|
expression: make_node(AST_Number, self, {
|
||||||
@@ -3548,8 +3715,13 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Infinity, function(self, compressor){
|
OPT(AST_Infinity, function(self, compressor){
|
||||||
var retain = compressor.option("keep_infinity") && !find_variable(compressor, "Infinity");
|
var lhs = is_lhs(compressor.self(), compressor.parent());
|
||||||
return retain ? self : make_node(AST_Binary, self, {
|
if (lhs && is_atomic(lhs, self)) return self;
|
||||||
|
if (compressor.option("keep_infinity")
|
||||||
|
&& !(lhs && !is_atomic(lhs, self))
|
||||||
|
&& !find_variable(compressor, "Infinity"))
|
||||||
|
return self;
|
||||||
|
return make_node(AST_Binary, self, {
|
||||||
operator: "/",
|
operator: "/",
|
||||||
left: make_node(AST_Number, self, {
|
left: make_node(AST_Number, self, {
|
||||||
value: 1
|
value: 1
|
||||||
@@ -3561,15 +3733,20 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_NaN, function(self, compressor){
|
OPT(AST_NaN, function(self, compressor){
|
||||||
return find_variable(compressor, "NaN") ? make_node(AST_Binary, self, {
|
var lhs = is_lhs(compressor.self(), compressor.parent());
|
||||||
operator: "/",
|
if (lhs && !is_atomic(lhs, self)
|
||||||
left: make_node(AST_Number, self, {
|
|| find_variable(compressor, "NaN")) {
|
||||||
value: 0
|
return make_node(AST_Binary, self, {
|
||||||
}),
|
operator: "/",
|
||||||
right: make_node(AST_Number, self, {
|
left: make_node(AST_Number, self, {
|
||||||
value: 0
|
value: 0
|
||||||
})
|
}),
|
||||||
}) : self;
|
right: make_node(AST_Number, self, {
|
||||||
|
value: 0
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
|
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
|
||||||
@@ -3815,7 +3992,7 @@ merge(Compressor.prototype, {
|
|||||||
OPT(AST_Dot, function(self, compressor){
|
OPT(AST_Dot, function(self, compressor){
|
||||||
var def = self.resolve_defines(compressor);
|
var def = self.resolve_defines(compressor);
|
||||||
if (def) {
|
if (def) {
|
||||||
return def;
|
return def.optimize(compressor);
|
||||||
}
|
}
|
||||||
var prop = self.property;
|
var prop = self.property;
|
||||||
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
|
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function OutputStream(options) {
|
|||||||
comments : false,
|
comments : false,
|
||||||
indent_level : 4,
|
indent_level : 4,
|
||||||
indent_start : 0,
|
indent_start : 0,
|
||||||
inline_script : false,
|
inline_script : true,
|
||||||
keep_quoted_props: false,
|
keep_quoted_props: false,
|
||||||
max_line_len : false,
|
max_line_len : false,
|
||||||
preamble : null,
|
preamble : null,
|
||||||
@@ -190,11 +190,7 @@ function OutputStream(options) {
|
|||||||
var might_need_space = false;
|
var might_need_space = false;
|
||||||
var might_need_semicolon = false;
|
var might_need_semicolon = false;
|
||||||
var might_add_newline = 0;
|
var might_add_newline = 0;
|
||||||
var last = null;
|
var last = "";
|
||||||
|
|
||||||
function last_char() {
|
|
||||||
return last.charAt(last.length - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
var ensure_line_len = options.max_line_len ? function() {
|
var ensure_line_len = options.max_line_len ? function() {
|
||||||
if (current_col > options.max_line_len) {
|
if (current_col > options.max_line_len) {
|
||||||
@@ -218,10 +214,11 @@ function OutputStream(options) {
|
|||||||
function print(str) {
|
function print(str) {
|
||||||
str = String(str);
|
str = String(str);
|
||||||
var ch = str.charAt(0);
|
var ch = str.charAt(0);
|
||||||
|
var prev = last.charAt(last.length - 1);
|
||||||
if (might_need_semicolon) {
|
if (might_need_semicolon) {
|
||||||
might_need_semicolon = false;
|
might_need_semicolon = false;
|
||||||
|
|
||||||
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
|
if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
|
||||||
if (options.semicolons || requireSemicolonChars(ch)) {
|
if (options.semicolons || requireSemicolonChars(ch)) {
|
||||||
OUTPUT += ";";
|
OUTPUT += ";";
|
||||||
current_col++;
|
current_col++;
|
||||||
@@ -258,7 +255,6 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (might_need_space) {
|
if (might_need_space) {
|
||||||
var prev = last_char();
|
|
||||||
if ((is_identifier_char(prev)
|
if ((is_identifier_char(prev)
|
||||||
&& (is_identifier_char(ch) || ch == "\\"))
|
&& (is_identifier_char(ch) || ch == "\\"))
|
||||||
|| (ch == "/" && ch == prev)
|
|| (ch == "/" && ch == prev)
|
||||||
|
|||||||
@@ -44,7 +44,15 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function find_builtins() {
|
function find_builtins() {
|
||||||
var a = [];
|
// NaN will be included due to Number.NaN
|
||||||
|
var a = [
|
||||||
|
"null",
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"Infinity",
|
||||||
|
"-Infinity",
|
||||||
|
"undefined",
|
||||||
|
];
|
||||||
[ Object, Array, Function, Number,
|
[ Object, Array, Function, Number,
|
||||||
String, Boolean, Error, Math,
|
String, Boolean, Error, Math,
|
||||||
Date, RegExp
|
Date, RegExp
|
||||||
@@ -149,13 +157,12 @@ function mangle_properties(ast, options) {
|
|||||||
// only function declarations after this line
|
// only function declarations after this line
|
||||||
|
|
||||||
function can_mangle(name) {
|
function can_mangle(name) {
|
||||||
if (!is_identifier(name)) return false;
|
|
||||||
if (unmangleable.indexOf(name) >= 0) return false;
|
if (unmangleable.indexOf(name) >= 0) return false;
|
||||||
if (reserved.indexOf(name) >= 0) return false;
|
if (reserved.indexOf(name) >= 0) return false;
|
||||||
if (options.only_cache) {
|
if (options.only_cache) {
|
||||||
return cache.props.has(name);
|
return cache.props.has(name);
|
||||||
}
|
}
|
||||||
if (/^[0-9.]+$/.test(name)) return false;
|
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "2.8.21",
|
"version": "2.8.24",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ if (!args.length) {
|
|||||||
}
|
}
|
||||||
args.push("--stats");
|
args.push("--stats");
|
||||||
var urls = [
|
var urls = [
|
||||||
"https://code.jquery.com/jquery-3.1.1.js",
|
"https://code.jquery.com/jquery-3.2.1.js",
|
||||||
"https://code.angularjs.org/1.6.1/angular.js",
|
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
|
||||||
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js",
|
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js",
|
||||||
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.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",
|
||||||
|
|||||||
@@ -1573,6 +1573,7 @@ var_side_effects_3: {
|
|||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
pure_getters: true,
|
pure_getters: true,
|
||||||
|
unsafe: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var print = console.log.bind(console);
|
var print = console.log.bind(console);
|
||||||
@@ -1591,3 +1592,49 @@ var_side_effects_3: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reassign_const_1: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
a = 2;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
a = 2;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
reassign_const_2: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
++a;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
++a;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
@@ -962,3 +962,56 @@ condition_symbol_matches_consequent: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "3 7 true 4"
|
expect_stdout: "3 7 true 4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete_conditional_1: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (1 ? undefined : x));
|
||||||
|
console.log(delete (1 ? void 0 : x));
|
||||||
|
console.log(delete (1 ? Infinity : x));
|
||||||
|
console.log(delete (1 ? 1 / 0 : x));
|
||||||
|
console.log(delete (1 ? NaN : x));
|
||||||
|
console.log(delete (1 ? 0 / 0 : x));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_conditional_2: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
keep_infinity: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (0 ? x : undefined));
|
||||||
|
console.log(delete (0 ? x : void 0));
|
||||||
|
console.log(delete (0 ? x : Infinity));
|
||||||
|
console.log(delete (0 ? x : 1 / 0));
|
||||||
|
console.log(delete (0 ? x : NaN));
|
||||||
|
console.log(delete (0 ? x : 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((Infinity, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
@@ -974,3 +974,118 @@ issue_1715_4: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "1"
|
expect_stdout: "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete_assign_1: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
console.log(delete (a = undefined));
|
||||||
|
console.log(delete (a = void 0));
|
||||||
|
console.log(delete (a = Infinity));
|
||||||
|
console.log(delete (a = 1 / 0));
|
||||||
|
console.log(delete (a = NaN));
|
||||||
|
console.log(delete (a = 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_assign_2: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
keep_infinity: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
console.log(delete (a = undefined));
|
||||||
|
console.log(delete (a = void 0));
|
||||||
|
console.log(delete (a = Infinity));
|
||||||
|
console.log(delete (a = 1 / 0));
|
||||||
|
console.log(delete (a = NaN));
|
||||||
|
console.log(delete (a = 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((Infinity, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1830_1: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
L: for (var b = console.log(1); !1;) continue L;
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
L: for (console.log(1); !1;) continue L;
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1830_2: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
L: for (var a = 1, b = console.log(a); --a;) continue L;
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
var a = 1;
|
||||||
|
L: for (console.log(a); --a;) continue L;
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
reassign_const: {
|
||||||
|
options = {
|
||||||
|
cascade: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
a = 2;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
return a = 2, a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
@@ -857,3 +857,135 @@ issue_1760_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "Infinity"
|
expect_stdout: "Infinity"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete_expr_1: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete undefined);
|
||||||
|
console.log(delete void 0);
|
||||||
|
console.log(delete Infinity);
|
||||||
|
console.log(delete (1 / 0));
|
||||||
|
console.log(delete NaN);
|
||||||
|
console.log(delete (0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(delete undefined);
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log(delete Infinity);
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log(delete NaN);
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_expr_2: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
keep_infinity: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete undefined);
|
||||||
|
console.log(delete void 0);
|
||||||
|
console.log(delete Infinity);
|
||||||
|
console.log(delete (1 / 0));
|
||||||
|
console.log(delete NaN);
|
||||||
|
console.log(delete (0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(delete undefined);
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log(delete Infinity);
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log(delete NaN);
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_binary_1: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (true && undefined));
|
||||||
|
console.log(delete (true && void 0));
|
||||||
|
console.log(delete (true && Infinity));
|
||||||
|
console.log(delete (true && (1 / 0)));
|
||||||
|
console.log(delete (true && NaN));
|
||||||
|
console.log(delete (true && (0 / 0)));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_binary_2: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
keep_infinity: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (false || undefined));
|
||||||
|
console.log(delete (false || void 0));
|
||||||
|
console.log(delete (false || Infinity));
|
||||||
|
console.log(delete (false || (1 / 0)));
|
||||||
|
console.log(delete (false || NaN));
|
||||||
|
console.log(delete (false || (0 / 0)));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((Infinity, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Infinity_NaN_undefined_LHS: {
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
Infinity = Infinity;
|
||||||
|
++Infinity;
|
||||||
|
Infinity--;
|
||||||
|
NaN *= NaN;
|
||||||
|
++NaN;
|
||||||
|
NaN--;
|
||||||
|
undefined |= undefined;
|
||||||
|
++undefined;
|
||||||
|
undefined--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: [
|
||||||
|
"function f() {",
|
||||||
|
" Infinity = 1 / 0;",
|
||||||
|
" ++Infinity;",
|
||||||
|
" Infinity--;",
|
||||||
|
" NaN *= NaN;",
|
||||||
|
" ++NaN;",
|
||||||
|
" NaN--;",
|
||||||
|
" undefined |= void 0;",
|
||||||
|
" ++undefined;",
|
||||||
|
" undefined--;",
|
||||||
|
"}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -93,3 +93,57 @@ issue_485_crashing_1530: {
|
|||||||
this, void 0;
|
this, void 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1841_1: {
|
||||||
|
options = {
|
||||||
|
keep_fargs: false,
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var b = 10;
|
||||||
|
!function(arg) {
|
||||||
|
for (var key in "hi")
|
||||||
|
var n = arg.baz, n = [ b = 42 ];
|
||||||
|
}(--b);
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var b = 10;
|
||||||
|
!function() {
|
||||||
|
for (var key in "hi") {
|
||||||
|
b = 42;
|
||||||
|
}
|
||||||
|
}(--b);
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
expect_exact: "42"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1841_2: {
|
||||||
|
options = {
|
||||||
|
keep_fargs: false,
|
||||||
|
pure_getters: false,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var b = 10;
|
||||||
|
!function(arg) {
|
||||||
|
for (var key in "hi")
|
||||||
|
var n = arg.baz, n = [ b = 42 ];
|
||||||
|
}(--b);
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var b = 10;
|
||||||
|
!function(arg) {
|
||||||
|
for (var key in "hi") {
|
||||||
|
arg.baz, b = 42;
|
||||||
|
}
|
||||||
|
}(--b);
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
expect_exact: "42"
|
||||||
|
}
|
||||||
|
|||||||
@@ -145,3 +145,18 @@ mixed: {
|
|||||||
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
|
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1801: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
global_defs: {
|
||||||
|
"CONFIG.FOO.BAR": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(CONFIG.FOO.BAR);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(!0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,11 +45,10 @@ chained_evaluation_2: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
(function() {
|
(function() {
|
||||||
var a = "long piece of string";
|
|
||||||
(function() {
|
(function() {
|
||||||
var c;
|
var c, b = "long piece of string";
|
||||||
c = f(a);
|
c = f(b);
|
||||||
c.bar = a;
|
c.bar = b;
|
||||||
})();
|
})();
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ f7: {
|
|||||||
console.log(a, b);
|
console.log(a, b);
|
||||||
}
|
}
|
||||||
expect_exact: [
|
expect_exact: [
|
||||||
"var a = 100, b = 10;",
|
"var b = 10;",
|
||||||
"",
|
"",
|
||||||
"!function() {",
|
"!function() {",
|
||||||
" for (;b = a, !1; ) ;",
|
" for (;b = 100, !1; ) ;",
|
||||||
"}(), console.log(a, b);",
|
"}(), console.log(100, b);",
|
||||||
]
|
]
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|||||||
239
test/compress/issue-1770.js
Normal file
239
test/compress/issue-1770.js
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
mangle_props: {
|
||||||
|
mangle_props = {}
|
||||||
|
input: {
|
||||||
|
var obj = {
|
||||||
|
undefined: 1,
|
||||||
|
NaN: 2,
|
||||||
|
Infinity: 3,
|
||||||
|
"-Infinity": 4,
|
||||||
|
null: 5,
|
||||||
|
};
|
||||||
|
console.log(
|
||||||
|
obj[void 0],
|
||||||
|
obj[undefined],
|
||||||
|
obj["undefined"],
|
||||||
|
obj[0/0],
|
||||||
|
obj[NaN],
|
||||||
|
obj["NaN"],
|
||||||
|
obj[1/0],
|
||||||
|
obj[Infinity],
|
||||||
|
obj["Infinity"],
|
||||||
|
obj[-1/0],
|
||||||
|
obj[-Infinity],
|
||||||
|
obj["-Infinity"],
|
||||||
|
obj[null],
|
||||||
|
obj["null"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var obj = {
|
||||||
|
undefined: 1,
|
||||||
|
NaN: 2,
|
||||||
|
Infinity: 3,
|
||||||
|
"-Infinity": 4,
|
||||||
|
null: 5,
|
||||||
|
};
|
||||||
|
console.log(
|
||||||
|
obj[void 0],
|
||||||
|
obj[void 0],
|
||||||
|
obj["undefined"],
|
||||||
|
obj[0/0],
|
||||||
|
obj[NaN],
|
||||||
|
obj["NaN"],
|
||||||
|
obj[1/0],
|
||||||
|
obj[1/0],
|
||||||
|
obj["Infinity"],
|
||||||
|
obj[-1/0],
|
||||||
|
obj[-1/0],
|
||||||
|
obj["-Infinity"],
|
||||||
|
obj[null],
|
||||||
|
obj["null"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_stdout: "1 1 1 2 2 2 3 3 3 4 4 4 5 5"
|
||||||
|
}
|
||||||
|
|
||||||
|
numeric_literal: {
|
||||||
|
beautify = {
|
||||||
|
beautify: true,
|
||||||
|
}
|
||||||
|
mangle_props = {}
|
||||||
|
input: {
|
||||||
|
var obj = {
|
||||||
|
0: 0,
|
||||||
|
"-0": 1,
|
||||||
|
42: 2,
|
||||||
|
"42": 3,
|
||||||
|
0x25: 4,
|
||||||
|
"0x25": 5,
|
||||||
|
1E42: 6,
|
||||||
|
"1E42": 7,
|
||||||
|
"1e+42": 8,
|
||||||
|
};
|
||||||
|
console.log(obj[-0], obj[-""], obj["-0"]);
|
||||||
|
console.log(obj[42], obj["42"]);
|
||||||
|
console.log(obj[0x25], obj["0x25"], obj[37], obj["37"]);
|
||||||
|
console.log(obj[1E42], obj["1E42"], obj["1e+42"]);
|
||||||
|
}
|
||||||
|
expect_exact: [
|
||||||
|
'var obj = {',
|
||||||
|
' 0: 0,',
|
||||||
|
' "-0": 1,',
|
||||||
|
' 42: 2,',
|
||||||
|
' "42": 3,',
|
||||||
|
' 37: 4,',
|
||||||
|
' a: 5,',
|
||||||
|
' 1e42: 6,',
|
||||||
|
' b: 7,',
|
||||||
|
' "1e+42": 8',
|
||||||
|
'};',
|
||||||
|
'',
|
||||||
|
'console.log(obj[-0], obj[-""], obj["-0"]);',
|
||||||
|
'',
|
||||||
|
'console.log(obj[42], obj["42"]);',
|
||||||
|
'',
|
||||||
|
'console.log(obj[37], obj["a"], obj[37], obj["37"]);',
|
||||||
|
'',
|
||||||
|
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
|
||||||
|
]
|
||||||
|
expect_stdout: [
|
||||||
|
"0 0 1",
|
||||||
|
"3 3",
|
||||||
|
"4 5 4 4",
|
||||||
|
"8 7 8",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
identifier: {
|
||||||
|
mangle_props = {}
|
||||||
|
input: {
|
||||||
|
var obj = {
|
||||||
|
abstract: 1,
|
||||||
|
boolean: 2,
|
||||||
|
byte: 3,
|
||||||
|
char: 4,
|
||||||
|
class: 5,
|
||||||
|
double: 6,
|
||||||
|
enum: 7,
|
||||||
|
export: 8,
|
||||||
|
extends: 9,
|
||||||
|
final: 10,
|
||||||
|
float: 11,
|
||||||
|
goto: 12,
|
||||||
|
implements: 13,
|
||||||
|
import: 14,
|
||||||
|
int: 15,
|
||||||
|
interface: 16,
|
||||||
|
let: 17,
|
||||||
|
long: 18,
|
||||||
|
native: 19,
|
||||||
|
package: 20,
|
||||||
|
private: 21,
|
||||||
|
protected: 22,
|
||||||
|
public: 23,
|
||||||
|
short: 24,
|
||||||
|
static: 25,
|
||||||
|
super: 26,
|
||||||
|
synchronized: 27,
|
||||||
|
this: 28,
|
||||||
|
throws: 29,
|
||||||
|
transient: 30,
|
||||||
|
volatile: 31,
|
||||||
|
yield: 32,
|
||||||
|
false: 33,
|
||||||
|
null: 34,
|
||||||
|
true: 35,
|
||||||
|
break: 36,
|
||||||
|
case: 37,
|
||||||
|
catch: 38,
|
||||||
|
const: 39,
|
||||||
|
continue: 40,
|
||||||
|
debugger: 41,
|
||||||
|
default: 42,
|
||||||
|
delete: 43,
|
||||||
|
do: 44,
|
||||||
|
else: 45,
|
||||||
|
finally: 46,
|
||||||
|
for: 47,
|
||||||
|
function: 48,
|
||||||
|
if: 49,
|
||||||
|
in: 50,
|
||||||
|
instanceof: 51,
|
||||||
|
new: 52,
|
||||||
|
return: 53,
|
||||||
|
switch: 54,
|
||||||
|
throw: 55,
|
||||||
|
try: 56,
|
||||||
|
typeof: 57,
|
||||||
|
var: 58,
|
||||||
|
void: 59,
|
||||||
|
while: 60,
|
||||||
|
with: 61,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var obj = {
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
c: 3,
|
||||||
|
d: 4,
|
||||||
|
e: 5,
|
||||||
|
f: 6,
|
||||||
|
g: 7,
|
||||||
|
h: 8,
|
||||||
|
i: 9,
|
||||||
|
j: 10,
|
||||||
|
k: 11,
|
||||||
|
l: 12,
|
||||||
|
m: 13,
|
||||||
|
n: 14,
|
||||||
|
o: 15,
|
||||||
|
p: 16,
|
||||||
|
q: 17,
|
||||||
|
r: 18,
|
||||||
|
s: 19,
|
||||||
|
t: 20,
|
||||||
|
u: 21,
|
||||||
|
v: 22,
|
||||||
|
w: 23,
|
||||||
|
x: 24,
|
||||||
|
y: 25,
|
||||||
|
z: 26,
|
||||||
|
A: 27,
|
||||||
|
B: 28,
|
||||||
|
C: 29,
|
||||||
|
D: 30,
|
||||||
|
F: 31,
|
||||||
|
G: 32,
|
||||||
|
false: 33,
|
||||||
|
null: 34,
|
||||||
|
true: 35,
|
||||||
|
H: 36,
|
||||||
|
I: 37,
|
||||||
|
J: 38,
|
||||||
|
K: 39,
|
||||||
|
L: 40,
|
||||||
|
M: 41,
|
||||||
|
N: 42,
|
||||||
|
O: 43,
|
||||||
|
P: 44,
|
||||||
|
Q: 45,
|
||||||
|
R: 46,
|
||||||
|
S: 47,
|
||||||
|
T: 48,
|
||||||
|
U: 49,
|
||||||
|
V: 50,
|
||||||
|
W: 51,
|
||||||
|
X: 52,
|
||||||
|
Y: 53,
|
||||||
|
Z: 54,
|
||||||
|
$: 55,
|
||||||
|
_: 56,
|
||||||
|
aa: 57,
|
||||||
|
ba: 58,
|
||||||
|
ca: 59,
|
||||||
|
da: 60,
|
||||||
|
ea: 61,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
19
test/compress/issue-1787.js
Normal file
19
test/compress/issue-1787.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
unary_prefix: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var x = -(2 / 3);
|
||||||
|
return x;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
return -2 / 3;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
134
test/compress/issue-1833.js
Normal file
134
test/compress/issue-1833.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
iife_for: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
function g() {
|
||||||
|
L: for (;;) break L;
|
||||||
|
}
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
!function() {
|
||||||
|
L: for (;;) break L;
|
||||||
|
}();
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iife_for_in: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
function g() {
|
||||||
|
L: for (var a in x) break L;
|
||||||
|
}
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
!function() {
|
||||||
|
L: for (var a in x) break L;
|
||||||
|
}();
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iife_do: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
function g() {
|
||||||
|
L: do {
|
||||||
|
break L;
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
!function() {
|
||||||
|
L: do {
|
||||||
|
break L;
|
||||||
|
} while (1);
|
||||||
|
}();
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iife_while: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
function g() {
|
||||||
|
L: while (1) break L;
|
||||||
|
}
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
!function() {
|
||||||
|
L: while (1) break L;
|
||||||
|
}();
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label_do: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
L: do {
|
||||||
|
continue L;
|
||||||
|
} while (0);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
L: do {
|
||||||
|
continue L;
|
||||||
|
} while (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label_while: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
dead_code: true,
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
L: while (0) continue L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function f(){L:;}"
|
||||||
|
}
|
||||||
@@ -215,8 +215,7 @@ evaluate: {
|
|||||||
a();
|
a();
|
||||||
for(;;)
|
for(;;)
|
||||||
c();
|
c();
|
||||||
// rule disabled due to issue_1532
|
d();
|
||||||
do d(); while (false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,3 +457,26 @@ issue_1648: {
|
|||||||
}
|
}
|
||||||
expect_exact: "function f(){for(x();1;);}"
|
expect_exact: "function f(){for(x();1;);}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do_switch: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
do {
|
||||||
|
switch (a) {
|
||||||
|
case b:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
do {
|
||||||
|
switch (a) {
|
||||||
|
case b:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
121
test/compress/pure_getters.js
Normal file
121
test/compress/pure_getters.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
strict: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: false,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
a.prop;
|
||||||
|
b.prop;
|
||||||
|
c.prop;
|
||||||
|
d.prop;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
undefined.prop;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
a.prop;
|
||||||
|
b.prop;
|
||||||
|
c.prop;
|
||||||
|
d.prop;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
(void 0).prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strict_reduce_vars: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
a.prop;
|
||||||
|
b.prop;
|
||||||
|
c.prop;
|
||||||
|
d.prop;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
undefined.prop;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
a.prop;
|
||||||
|
b.prop;
|
||||||
|
d.prop;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
(void 0).prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe: {
|
||||||
|
options = {
|
||||||
|
pure_getters: true,
|
||||||
|
reduce_vars: false,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
a.prop;
|
||||||
|
b.prop;
|
||||||
|
c.prop;
|
||||||
|
d.prop;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
undefined.prop;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
d;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
(void 0).prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe_reduce_vars: {
|
||||||
|
options = {
|
||||||
|
pure_getters: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
a.prop;
|
||||||
|
b.prop;
|
||||||
|
c.prop;
|
||||||
|
d.prop;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
undefined.prop;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a, b = null, c = {};
|
||||||
|
d;
|
||||||
|
null.prop;
|
||||||
|
(void 0).prop;
|
||||||
|
(void 0).prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chained: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
a.b.c;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a.b.c;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,22 +41,22 @@ reduce_vars: {
|
|||||||
var A = 1;
|
var A = 1;
|
||||||
(function() {
|
(function() {
|
||||||
console.log(-3);
|
console.log(-3);
|
||||||
console.log(-4);
|
console.log(A - 5);
|
||||||
})();
|
})();
|
||||||
(function f1() {
|
(function f1() {
|
||||||
var a = 2;
|
var a = 2;
|
||||||
console.log(-3);
|
console.log(a - 5);
|
||||||
eval("console.log(a);");
|
eval("console.log(a);");
|
||||||
})();
|
})();
|
||||||
(function f2(eval) {
|
(function f2(eval) {
|
||||||
var a = 2;
|
var a = 2;
|
||||||
console.log(-3);
|
console.log(a - 5);
|
||||||
eval("console.log(a);");
|
eval("console.log(a);");
|
||||||
})(eval);
|
})(eval);
|
||||||
(function() {
|
(function() {
|
||||||
return "yes";
|
return "yes";
|
||||||
})();
|
})();
|
||||||
console.log(2);
|
console.log(A + 1);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
@@ -300,7 +300,7 @@ unsafe_evaluate_array: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe_evaluate_equality: {
|
unsafe_evaluate_equality_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate : true,
|
evaluate : true,
|
||||||
reduce_vars : true,
|
reduce_vars : true,
|
||||||
@@ -308,47 +308,62 @@ unsafe_evaluate_equality: {
|
|||||||
unused : true
|
unused : true
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
function f0(){
|
function f0() {
|
||||||
var a = {};
|
var a = {};
|
||||||
console.log(a === a);
|
return a === a;
|
||||||
}
|
}
|
||||||
|
function f1() {
|
||||||
function f1(){
|
|
||||||
var a = [];
|
var a = [];
|
||||||
console.log(a === a);
|
return a === a;
|
||||||
}
|
}
|
||||||
|
console.log(f0(), f1());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f0() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function f1() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.log(f0(), f1());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
function f2(){
|
unsafe_evaluate_equality_2: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
evaluate : true,
|
||||||
|
passes : 2,
|
||||||
|
reduce_vars : true,
|
||||||
|
unsafe : true,
|
||||||
|
unused : true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f2() {
|
||||||
var a = {a:1, b:2};
|
var a = {a:1, b:2};
|
||||||
var b = a;
|
var b = a;
|
||||||
var c = a;
|
var c = a;
|
||||||
console.log(b === c);
|
return b === c;
|
||||||
}
|
}
|
||||||
|
function f3() {
|
||||||
function f3(){
|
|
||||||
var a = [1, 2, 3];
|
var a = [1, 2, 3];
|
||||||
var b = a;
|
var b = a;
|
||||||
var c = a;
|
var c = a;
|
||||||
console.log(b === c);
|
return b === c;
|
||||||
}
|
}
|
||||||
|
console.log(f2(), f3());
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
function f0(){
|
function f2() {
|
||||||
console.log(true);
|
return true;
|
||||||
}
|
}
|
||||||
|
function f3() {
|
||||||
function f1(){
|
return true;
|
||||||
console.log(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function f2(){
|
|
||||||
console.log(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function f3(){
|
|
||||||
console.log(true);
|
|
||||||
}
|
}
|
||||||
|
console.log(f2(), f3());
|
||||||
}
|
}
|
||||||
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
passes: {
|
passes: {
|
||||||
@@ -1717,7 +1732,10 @@ redefine_arguments_3: {
|
|||||||
console.log(function() {
|
console.log(function() {
|
||||||
var arguments;
|
var arguments;
|
||||||
return typeof arguments;
|
return typeof arguments;
|
||||||
}(), "number", "undefined");
|
}(), "number", function(x) {
|
||||||
|
var arguments = x;
|
||||||
|
return typeof arguments;
|
||||||
|
}());
|
||||||
}
|
}
|
||||||
expect_stdout: "object number undefined"
|
expect_stdout: "object number undefined"
|
||||||
}
|
}
|
||||||
@@ -1866,3 +1884,288 @@ delay_def: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
booleans: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
if (a != 0);
|
||||||
|
switch (a) {
|
||||||
|
case 0:
|
||||||
|
return "FAIL";
|
||||||
|
case false:
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
}(false));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
if (!1);
|
||||||
|
switch (!1) {
|
||||||
|
case 0:
|
||||||
|
return "FAIL";
|
||||||
|
case !1:
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
}(!1));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
side_effects_assign: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = typeof void (a && a.in == 1, 0);
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = typeof void (a && a.in);
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
pure_getters_1: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
var a = (a.b, 2);
|
||||||
|
} catch (e) {}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
var a = (a.b, 2);
|
||||||
|
} catch (e) {}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
pure_getters_2: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
var a = a && a.b;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
var a = a && a.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pure_getters_3: {
|
||||||
|
options = {
|
||||||
|
pure_getters: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
var a = a && a.b;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catch_var: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
throw {};
|
||||||
|
} catch (e) {
|
||||||
|
var e;
|
||||||
|
console.log(!!e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
throw {};
|
||||||
|
} catch (e) {
|
||||||
|
var e;
|
||||||
|
console.log(!!e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_stdout: "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1814_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
const a = 42;
|
||||||
|
!function() {
|
||||||
|
var b = a;
|
||||||
|
!function(a) {
|
||||||
|
console.log(a++, b);
|
||||||
|
}(0);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
const a = 42;
|
||||||
|
!function() {
|
||||||
|
!function(a) {
|
||||||
|
console.log(a++, 42);
|
||||||
|
}(0);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "0 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1814_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
const a = "32";
|
||||||
|
!function() {
|
||||||
|
var b = a + 1;
|
||||||
|
!function(a) {
|
||||||
|
console.log(a++, b);
|
||||||
|
}(0);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
const a = "32";
|
||||||
|
!function() {
|
||||||
|
!function(a) {
|
||||||
|
console.log(a++, "321");
|
||||||
|
}(0);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "0 '321'"
|
||||||
|
}
|
||||||
|
|
||||||
|
try_abort: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
try {
|
||||||
|
var a = 1;
|
||||||
|
throw "";
|
||||||
|
var b = 2;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
console.log(a, b);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
try {
|
||||||
|
var a = 1;
|
||||||
|
throw "";
|
||||||
|
var b = 2;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
console.log(a, b);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "1 undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1865: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(some) {
|
||||||
|
some.thing = false;
|
||||||
|
}
|
||||||
|
console.log(function() {
|
||||||
|
var some = { thing: true };
|
||||||
|
f(some);
|
||||||
|
return some.thing;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(some) {
|
||||||
|
some.thing = false;
|
||||||
|
}
|
||||||
|
console.log(function() {
|
||||||
|
var some = { thing: true };
|
||||||
|
f(some);
|
||||||
|
return some.thing;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1922_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
arguments[0] = 2;
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
arguments[0] = 2;
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1922_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var a;
|
||||||
|
eval("a = 1");
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a;
|
||||||
|
eval("a = 1");
|
||||||
|
return a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|||||||
@@ -466,3 +466,171 @@ issue_1758: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "undefined"
|
expect_stdout: "undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete_seq_1: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (1, undefined));
|
||||||
|
console.log(delete (1, void 0));
|
||||||
|
console.log(delete (1, Infinity));
|
||||||
|
console.log(delete (1, 1 / 0));
|
||||||
|
console.log(delete (1, NaN));
|
||||||
|
console.log(delete (1, 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_seq_2: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (1, 2, undefined));
|
||||||
|
console.log(delete (1, 2, void 0));
|
||||||
|
console.log(delete (1, 2, Infinity));
|
||||||
|
console.log(delete (1, 2, 1 / 0));
|
||||||
|
console.log(delete (1, 2, NaN));
|
||||||
|
console.log(delete (1, 2, 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_seq_3: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
keep_infinity: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(delete (1, 2, undefined));
|
||||||
|
console.log(delete (1, 2, void 0));
|
||||||
|
console.log(delete (1, 2, Infinity));
|
||||||
|
console.log(delete (1, 2, 1 / 0));
|
||||||
|
console.log(delete (1, 2, NaN));
|
||||||
|
console.log(delete (1, 2, 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((void 0, !0));
|
||||||
|
console.log((Infinity, !0));
|
||||||
|
console.log((1 / 0, !0));
|
||||||
|
console.log((NaN, !0));
|
||||||
|
console.log((0 / 0, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_seq_4: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {}
|
||||||
|
console.log(delete (f(), undefined));
|
||||||
|
console.log(delete (f(), void 0));
|
||||||
|
console.log(delete (f(), Infinity));
|
||||||
|
console.log(delete (f(), 1 / 0));
|
||||||
|
console.log(delete (f(), NaN));
|
||||||
|
console.log(delete (f(), 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {}
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_seq_5: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
keep_infinity: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {}
|
||||||
|
console.log(delete (f(), undefined));
|
||||||
|
console.log(delete (f(), void 0));
|
||||||
|
console.log(delete (f(), Infinity));
|
||||||
|
console.log(delete (f(), 1 / 0));
|
||||||
|
console.log(delete (f(), NaN));
|
||||||
|
console.log(delete (f(), 0 / 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {}
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0)),
|
||||||
|
console.log((f(), !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_seq_6: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
console.log(delete (1, a));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
console.log((a, !0));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
reassign_const: {
|
||||||
|
options = {
|
||||||
|
cascade: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
a++;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
const a = 1;
|
||||||
|
return a++, a;
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,37 @@
|
|||||||
var vm = require("vm");
|
var vm = require("vm");
|
||||||
|
|
||||||
|
function safe_log(arg) {
|
||||||
|
if (arg) switch (typeof arg) {
|
||||||
|
case "function":
|
||||||
|
return arg.toString();
|
||||||
|
case "object":
|
||||||
|
if (/Error$/.test(arg.name)) return arg.toString();
|
||||||
|
arg.constructor.toString();
|
||||||
|
for (var key in arg) {
|
||||||
|
arg[key] = safe_log(arg[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
var FUNC_TOSTRING = [
|
var FUNC_TOSTRING = [
|
||||||
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
||||||
" var ids = [];",
|
" var id = 0;",
|
||||||
" return function() {",
|
" return function() {",
|
||||||
" var i = ids.indexOf(this);",
|
' if (this === Array) return "[Function: Array]";',
|
||||||
" if (i < 0) {",
|
' if (this === Object) return "[Function: Object]";',
|
||||||
" i = ids.length;",
|
" var i = this.name;",
|
||||||
" ids.push(this);",
|
' if (typeof i != "number") {',
|
||||||
|
" i = ++id;",
|
||||||
|
' Object.defineProperty(this, "name", {',
|
||||||
|
" get: function() {",
|
||||||
|
" return i;",
|
||||||
|
" }",
|
||||||
|
" });",
|
||||||
" }",
|
" }",
|
||||||
' return "[Function: __func_" + i + "__]";',
|
' return "[Function: " + i + "]";',
|
||||||
" }",
|
" }",
|
||||||
"}();",
|
"}();",
|
||||||
""
|
|
||||||
].join("\n");
|
].join("\n");
|
||||||
exports.run_code = function(code) {
|
exports.run_code = function(code) {
|
||||||
var stdout = "";
|
var stdout = "";
|
||||||
@@ -21,15 +40,18 @@ exports.run_code = function(code) {
|
|||||||
stdout += chunk;
|
stdout += chunk;
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
new vm.Script(FUNC_TOSTRING + code).runInNewContext({
|
vm.runInNewContext([
|
||||||
|
FUNC_TOSTRING,
|
||||||
|
"!function() {",
|
||||||
|
code,
|
||||||
|
"}();",
|
||||||
|
].join("\n"), {
|
||||||
console: {
|
console: {
|
||||||
log: function() {
|
log: function() {
|
||||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
return console.log.apply(console, [].map.call(arguments, safe_log));
|
||||||
return typeof arg == "function" ? arg.toString() : arg;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { timeout: 30000 });
|
}, { timeout: 5000 });
|
||||||
return stdout;
|
return stdout;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return ex;
|
return ex;
|
||||||
|
|||||||
620
test/ufuzz.js
620
test/ufuzz.js
@@ -2,7 +2,7 @@
|
|||||||
// 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";
|
||||||
|
|
||||||
// check both cli and file modes of nodejs (!). See #1695 for details. and the various settings of uglify.
|
// check both CLI and file modes of nodejs (!). See #1695 for details. and the various settings of uglify.
|
||||||
// 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
|
||||||
|
|
||||||
@@ -20,49 +20,26 @@ var MAX_GENERATED_TOPLEVELS_PER_RUN = 1;
|
|||||||
var MAX_GENERATION_RECURSION_DEPTH = 12;
|
var MAX_GENERATION_RECURSION_DEPTH = 12;
|
||||||
var INTERVAL_COUNT = 100;
|
var INTERVAL_COUNT = 100;
|
||||||
|
|
||||||
var STMT_BLOCK = 0;
|
var STMT_ARG_TO_ID = Object.create(null);
|
||||||
var STMT_IF_ELSE = 1;
|
var STMTS_TO_USE = [];
|
||||||
var STMT_DO_WHILE = 2;
|
function STMT_(name) {
|
||||||
var STMT_WHILE = 3;
|
return STMT_ARG_TO_ID[name] = STMTS_TO_USE.push(STMTS_TO_USE.length) - 1;
|
||||||
var STMT_FOR_LOOP = 4;
|
}
|
||||||
var STMT_SEMI = 5;
|
|
||||||
var STMT_EXPR = 6;
|
var STMT_BLOCK = STMT_("block");
|
||||||
var STMT_SWITCH = 7;
|
var STMT_IF_ELSE = STMT_("ifelse");
|
||||||
var STMT_VAR = 8;
|
var STMT_DO_WHILE = STMT_("dowhile");
|
||||||
var STMT_RETURN_ETC = 9;
|
var STMT_WHILE = STMT_("while");
|
||||||
var STMT_FUNC_EXPR = 10;
|
var STMT_FOR_LOOP = STMT_("forloop");
|
||||||
var STMT_TRY = 11;
|
var STMT_FOR_IN = STMT_("forin");
|
||||||
var STMT_C = 12;
|
var STMT_SEMI = STMT_("semi");
|
||||||
var STMTS_TO_USE = [
|
var STMT_EXPR = STMT_("expr");
|
||||||
STMT_BLOCK,
|
var STMT_SWITCH = STMT_("switch");
|
||||||
STMT_IF_ELSE,
|
var STMT_VAR = STMT_("var");
|
||||||
STMT_DO_WHILE,
|
var STMT_RETURN_ETC = STMT_("stop");
|
||||||
STMT_WHILE,
|
var STMT_FUNC_EXPR = STMT_("funcexpr");
|
||||||
STMT_FOR_LOOP,
|
var STMT_TRY = STMT_("try");
|
||||||
STMT_SEMI,
|
var STMT_C = STMT_("c");
|
||||||
STMT_EXPR,
|
|
||||||
STMT_SWITCH,
|
|
||||||
STMT_VAR,
|
|
||||||
STMT_RETURN_ETC,
|
|
||||||
STMT_FUNC_EXPR,
|
|
||||||
STMT_TRY,
|
|
||||||
STMT_C,
|
|
||||||
];
|
|
||||||
var STMT_ARG_TO_ID = {
|
|
||||||
block: STMT_BLOCK,
|
|
||||||
ifelse: STMT_IF_ELSE,
|
|
||||||
dowhile: STMT_DO_WHILE,
|
|
||||||
while: STMT_WHILE,
|
|
||||||
forloop: STMT_FOR_LOOP,
|
|
||||||
semi: STMT_SEMI,
|
|
||||||
expr: STMT_EXPR,
|
|
||||||
switch: STMT_SWITCH,
|
|
||||||
var: STMT_VAR,
|
|
||||||
stop: STMT_RETURN_ETC,
|
|
||||||
funcexpr: STMT_FUNC_EXPR,
|
|
||||||
try: STMT_TRY,
|
|
||||||
c: STMT_C,
|
|
||||||
};
|
|
||||||
|
|
||||||
var STMT_FIRST_LEVEL_OVERRIDE = -1;
|
var STMT_FIRST_LEVEL_OVERRIDE = -1;
|
||||||
var STMT_SECOND_LEVEL_OVERRIDE = -1;
|
var STMT_SECOND_LEVEL_OVERRIDE = -1;
|
||||||
@@ -71,6 +48,8 @@ var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest functio
|
|||||||
var num_iterations = +process.argv[2] || 1/0;
|
var num_iterations = +process.argv[2] || 1/0;
|
||||||
var verbose = false; // log every generated test
|
var verbose = false; // log every generated test
|
||||||
var verbose_interval = false; // log every 100 generated tests
|
var verbose_interval = false; // log every 100 generated tests
|
||||||
|
var verbose_error = false;
|
||||||
|
var use_strict = false;
|
||||||
for (var i = 2; i < process.argv.length; ++i) {
|
for (var i = 2; i < process.argv.length; ++i) {
|
||||||
switch (process.argv[i]) {
|
switch (process.argv[i]) {
|
||||||
case '-v':
|
case '-v':
|
||||||
@@ -79,6 +58,9 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
case '-V':
|
case '-V':
|
||||||
verbose_interval = true;
|
verbose_interval = true;
|
||||||
break;
|
break;
|
||||||
|
case '-E':
|
||||||
|
verbose_error = true;
|
||||||
|
break;
|
||||||
case '-t':
|
case '-t':
|
||||||
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
|
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
|
||||||
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
|
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
|
||||||
@@ -97,6 +79,9 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
||||||
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
||||||
break;
|
break;
|
||||||
|
case '--use-strict':
|
||||||
|
use_strict = true;
|
||||||
|
break;
|
||||||
case '--stmt-depth-from-func':
|
case '--stmt-depth-from-func':
|
||||||
STMT_COUNT_FROM_GLOBAL = false;
|
STMT_COUNT_FROM_GLOBAL = false;
|
||||||
break;
|
break;
|
||||||
@@ -118,10 +103,12 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
console.log('<number>: generate this many cases (if used must be first arg)');
|
console.log('<number>: generate this many cases (if used must be first arg)');
|
||||||
console.log('-v: print every generated test case');
|
console.log('-v: print every generated test case');
|
||||||
console.log('-V: print every 100th generated test case');
|
console.log('-V: print every 100th generated test case');
|
||||||
|
console.log('-E: print generated test case with runtime error');
|
||||||
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
||||||
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
||||||
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
||||||
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
||||||
|
console.log('--use-strict: generate "use strict"');
|
||||||
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
||||||
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
||||||
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
|
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
|
||||||
@@ -135,6 +122,7 @@ for (var i = 2; i < process.argv.length; ++i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var VALUES = [
|
var VALUES = [
|
||||||
|
'""',
|
||||||
'true',
|
'true',
|
||||||
'false',
|
'false',
|
||||||
' /[a2][^e]+$/ ',
|
' /[a2][^e]+$/ ',
|
||||||
@@ -221,15 +209,19 @@ var ASSIGNMENTS = [
|
|||||||
'>>>=',
|
'>>>=',
|
||||||
'%=' ];
|
'%=' ];
|
||||||
|
|
||||||
var UNARY_OPS = [
|
var UNARY_SAFE = [
|
||||||
'--',
|
'+',
|
||||||
'++',
|
'-',
|
||||||
'~',
|
'~',
|
||||||
'!',
|
'!',
|
||||||
'void ',
|
'void ',
|
||||||
'delete ', // should be safe, even `delete foo` and `delete f()` shouldn't crash
|
'delete ',
|
||||||
' - ',
|
];
|
||||||
' + ' ];
|
var UNARY_POSTFIX = [
|
||||||
|
'++',
|
||||||
|
'--',
|
||||||
|
];
|
||||||
|
var UNARY_PREFIX = UNARY_POSTFIX.concat(UNARY_SAFE);
|
||||||
|
|
||||||
var NO_COMMA = true;
|
var NO_COMMA = true;
|
||||||
var COMMA_OK = false;
|
var COMMA_OK = false;
|
||||||
@@ -250,26 +242,26 @@ var NO_DECL = true;
|
|||||||
var DONT_STORE = true;
|
var DONT_STORE = true;
|
||||||
|
|
||||||
var VAR_NAMES = [
|
var VAR_NAMES = [
|
||||||
'foo',
|
'a',
|
||||||
'bar',
|
'a',
|
||||||
|
'a',
|
||||||
'a',
|
'a',
|
||||||
'b',
|
'b',
|
||||||
|
'b',
|
||||||
|
'b',
|
||||||
|
'b',
|
||||||
'c', // prevent redeclaring this, avoid assigning to this
|
'c', // prevent redeclaring this, avoid assigning to this
|
||||||
'undefined', // fun!
|
'foo',
|
||||||
'eval', // mmmm, ok, also fun!
|
'foo',
|
||||||
'NaN', // mmmm, ok, also fun!
|
'bar',
|
||||||
'Infinity', // the fun never ends!
|
'bar',
|
||||||
'arguments', // this one is just creepy
|
'undefined',
|
||||||
'Math', // since Math is assumed to be a non-constructor/function it may trip certain cases
|
'NaN',
|
||||||
|
'Infinity',
|
||||||
|
'arguments',
|
||||||
|
'Math',
|
||||||
'parseInt',
|
'parseInt',
|
||||||
'parseFloat',
|
];
|
||||||
'isNaN',
|
|
||||||
'isFinite',
|
|
||||||
'decodeURI',
|
|
||||||
'decodeURIComponent',
|
|
||||||
'encodeURI',
|
|
||||||
'encodeURIComponent',
|
|
||||||
'Object'];
|
|
||||||
var INITIAL_NAMES_LEN = VAR_NAMES.length;
|
var INITIAL_NAMES_LEN = VAR_NAMES.length;
|
||||||
|
|
||||||
var TYPEOF_OUTCOMES = [
|
var TYPEOF_OUTCOMES = [
|
||||||
@@ -286,15 +278,26 @@ var TYPEOF_OUTCOMES = [
|
|||||||
|
|
||||||
var loops = 0;
|
var loops = 0;
|
||||||
var funcs = 0;
|
var funcs = 0;
|
||||||
|
var labels = 10000;
|
||||||
|
|
||||||
function rng(max) {
|
function rng(max) {
|
||||||
var r = randomBytes(2).readUInt16LE(0) / 65536;
|
var r = randomBytes(2).readUInt16LE(0) / 65536;
|
||||||
return Math.floor(max * r);
|
return Math.floor(max * r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function strictMode() {
|
||||||
|
return use_strict && rng(4) == 0 ? '"use strict";' : '';
|
||||||
|
}
|
||||||
|
|
||||||
function createTopLevelCode() {
|
function createTopLevelCode() {
|
||||||
if (rng(2) === 0) return createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0);
|
return [
|
||||||
return createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, IN_GLOBAL, ANY_TYPE, CANNOT_THROW, 0);
|
strictMode(),
|
||||||
|
'var a = 100, b = 10, c = 0;',
|
||||||
|
rng(2) == 0
|
||||||
|
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
|
||||||
|
: createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, IN_GLOBAL, ANY_TYPE, CANNOT_THROW, 0),
|
||||||
|
'console.log(null, a, b, c);' // preceding `null` makes for a cleaner output (empty string still shows up etc)
|
||||||
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
||||||
@@ -306,6 +309,22 @@ function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createParams() {
|
||||||
|
var params = [];
|
||||||
|
for (var n = rng(4); --n >= 0;) {
|
||||||
|
params.push(createVarName(MANDATORY));
|
||||||
|
}
|
||||||
|
return params.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createArgs() {
|
||||||
|
var args = [];
|
||||||
|
for (var n = rng(4); --n >= 0;) {
|
||||||
|
args.push(createValue());
|
||||||
|
}
|
||||||
|
return args.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
||||||
if (--recurmax < 0) { return ';'; }
|
if (--recurmax < 0) { return ';'; }
|
||||||
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
||||||
@@ -316,17 +335,29 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
|||||||
var s = '';
|
var s = '';
|
||||||
if (rng(5) === 0) {
|
if (rng(5) === 0) {
|
||||||
// functions with functions. lower the recursion to prevent a mess.
|
// functions with functions. lower the recursion to prevent a mess.
|
||||||
s = 'function ' + name + '(' + createVarName(MANDATORY) + '){' + createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth) + '}\n';
|
s = [
|
||||||
|
'function ' + name + '(' + createParams() + '){',
|
||||||
|
strictMode(),
|
||||||
|
createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth),
|
||||||
|
'}',
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
} else {
|
} else {
|
||||||
// functions with statements
|
// functions with statements
|
||||||
s = 'function ' + name + '(' + createVarName(MANDATORY) + '){' + createStatements(3, recurmax, canThrow, CANNOT_THROW, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}\n';
|
s = [
|
||||||
|
'function ' + name + '(' + createParams() + '){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'}',
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
VAR_NAMES.length = namesLenBefore;
|
VAR_NAMES.length = namesLenBefore;
|
||||||
|
|
||||||
if (noDecl) s = '!' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
|
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ');';
|
||||||
// avoid "function statements" (decl inside statements)
|
// avoid "function statements" (decl inside statements)
|
||||||
else if (inGlobal || rng(10) > 0) s += name + '();'
|
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
|
||||||
|
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -341,6 +372,40 @@ function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotRe
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enableLoopControl(flag, defaultValue) {
|
||||||
|
return Array.isArray(flag) && flag.indexOf("") < 0 ? flag.concat("") : flag || defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLabel(canBreak, canContinue) {
|
||||||
|
var label;
|
||||||
|
if (rng(10) < 3) {
|
||||||
|
label = ++labels;
|
||||||
|
if (Array.isArray(canBreak)) {
|
||||||
|
canBreak = canBreak.slice();
|
||||||
|
} else {
|
||||||
|
canBreak = canBreak ? [ "" ] : [];
|
||||||
|
}
|
||||||
|
canBreak.push(label);
|
||||||
|
if (Array.isArray(canContinue)) {
|
||||||
|
canContinue = canContinue.slice();
|
||||||
|
} else {
|
||||||
|
canContinue = canContinue ? [ "" ] : [];
|
||||||
|
}
|
||||||
|
canContinue.push(label);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
break: canBreak,
|
||||||
|
continue: canContinue,
|
||||||
|
target: label ? "L" + label + ": " : ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabel(label) {
|
||||||
|
if (!Array.isArray(label)) return "";
|
||||||
|
label = label[rng(label.length)];
|
||||||
|
return label && " L" + label;
|
||||||
|
}
|
||||||
|
|
||||||
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||||
++stmtDepth;
|
++stmtDepth;
|
||||||
var loop = ++loops;
|
var loop = ++loops;
|
||||||
@@ -356,17 +421,36 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case STMT_BLOCK:
|
case STMT_BLOCK:
|
||||||
return '{' + createStatements(rng(5) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}';
|
var label = createLabel(canBreak);
|
||||||
|
return label.target + '{' + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + '}';
|
||||||
case STMT_IF_ELSE:
|
case STMT_IF_ELSE:
|
||||||
return 'if (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) === 1 ? ' else ' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : '');
|
return 'if (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) === 1 ? ' else ' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : '');
|
||||||
case STMT_DO_WHILE:
|
case STMT_DO_WHILE:
|
||||||
return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth) + '} while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0);}';
|
var label = createLabel(canBreak, canContinue);
|
||||||
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
|
return '{var brake' + loop + ' = 5; ' + label.target + 'do {' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '} while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0);}';
|
||||||
case STMT_WHILE:
|
case STMT_WHILE:
|
||||||
return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth) + '}';
|
var label = createLabel(canBreak, canContinue);
|
||||||
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
|
return '{var brake' + loop + ' = 5; ' + label.target + 'while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}';
|
||||||
case STMT_FOR_LOOP:
|
case STMT_FOR_LOOP:
|
||||||
return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth);
|
var label = createLabel(canBreak, canContinue);
|
||||||
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
|
return label.target + 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
||||||
|
case STMT_FOR_IN:
|
||||||
|
var label = createLabel(canBreak, canContinue);
|
||||||
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
|
var optElementVar = '';
|
||||||
|
if (rng(5) > 1) {
|
||||||
|
optElementVar = 'c = 1 + c; var ' + createVarName(MANDATORY) + ' = expr' + loop + '[key' + loop + ']; ';
|
||||||
|
}
|
||||||
|
return '{var expr' + loop + ' = ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '; ' + label.target + ' for (var key' + loop + ' in expr' + loop + ') {' + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}}';
|
||||||
case STMT_SEMI:
|
case STMT_SEMI:
|
||||||
return ';';
|
return use_strict && rng(20) === 0 ? '"use strict";' : ';';
|
||||||
case STMT_EXPR:
|
case STMT_EXPR:
|
||||||
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';';
|
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';';
|
||||||
case STMT_SWITCH:
|
case STMT_SWITCH:
|
||||||
@@ -393,23 +477,27 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
}
|
}
|
||||||
case STMT_RETURN_ETC:
|
case STMT_RETURN_ETC:
|
||||||
switch (rng(3)) {
|
switch (rng(8)) {
|
||||||
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
if (canBreak && rng(5) === 0) return 'break;';
|
|
||||||
if (canContinue && rng(5) === 0) return 'continue;';
|
|
||||||
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
|
||||||
return '/*3*/return;';
|
|
||||||
case 2:
|
case 2:
|
||||||
// must wrap in curlies to prevent orphaned `else` statement
|
case 3:
|
||||||
if (canThrow && rng(5) === 0) return '{ throw ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
if (canBreak && rng(5) === 0) return 'break' + getLabel(canBreak) + ';';
|
||||||
|
if (canContinue && rng(5) === 0) return 'continue' + getLabel(canContinue) + ';';
|
||||||
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
return '{ /*1*/ return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
if (rng(3) == 0) return '/*3*/return;';
|
||||||
default:
|
return 'return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
|
case 4:
|
||||||
// this is actually more like a parser test, but perhaps it hits some dead code elimination traps
|
// this is actually more like a parser test, but perhaps it hits some dead code elimination traps
|
||||||
// must wrap in curlies to prevent orphaned `else` statement
|
// must wrap in curlies to prevent orphaned `else` statement
|
||||||
// note: you can't `throw` without an expression so don't put a `throw` option in this case
|
// note: you can't `throw` without an expression so don't put a `throw` option in this case
|
||||||
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
return '{ /*2*/ return\n' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
return '{ /*2*/ return\n' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
||||||
|
default:
|
||||||
|
// must wrap in curlies to prevent orphaned `else` statement
|
||||||
|
if (canThrow && rng(5) === 0) return '{ throw ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
||||||
|
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
|
return '{ return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
||||||
}
|
}
|
||||||
case STMT_FUNC_EXPR:
|
case STMT_FUNC_EXPR:
|
||||||
// "In non-strict mode code, functions can only be declared at top level, inside a block, or ..."
|
// "In non-strict mode code, functions can only be declared at top level, inside a block, or ..."
|
||||||
@@ -440,90 +528,130 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
|
|
||||||
function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||||
var hadDefault = false;
|
var hadDefault = false;
|
||||||
var s = '';
|
var s = [''];
|
||||||
|
canBreak = enableLoopControl(canBreak, CAN_BREAK);
|
||||||
while (n-- > 0) {
|
while (n-- > 0) {
|
||||||
//hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes)
|
//hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes)
|
||||||
if (hadDefault || rng(5) > 0) {
|
if (hadDefault || rng(5) > 0) {
|
||||||
s += '' +
|
s.push(
|
||||||
'case ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':\n' +
|
'case ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':',
|
||||||
createStatements(rng(3) + 1, recurmax, canThrow, CAN_BREAK, canContinue, cannotReturn, stmtDepth) +
|
createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
|
||||||
'\n' +
|
rng(10) > 0 ? ' break;' : '/* fall-through */',
|
||||||
(rng(10) > 0 ? ' break;' : '/* fall-through */') +
|
''
|
||||||
'\n';
|
);
|
||||||
} else {
|
} else {
|
||||||
hadDefault = true;
|
hadDefault = true;
|
||||||
s += '' +
|
s.push(
|
||||||
'default:\n' +
|
'default:',
|
||||||
createStatements(rng(3) + 1, recurmax, canThrow, CAN_BREAK, canContinue, cannotReturn, stmtDepth) +
|
createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
|
||||||
'\n';
|
''
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s;
|
return s.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
function createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
if (--recurmax < 0) {
|
if (--recurmax < 0) {
|
||||||
return '(c = 1 + c, ' + createNestedBinaryExpr(recurmax, noComma) + ')'; // note: should return a simple non-recursing expression value!
|
return '(c = 1 + c, ' + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; // note: should return a simple non-recursing expression value!
|
||||||
}
|
}
|
||||||
// since `a` and `b` are our canaries we want them more frequently than other expressions (1/3rd chance of a canary)
|
// since `a` and `b` are our canaries we want them more frequently than other expressions (1/3rd chance of a canary)
|
||||||
var r = rng(6);
|
switch (rng(6)) {
|
||||||
if (r < 1) return 'a++ + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
case 0:
|
||||||
if (r < 2) return '(--b) + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
return '(a++ + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))';
|
||||||
if (r < 3) return '(c = c + 1) + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); // c only gets incremented
|
case 1:
|
||||||
|
return '((--b) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))';
|
||||||
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
case 2:
|
||||||
|
return '((c = c + 1) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; // c only gets incremented
|
||||||
|
default:
|
||||||
|
return '(' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
switch (rng(15)) {
|
var p = 0;
|
||||||
case 0:
|
switch (rng(_createExpression.N)) {
|
||||||
return createUnaryOp() + (rng(2) === 1 ? 'a' : 'b');
|
case p++:
|
||||||
case 1:
|
case p++:
|
||||||
return 'a' + (rng(2) == 1 ? '++' : '--');
|
return createUnaryPrefix() + (rng(2) === 1 ? 'a' : 'b');
|
||||||
case 2:
|
case p++:
|
||||||
|
case p++:
|
||||||
|
return (rng(2) === 1 ? 'a' : 'b') + createUnaryPostfix();
|
||||||
|
case p++:
|
||||||
|
case p++:
|
||||||
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
||||||
return '(b ' + createAssignment() + ' a)';
|
return 'b ' + createAssignment() + ' a';
|
||||||
case 3:
|
case p++:
|
||||||
|
case p++:
|
||||||
return rng(2) + ' === 1 ? a : b';
|
return rng(2) + ' === 1 ? a : b';
|
||||||
case 4:
|
case p++:
|
||||||
return createNestedBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
case p++:
|
||||||
case 5:
|
|
||||||
return createValue();
|
return createValue();
|
||||||
case 6:
|
case p++:
|
||||||
return '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
|
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
|
||||||
case 7:
|
case p++:
|
||||||
return createExpression(recurmax, noComma, stmtDepth, canThrow) + '?' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':' + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
return createExpression(recurmax, noComma, stmtDepth, canThrow) + '?' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':' + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
case 8:
|
case p++:
|
||||||
|
case p++:
|
||||||
var nameLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
|
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
|
||||||
if (name === 'c') name = 'a';
|
if (name == 'c') name = 'a';
|
||||||
var s = '';
|
var s = [];
|
||||||
switch(rng(4)) {
|
switch (rng(5)) {
|
||||||
case 0:
|
case 0:
|
||||||
s = '(function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '})()';
|
s.push(
|
||||||
|
'(function ' + name + '(){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'})()'
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
s = '+function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}()';
|
s.push(
|
||||||
|
'+function ' + name + '(){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'}()'
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
s = '!function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}()';
|
s.push(
|
||||||
|
'!function ' + name + '(){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'}()'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
s.push(
|
||||||
|
'void function ' + name + '(){',
|
||||||
|
strictMode(),
|
||||||
|
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'}()'
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s = 'void function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}()';
|
var instantiate = rng(4) ? 'new ' : '';
|
||||||
|
s.push(
|
||||||
|
instantiate + 'function ' + name + '(){',
|
||||||
|
strictMode()
|
||||||
|
);
|
||||||
|
if (instantiate) for (var i = rng(4); --i >= 0;) {
|
||||||
|
if (rng(2)) s.push('this.' + getDotKey() + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
|
||||||
|
else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
|
||||||
|
}
|
||||||
|
s.push(
|
||||||
|
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
|
'}'
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
VAR_NAMES.length = nameLenBefore;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
return s;
|
return s.join('\n');
|
||||||
case 9:
|
case p++:
|
||||||
|
case p++:
|
||||||
return createTypeofExpr(recurmax, stmtDepth, canThrow);
|
return createTypeofExpr(recurmax, stmtDepth, canThrow);
|
||||||
case 10:
|
case p++:
|
||||||
// you could statically infer that this is just `Math`, regardless of the other expression
|
case p++:
|
||||||
// I don't think Uglify does this at this time...
|
|
||||||
return ''+
|
|
||||||
'new function(){ \n' +
|
|
||||||
(rng(2) === 1 ? createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '\n' : '') +
|
|
||||||
'return Math;\n' +
|
|
||||||
'}';
|
|
||||||
case 11:
|
|
||||||
// more like a parser test but perhaps comment nodes mess up the analysis?
|
// more like a parser test but perhaps comment nodes mess up the analysis?
|
||||||
// note: parens not needed for post-fix (since that's the default when ambiguous)
|
// note: parens not needed for post-fix (since that's the default when ambiguous)
|
||||||
// for prefix ops we need parens to prevent accidental syntax errors.
|
// for prefix ops we need parens to prevent accidental syntax errors.
|
||||||
@@ -533,58 +661,152 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
case 1:
|
case 1:
|
||||||
return 'b/* ignore */--';
|
return 'b/* ignore */--';
|
||||||
case 2:
|
case 2:
|
||||||
return '(++/* ignore */a)';
|
return '++/* ignore */a';
|
||||||
case 3:
|
case 3:
|
||||||
return '(--/* ignore */b)';
|
return '--/* ignore */b';
|
||||||
case 4:
|
case 4:
|
||||||
// only groups that wrap a single variable return a "Reference", so this is still valid.
|
// only groups that wrap a single variable return a "Reference", so this is still valid.
|
||||||
// may just be a parser edge case that is invisible to uglify...
|
// may just be a parser edge case that is invisible to uglify...
|
||||||
return '(--(b))';
|
return '--(b)';
|
||||||
case 5:
|
case 5:
|
||||||
// classic 0.3-0.1 case; 1-0.1-0.1-0.1 is not 0.7 :)
|
// classic 0.3-0.1 case; 1-0.1-0.1-0.1 is not 0.7 :)
|
||||||
return 'b + 1-0.1-0.1-0.1';
|
return 'b + 1-0.1-0.1-0.1';
|
||||||
default:
|
default:
|
||||||
return '(--/* ignore */b)';
|
return '--/* ignore */b';
|
||||||
}
|
}
|
||||||
case 12:
|
case p++:
|
||||||
return createNestedBinaryExpr(recurmax, noComma);
|
case p++:
|
||||||
case 13:
|
return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
|
||||||
|
case p++:
|
||||||
|
case p++:
|
||||||
|
return createUnarySafePrefix() + '(' + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
|
case p++:
|
||||||
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() ";
|
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() ";
|
||||||
case 14:
|
case p++:
|
||||||
return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
|
return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
|
||||||
|
case p++:
|
||||||
|
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) +
|
||||||
|
") || " + rng(10) + ").toString()[" +
|
||||||
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
|
||||||
|
case p++:
|
||||||
|
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
|
||||||
|
case p++:
|
||||||
|
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
|
||||||
|
case p++:
|
||||||
|
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
|
||||||
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
||||||
|
case p++:
|
||||||
|
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
|
||||||
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
||||||
|
case p++:
|
||||||
|
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
|
||||||
|
case p++:
|
||||||
|
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
|
||||||
|
case p++:
|
||||||
|
var name = getVarName();
|
||||||
|
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
|
||||||
|
case p++:
|
||||||
|
var name = getVarName();
|
||||||
|
return name + ' && ' + name + '.' + getDotKey();
|
||||||
}
|
}
|
||||||
|
_createExpression.N = p;
|
||||||
|
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNestedBinaryExpr(recurmax, noComma) {
|
function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
recurmax = 3; // note that this generates 2^recurmax expression parts... make sure to cap it
|
recurmax--;
|
||||||
return _createSimpleBinaryExpr(recurmax, noComma);
|
var arr = "[";
|
||||||
|
for (var i = rng(6); --i >= 0;) {
|
||||||
|
// in rare cases produce an array hole element
|
||||||
|
var element = rng(20) ? createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) : "";
|
||||||
|
arr += element + ", ";
|
||||||
|
}
|
||||||
|
return arr + "]";
|
||||||
}
|
}
|
||||||
function _createSimpleBinaryExpr(recurmax, noComma) {
|
|
||||||
|
var SAFE_KEYS = [
|
||||||
|
"length",
|
||||||
|
"foo",
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c",
|
||||||
|
"undefined",
|
||||||
|
"null",
|
||||||
|
"NaN",
|
||||||
|
"Infinity",
|
||||||
|
"in",
|
||||||
|
"var",
|
||||||
|
];
|
||||||
|
var KEYS = [
|
||||||
|
"''",
|
||||||
|
'"\t"',
|
||||||
|
'"-2"',
|
||||||
|
"0",
|
||||||
|
"1.5",
|
||||||
|
"3",
|
||||||
|
].concat(SAFE_KEYS);
|
||||||
|
|
||||||
|
function getDotKey() {
|
||||||
|
return SAFE_KEYS[rng(SAFE_KEYS.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
|
recurmax--;
|
||||||
|
var obj = "({";
|
||||||
|
for (var i = rng(6); --i >= 0;) {
|
||||||
|
var key = KEYS[rng(KEYS.length)];
|
||||||
|
obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), ";
|
||||||
|
}
|
||||||
|
return obj + "})";
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
|
recurmax = 3; // note that this generates 2^recurmax expression parts... make sure to cap it
|
||||||
|
return _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
|
||||||
|
}
|
||||||
|
function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
|
return '(' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow)
|
||||||
|
+ createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
|
}
|
||||||
|
function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
// intentionally generate more hardcore ops
|
// intentionally generate more hardcore ops
|
||||||
if (--recurmax < 0) return createValue();
|
if (--recurmax < 0) return createValue();
|
||||||
var r = rng(30);
|
var assignee, expr;
|
||||||
if (r === 0) return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma) + ')';
|
switch (rng(30)) {
|
||||||
var s = _createSimpleBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma);
|
case 0:
|
||||||
if (r === 1) {
|
return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
// try to get a generated name reachable from current scope. default to just `a`
|
case 1:
|
||||||
var assignee = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || 'a';
|
return '(' + createUnarySafePrefix() + '(' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + '))';
|
||||||
return '( ' + assignee + createAssignment() + s + ')';
|
case 2:
|
||||||
|
assignee = getVarName();
|
||||||
|
return '(' + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
|
case 3:
|
||||||
|
assignee = getVarName();
|
||||||
|
expr = '(' + assignee + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow)
|
||||||
|
+ ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
|
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
|
||||||
|
case 4:
|
||||||
|
assignee = getVarName();
|
||||||
|
expr = '(' + assignee + '.' + getDotKey() + createAssignment()
|
||||||
|
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
|
||||||
|
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
|
||||||
|
default:
|
||||||
|
return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
|
||||||
}
|
}
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTypeofExpr(recurmax, stmtDepth, canThrow) {
|
function createTypeofExpr(recurmax, stmtDepth, canThrow) {
|
||||||
switch (rng(8)) {
|
switch (rng(8)) {
|
||||||
case 0:
|
case 0:
|
||||||
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
|
||||||
case 1:
|
case 1:
|
||||||
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
|
||||||
case 2:
|
case 2:
|
||||||
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
|
||||||
case 3:
|
case 3:
|
||||||
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
|
||||||
case 4:
|
case 4:
|
||||||
return 'typeof ' + createVarName(MANDATORY, DONT_STORE);
|
return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ')';
|
||||||
default:
|
default:
|
||||||
return '(typeof ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
|
return '(typeof ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
|
||||||
}
|
}
|
||||||
@@ -603,16 +825,31 @@ function createAssignment() {
|
|||||||
return ASSIGNMENTS[rng(ASSIGNMENTS.length)];
|
return ASSIGNMENTS[rng(ASSIGNMENTS.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createUnaryOp() {
|
function createUnarySafePrefix() {
|
||||||
return UNARY_OPS[rng(UNARY_OPS.length)];
|
return UNARY_SAFE[rng(UNARY_SAFE.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUnaryPrefix() {
|
||||||
|
return UNARY_PREFIX[rng(UNARY_PREFIX.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUnaryPostfix() {
|
||||||
|
return UNARY_POSTFIX[rng(UNARY_POSTFIX.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVarName() {
|
||||||
|
// try to get a generated name reachable from current scope. default to just `a`
|
||||||
|
return VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || 'a';
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVarName(maybe, dontStore) {
|
function createVarName(maybe, dontStore) {
|
||||||
if (!maybe || rng(2) === 1) {
|
if (!maybe || rng(2)) {
|
||||||
var r = rng(VAR_NAMES.length);
|
var name = VAR_NAMES[rng(VAR_NAMES.length)];
|
||||||
var suffixed = rng(5) > 0;
|
var suffix = rng(3);
|
||||||
var name = VAR_NAMES[r] + (suffixed ? '_' + (++loops) : '');
|
if (suffix) {
|
||||||
if (!dontStore && suffixed) VAR_NAMES.push(name);
|
name += '_' + suffix;
|
||||||
|
if (!dontStore) VAR_NAMES.push(name);
|
||||||
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
@@ -655,8 +892,8 @@ var default_options = {
|
|||||||
mangle: {
|
mangle: {
|
||||||
"cache": null,
|
"cache": null,
|
||||||
"eval": false,
|
"eval": false,
|
||||||
|
"ie8": false,
|
||||||
"keep_fnames": false,
|
"keep_fnames": false,
|
||||||
"screw_ie8": true,
|
|
||||||
"toplevel": false,
|
"toplevel": false,
|
||||||
},
|
},
|
||||||
output: infer_options(UglifyJS.OutputStream),
|
output: infer_options(UglifyJS.OutputStream),
|
||||||
@@ -711,17 +948,27 @@ function log(options) {
|
|||||||
} else {
|
} else {
|
||||||
console.log("// !!! uglify failed !!!");
|
console.log("// !!! uglify failed !!!");
|
||||||
console.log(uglify_code.stack);
|
console.log(uglify_code.stack);
|
||||||
|
if (typeof original_result != "string") {
|
||||||
|
console.log();
|
||||||
|
console.log();
|
||||||
|
console.log("original stacktrace:");
|
||||||
|
console.log(original_result.stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.log("minify(options):");
|
console.log("minify(options):");
|
||||||
options = JSON.parse(options);
|
options = JSON.parse(options);
|
||||||
console.log(options);
|
console.log(options);
|
||||||
console.log();
|
console.log();
|
||||||
if (!ok) {
|
if (!ok && typeof uglify_code == "string") {
|
||||||
Object.keys(default_options).forEach(log_suspects.bind(null, options));
|
Object.keys(default_options).forEach(log_suspects.bind(null, options));
|
||||||
console.log("!!!!!! Failed... round", round);
|
console.log("!!!!!! Failed... round", round);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fallback_options = [ JSON.stringify({
|
||||||
|
compress: false,
|
||||||
|
mangle: false
|
||||||
|
}) ];
|
||||||
var minify_options = require("./ufuzz.json").map(function(options) {
|
var minify_options = require("./ufuzz.json").map(function(options) {
|
||||||
options.fromString = true;
|
options.fromString = true;
|
||||||
return JSON.stringify(options);
|
return JSON.stringify(options);
|
||||||
@@ -735,13 +982,9 @@ for (var round = 1; round <= num_iterations; round++) {
|
|||||||
loops = 0;
|
loops = 0;
|
||||||
funcs = 0;
|
funcs = 0;
|
||||||
|
|
||||||
original_code = [
|
original_code = createTopLevelCode();
|
||||||
"var a = 100, b = 10, c = 0;",
|
original_result = sandbox.run_code(original_code);
|
||||||
createTopLevelCode(),
|
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
|
||||||
"console.log(null, a, b, c);" // preceding `null` makes for a cleaner output (empty string still shows up etc)
|
|
||||||
].join("\n");
|
|
||||||
|
|
||||||
minify_options.forEach(function(options) {
|
|
||||||
try {
|
try {
|
||||||
uglify_code = UglifyJS.minify(original_code, JSON.parse(options)).code;
|
uglify_code = UglifyJS.minify(original_code, JSON.parse(options)).code;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -750,11 +993,26 @@ for (var round = 1; round <= num_iterations; round++) {
|
|||||||
|
|
||||||
ok = typeof uglify_code == "string";
|
ok = typeof uglify_code == "string";
|
||||||
if (ok) {
|
if (ok) {
|
||||||
original_result = sandbox.run_code(original_code);
|
|
||||||
uglify_result = sandbox.run_code(uglify_code);
|
uglify_result = sandbox.run_code(uglify_code);
|
||||||
ok = sandbox.same_stdout(original_result, uglify_result);
|
ok = sandbox.same_stdout(original_result, uglify_result);
|
||||||
|
} else if (typeof original_result != "string") {
|
||||||
|
ok = uglify_code.name == original_result.name;
|
||||||
}
|
}
|
||||||
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
|
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
|
||||||
if (!ok && isFinite(num_iterations)) process.exit(1);
|
else if (verbose_error && typeof original_result != "string") {
|
||||||
|
console.log("//=============================================================");
|
||||||
|
console.log("// original code");
|
||||||
|
try_beautify(original_code, original_result);
|
||||||
|
console.log();
|
||||||
|
console.log();
|
||||||
|
console.log("original result:");
|
||||||
|
console.log(original_result);
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
if (!ok && isFinite(num_iterations)) {
|
||||||
|
console.log();
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log();
|
||||||
|
|||||||
@@ -1,19 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"compress": {
|
|
||||||
"warnings": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"compress": {
|
|
||||||
"warnings": false
|
|
||||||
},
|
|
||||||
"mangle": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"compress": false,
|
|
||||||
"mangle": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"compress": false,
|
"compress": false,
|
||||||
"mangle": false,
|
"mangle": false,
|
||||||
@@ -22,11 +7,33 @@
|
|||||||
"bracketize": true
|
"bracketize": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"compress": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"compress": {
|
||||||
|
"warnings": false
|
||||||
|
},
|
||||||
|
"mangle": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"compress": {
|
||||||
|
"warnings": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"compress": {
|
||||||
|
"toplevel": true,
|
||||||
|
"warnings": false
|
||||||
|
},
|
||||||
|
"mangle": {
|
||||||
|
"toplevel": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"compress": {
|
"compress": {
|
||||||
"keep_fargs": false,
|
"keep_fargs": false,
|
||||||
"passes": 3,
|
"passes": 3,
|
||||||
"pure_getters": true,
|
|
||||||
"warnings": false
|
"warnings": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user