Compare commits

...

75 Commits

Author SHA1 Message Date
Alex Lam S.L
30ed8f5580 v3.9.3 2020-05-13 17:23:01 +08:00
Alex Lam S.L
dc9e7cd1fe suppress ufuzz false positives (#3893) 2020-05-13 07:07:49 +08:00
Alex Lam S.L
76f40e2528 fix corner case in collapse_vars (#3892)
fixes #3891
2020-05-12 22:28:55 +08:00
Alex Lam S.L
8024f7f7a8 fix corner case in ie8 (#3890)
fixes #3889
2020-05-12 19:28:29 +08:00
Alex Lam S.L
eb7fa25270 fix corner case in evaluate (#3888)
fixes #3887
2020-05-12 17:58:37 +08:00
Alex Lam S.L
ee7647dc67 fix corner case in collapse_vars (#3885)
fixes #3884
2020-05-12 04:01:14 +08:00
Alex Lam S.L
bd2f53bc8b fix corner case in evaluate (#3883)
fixes #3882
2020-05-12 03:24:44 +08:00
Alex Lam S.L
e8a7956b6f fix corner case in reduce_vars (#3881)
fixes #3880
2020-05-12 02:29:33 +08:00
Alex Lam S.L
2b24dc25fb fix corner cases in evaluate & reduce_vars (#3879)
fixes #3878
2020-05-11 22:46:00 +08:00
Alex Lam S.L
35cc5aa06f extend --reduce-test to cover minify() bugs (#3876) 2020-05-11 07:32:21 +08:00
Alex Lam S.L
c1dd49e075 fix corner case in comparisons (#3877) 2020-05-11 06:33:52 +08:00
Alex Lam S.L
c76ee4b868 enhance if_return (#3875) 2020-05-11 04:29:55 +08:00
Alex Lam S.L
e23bf48052 enhance evaluate & reduce_vars (#3873) 2020-05-11 03:08:05 +08:00
Alex Lam S.L
7e0ad232b0 retain @__PURE__ call when return value is used (#3874) 2020-05-11 01:07:05 +08:00
Alex Lam S.L
63adfb1590 fix corner case in hoist_props (#3872)
fixes #3871
2020-05-10 22:23:09 +08:00
Alex Lam S.L
f9806b43c3 enhance evaluate & reduce_vars (#3870) 2020-05-10 18:38:32 +08:00
Alex Lam S.L
c4c9c6d37d fix corner case in hoist_props (#3869)
fixes #3868
2020-05-10 10:35:24 +01:00
Alex Lam S.L
33f3b0c1d9 fix corner case in reduce_vars (#3867)
fixes #3866
2020-05-10 16:35:03 +08:00
Alex Lam S.L
abb8ae02a5 improve inline of /*@__PURE__*/ calls (#3865) 2020-05-10 07:16:09 +08:00
Alex Lam S.L
97728c4f0b improve AST validation (#3864) 2020-05-10 05:25:44 +08:00
Alex Lam S.L
f74b7f7401 implement AST validation (#3863) 2020-05-09 09:58:03 +08:00
Alex Lam S.L
b06fd8a933 improve fix for #3856 (#3862) 2020-05-09 08:50:25 +08:00
Alex Lam S.L
1bb0804d60 improve ufuzz detection of suspicious options (#3860) 2020-05-08 15:03:48 +08:00
Alex Lam S.L
998245ffd6 fix corner case in inline (#3859)
fixes #3858
2020-05-08 15:03:29 +08:00
Alex Lam S.L
7a033bb825 fix corner case in join_vars (#3857)
fixes #3856
2020-05-08 11:49:17 +08:00
Alex Lam S.L
a441b00951 suppress ufuzz false positives (#3855) 2020-05-08 03:21:44 +08:00
Alex Lam S.L
88985a46ed fix corner case in inline (#3853)
fixes #3852
2020-05-07 20:53:05 +08:00
Alex Lam S.L
34ead0430b enhance dead_code (#3849) 2020-05-06 05:02:35 +08:00
Alex Lam S.L
66ab2df97f fix intermittent CI failures on GitHub Actions (#3851) 2020-05-06 03:29:23 +08:00
kzc
b656f7c083 remove unused returns from tree walk (#3850) 2020-05-06 02:21:36 +08:00
Alex Lam S.L
873db281e8 improve TreeWalker performance (#3848) 2020-05-05 22:45:58 +08:00
Alex Lam S.L
6bf1486935 update links to repository after rename (#3847) 2020-05-05 21:07:33 +08:00
Alex Lam S.L
ffa1943177 fix corner case in reduce_vars (#3845)
fixes #3844
2020-05-04 03:30:10 +08:00
Alex Lam S.L
ac429dc8e1 enhance reduce_vars (#3843) 2020-05-03 22:52:43 +08:00
Alex Lam S.L
3766d5c962 enhance unused (#3839) 2020-05-03 17:38:28 +08:00
Alex Lam S.L
20f9a1d908 v3.9.2 2020-05-03 11:01:18 +08:00
Alex Lam S.L
dcb74f558e fix diagnostic text (#3838) 2020-05-01 18:55:06 +08:00
Alex Lam S.L
0794aaa2c2 fix corner case in inline (#3837)
fixes #3836
2020-05-01 17:20:23 +08:00
Alex Lam S.L
74801de315 fix corner cases in inline (#3834)
fixes #3833
fixes #3835
2020-05-01 09:06:40 +08:00
Alex Lam S.L
f80d5b8c9e enhance inline (#3832) 2020-05-01 04:33:46 +08:00
Alex Lam S.L
d900006973 fix corner case in dead_code (#3831)
fixes #3830
2020-04-30 21:52:57 +08:00
Alex Lam S.L
39f849590b update dependencies (#3828)
- actions/checkout@v2
2020-04-30 05:55:04 +08:00
Alex Lam S.L
818738beec fix corner case in ie8 (#3826)
fixes #3825
2020-04-27 16:51:21 +08:00
Alex Lam S.L
bc2a4a3bb8 fix corner case in ie8 (#3824)
fixes #3823
2020-04-27 06:44:53 +08:00
Alex Lam S.L
a4a8ccea8c fix corner case in inline (#3822)
fixes #3821
2020-04-27 03:31:19 +08:00
Alex Lam S.L
36dcfa3e82 improve suspicious option detection (#3820) 2020-04-27 00:59:26 +08:00
Alex Lam S.L
94f33570e3 fix corner case in --reduce-test (#3819) 2020-04-25 06:49:50 +08:00
Alex Lam S.L
44d6912a55 improve --reduce-test on Error.message (#3816)
closes #3815
2020-04-25 01:30:37 +08:00
Alex Lam S.L
3a4497a1c3 fix corner case in typeofs (#3818)
fixes #3817
2020-04-25 01:29:42 +08:00
Alex Lam S.L
3ee13cae02 improve compress (#3814)
- avoid identifier overflow through consecutive API calls
- simplify `reduce_vars`
- enhance `unsafe` `evaluate`
2020-04-24 06:50:53 +08:00
Alex Lam S.L
99cf3a38c5 improve unused efficiency (#3813) 2020-04-23 08:31:35 +08:00
Alex Lam S.L
3ae24329eb gate various label-related transformations (#3812) 2020-04-23 05:27:26 +08:00
Alex Lam S.L
01b13d797c enhance dead_code (#3811) 2020-04-22 18:22:45 +08:00
Alex Lam S.L
306e8e9873 adjust ufuzz schedule (#3810) 2020-04-22 16:03:15 +08:00
Alex Lam S.L
9577c8c1b7 fix corner case in conditionals (#3809)
fixes #3808
2020-04-22 06:30:08 +08:00
Alex Lam S.L
925a0ca1a0 adjust ufuzz schedule (#3807) 2020-04-22 02:58:02 +08:00
Alex Lam S.L
b694bfa351 speed up ufuzz on GitHub Actions (#3806) 2020-04-21 22:51:42 +08:00
Alex Lam S.L
a2fc32c64b enhance conditionals (#3805) 2020-04-20 09:42:13 +08:00
Alex Lam S.L
88504ab869 enhance join_vars (#3804) 2020-04-20 06:37:46 +08:00
Alex Lam S.L
e38754e802 fix corner case in functions & unused (#3803)
fixes #3802
2020-04-19 06:28:01 +08:00
Alex Lam S.L
eb6f32bfc3 enhance collapse_vars (#3801) 2020-04-19 05:04:21 +08:00
Alex Lam S.L
f110601fb4 enhance unused (#3800) 2020-04-19 00:10:24 +08:00
Alex Lam S.L
2a508c6e5f enhance collapse_vars (#3799) 2020-04-18 23:12:20 +08:00
Alex Lam S.L
fd6144d95b enhance conditionals (#3798) 2020-04-18 22:32:22 +08:00
Alex Lam S.L
60d4e7b09f enhance unused (#3794) 2020-04-18 18:52:44 +08:00
Alex Lam S.L
b38838c6bf fix corner case in join_vars (#3796)
fixes #3795
2020-04-18 18:08:05 +08:00
Alex Lam S.L
708973e51d report top-level default options (#3797) 2020-04-18 18:03:06 +08:00
Alex Lam S.L
dac9e69f9e enhance collapse_vars (#3793) 2020-04-18 10:06:20 +08:00
Alex Lam S.L
39aa33749b expand ufuzz patterns (#3792) 2020-04-18 07:12:13 +08:00
Alex Lam S.L
da68ec6e19 fix corner cases in join_vars (#3790)
fixes #3789
fixes #3791
2020-04-18 02:53:26 +08:00
Alex Lam S.L
15a3ebd467 fix corner cases in join_vars (#3787)
fixes #3786
fixes #3788
2020-04-17 21:19:18 +08:00
Alex Lam S.L
9110fac9a2 suppress mutation of input options (#3785) 2020-04-17 15:13:49 +08:00
Alex Lam S.L
83f42ede36 support optional output of names in source maps (#3784) 2020-04-17 07:20:48 +08:00
Alex Lam S.L
0ce71bbec0 enhance join_vars (#3783) 2020-04-17 05:31:33 +08:00
Alex Lam S.L
46d142cbf6 improve source-map generation (#3782)
- emit singleton segments to mark generated code from input
2020-04-16 23:30:25 +08:00
55 changed files with 4969 additions and 1369 deletions

View File

@@ -8,6 +8,8 @@ jobs:
os: [ ubuntu-latest, windows-latest ]
script: [ compress, mocha, release/benchmark, release/jetstream ]
exclude:
- node: "0.8"
script: release/benchmark
- node: "0.8"
script: release/jetstream
name: ${{ matrix.node }} ${{ matrix.os }} ${{ matrix.script }}
@@ -16,7 +18,7 @@ jobs:
NODE: ${{ matrix.node }}
TYPE: ${{ matrix.script }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: tmp

View File

@@ -1,7 +1,7 @@
name: Fuzzing
on:
schedule:
- cron: "*/15 * * * *"
- cron: "*/8 * * * *"
jobs:
ufuzz:
strategy:
@@ -11,7 +11,7 @@ jobs:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Perform fuzzing
shell: bash
run: |

View File

@@ -4,8 +4,8 @@ UglifyJS 3
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note:
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- `uglify-js` only supports JavaScript (ECMAScript 5).
- To minify ECMAScript 2015 or above, transpile using tools like [Babel](https://babeljs.io/).
@@ -126,6 +126,7 @@ a double dash to prevent input files being used as option arguments:
`includeSources` Pass this flag if you want to include
the content of source files in the
source map as sourcesContent property.
`names` Include symbol names in the source map.
`root` Path to the original source to be included in
the source map.
`url` If specified, path to the source map to append in
@@ -159,6 +160,9 @@ Additional options:
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
- `--source-map "names=false"` to omit symbol names if you want to reduce size
of the source map file.
- `--source-map "url='<URL>'"` to specify the URL where the source map can be found.
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
`//# sourceMappingURL=` directive.
@@ -593,6 +597,9 @@ var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
If you wish to reduce file size of the source map, set option `sourceMap.names`
to be `false` and all symbol names will be omitted.
## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements
@@ -906,7 +913,7 @@ can pass additional arguments that control the code output:
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details.
[#640](https://github.com/mishoo/UglifyJS/issues/640) for more details.
# Miscellaneous
@@ -1065,8 +1072,8 @@ var result = UglifyJS.minify(ast, {
### Working with Uglify AST
Transversal and transformation of the native AST can be performed through
[`TreeWalker`](https://github.com/mishoo/UglifyJS2/blob/master/lib/ast.js) and
[`TreeTransformer`](https://github.com/mishoo/UglifyJS2/blob/master/lib/transform.js)
[`TreeWalker`](https://github.com/mishoo/UglifyJS/blob/master/lib/ast.js) and
[`TreeTransformer`](https://github.com/mishoo/UglifyJS/blob/master/lib/transform.js)
respectively.
### ESTree / SpiderMonkey AST

View File

@@ -23,12 +23,29 @@ program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var toplevels = [];
var padding = "";
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
text.push(format_object(options[option]));
text.push("");
for (var name in options) {
var option = options[name];
if (option && typeof option == "object") {
text.push("--" + ({
output: "beautify",
sourceMap: "source-map",
}[name] || name) + " options:");
text.push(format_object(option));
text.push("");
} else {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
toplevels.push([ {
keep_fnames: "keep-fnames",
nameCache: "name-cache",
}[name] || name, option ]);
}
}
toplevels.forEach(function(tokens) {
text.push("--" + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js());
@@ -51,6 +68,7 @@ program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_js());
program.option("--timings", "Display operations run time on STDERR.");
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--validate", "Perform validation during AST manipulations.");
program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages.");
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally.");
@@ -74,6 +92,7 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
"mangle",
"sourceMap",
"toplevel",
"validate",
"wrap"
].forEach(function(name) {
if (name in program) {
@@ -220,7 +239,10 @@ function run() {
// load on demand - assumes dev tree checked out
var reduce_test = require("../test/reduce");
var testcase = files[0] || files[Object.keys(files)[0]];
var result = reduce_test(testcase, options, {verbose: true});
var result = reduce_test(testcase, options, {
log: print_error,
verbose: true,
});
}
else {
var result = UglifyJS.minify(files, options);

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -110,12 +110,16 @@ var AST_Node = DEFNODE("Node", "start end", {
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
_walk: function(visitor) {
return visitor._visit(this);
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
}
visitor.visit(this);
},
_validate: noop,
validate: function() {
var ctor = this.CTOR;
do {
ctor.prototype._validate.call(this);
} while (ctor = ctor.BASE);
},
}, null);
(AST_Node.log_function = function(fn, verbose) {
@@ -138,6 +142,32 @@ var AST_Node = DEFNODE("Node", "start end", {
}
})();
var restore_transforms = [];
AST_Node.enable_validation = function() {
AST_Node.disable_validation();
(function validate_transform(ctor) {
var transform = ctor.prototype.transform;
ctor.prototype.transform = function(tw, in_list) {
var node = transform.call(this, tw, in_list);
if (node instanceof AST_Node) {
node.validate();
} else if (!(node === null || in_list && List.is_op(node))) {
throw new Error("invalid transformed value: " + node);
}
return node;
};
restore_transforms.push(function() {
ctor.prototype.transform = transform;
});
ctor.SUBCLASSES.forEach(validate_transform);
})(this);
};
AST_Node.disable_validation = function() {
var restore;
while (restore = restore_transforms.pop()) restore();
};
/* -----[ statements ]----- */
var AST_Statement = DEFNODE("Statement", null, {
@@ -154,23 +184,37 @@ var AST_Directive = DEFNODE("Directive", "value quote", {
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
quote: "[string] the original quote character"
},
_validate: function() {
if (typeof this.value != "string") throw new Error("value must be string");
},
}, AST_Statement);
function must_be_expression(node, prop) {
if (!(node[prop] instanceof AST_Node)) throw new Error(prop + " must be AST_Node");
if (node[prop] instanceof AST_Statement && !(node[prop] instanceof AST_Function)) {
throw new Error(prop + " cannot be AST_Statement");
}
}
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
$propdoc: {
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.body._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.body.walk(visitor);
});
}
},
_validate: function() {
must_be_expression(this, "body");
},
}, AST_Statement);
function walk_body(node, visitor) {
node.body.forEach(function(node) {
node._walk(visitor);
node.walk(visitor);
});
}
@@ -179,11 +223,18 @@ var AST_Block = DEFNODE("Block", "body", {
$propdoc: {
body: "[AST_Statement*] an array of statements"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
walk_body(this, visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
walk_body(node, visitor);
});
}
},
_validate: function() {
this.body.forEach(function(node) {
if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]");
if (node instanceof AST_Function) throw new Error("body cannot contain AST_Function");
});
},
}, AST_Statement);
var AST_BlockStatement = DEFNODE("BlockStatement", null, {
@@ -198,7 +249,11 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
$propdoc: {
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
}
},
_validate: function() {
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
if (this.body instanceof AST_Function) throw new Error("body cannot be AST_Function");
},
}, AST_Statement);
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
@@ -206,10 +261,11 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
$propdoc: {
label: "[AST_Label] a label definition"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.label._walk(visitor);
this.body._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.label.walk(visitor);
node.body.walk(visitor);
});
},
clone: function(deep) {
@@ -225,7 +281,10 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
}));
}
return node;
}
},
_validate: function() {
if (!(this.label instanceof AST_Label)) throw new Error("label must be AST_Label");
},
}, AST_StatementWithBody);
var AST_IterationStatement = DEFNODE("IterationStatement", null, {
@@ -236,25 +295,30 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
$documentation: "Base class for do/while statements",
$propdoc: {
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
}
},
_validate: function() {
must_be_expression(this, "condition");
},
}, AST_IterationStatement);
var AST_Do = DEFNODE("Do", null, {
$documentation: "A `do` statement",
_walk: function(visitor) {
return visitor._visit(this, function() {
this.body._walk(visitor);
this.condition._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.body.walk(visitor);
node.condition.walk(visitor);
});
}
}, AST_DWLoop);
var AST_While = DEFNODE("While", null, {
$documentation: "A `while` statement",
_walk: function(visitor) {
return visitor._visit(this, function() {
this.condition._walk(visitor);
this.body._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.condition.walk(visitor);
node.body.walk(visitor);
});
}
}, AST_DWLoop);
@@ -266,14 +330,26 @@ var AST_For = DEFNODE("For", "init condition step", {
condition: "[AST_Node?] the `for` termination clause, or null if empty",
step: "[AST_Node?] the `for` update clause, or null if empty"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
if (this.init) this.init._walk(visitor);
if (this.condition) this.condition._walk(visitor);
if (this.step) this.step._walk(visitor);
this.body._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.init) node.init.walk(visitor);
if (node.condition) node.condition.walk(visitor);
if (node.step) node.step.walk(visitor);
node.body.walk(visitor);
});
}
},
_validate: function() {
if (this.init != null) {
if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node");
if (this.init instanceof AST_Statement
&& !(this.init instanceof AST_Definitions || this.init instanceof AST_Function)) {
throw new Error("init cannot be AST_Statement");
}
}
if (this.condition != null) must_be_expression(this, "condition");
if (this.step != null) must_be_expression(this, "step");
},
}, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", "init object", {
@@ -282,13 +358,22 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
init: "[AST_Node] the `for/in` initialization code",
object: "[AST_Node] the object that we're looping through"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.init._walk(visitor);
this.object._walk(visitor);
this.body._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.init.walk(visitor);
node.object.walk(visitor);
node.body.walk(visitor);
});
}
},
_validate: function() {
if (this.init instanceof AST_Definitions) {
if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
} else if (!(this.init instanceof AST_PropAccess || this.init instanceof AST_SymbolRef)) {
throw new Error("init must be assignable");
}
must_be_expression(this, "object");
},
}, AST_IterationStatement);
var AST_With = DEFNODE("With", "expression", {
@@ -296,12 +381,16 @@ var AST_With = DEFNODE("With", "expression", {
$propdoc: {
expression: "[AST_Node] the `with` expression"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
this.body._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
node.body.walk(visitor);
});
}
},
_validate: function() {
must_be_expression(this, "expression");
},
}, AST_StatementWithBody);
/* -----[ scope and functions ]----- */
@@ -326,7 +415,12 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
},
pinned: function() {
return this.uses_eval || this.uses_with;
}
},
_validate: function() {
if (this.parent_scope != null) {
if (!(this.parent_scope instanceof AST_Scope)) throw new Error("parent_scope must be AST_Scope");
}
},
}, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
@@ -380,27 +474,44 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
argnames: "[AST_SymbolFunarg*] array of function arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
if (this.name) this.name._walk(visitor);
this.argnames.forEach(function(argname) {
argname._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.name) node.name.walk(visitor);
node.argnames.forEach(function(argname) {
argname.walk(visitor);
});
walk_body(this, visitor);
walk_body(node, visitor);
});
}
},
_validate: function() {
this.argnames.forEach(function(node) {
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
});
},
}, AST_Scope);
var AST_Accessor = DEFNODE("Accessor", null, {
$documentation: "A setter/getter function. The `name` property is always null."
$documentation: "A setter/getter function. The `name` property is always null.",
_validate: function() {
if (this.name != null) throw new Error("name must be null");
},
}, AST_Lambda);
var AST_Function = DEFNODE("Function", "inlined", {
$documentation: "A function expression"
$documentation: "A function expression",
_validate: function() {
if (this.name != null) {
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
}
},
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", "inlined", {
$documentation: "A function definition"
$documentation: "A function definition",
_validate: function() {
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
},
}, AST_Lambda);
/* -----[ JUMPS ]----- */
@@ -414,19 +525,26 @@ var AST_Exit = DEFNODE("Exit", "value", {
$propdoc: {
value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
},
_walk: function(visitor) {
return visitor._visit(this, this.value && function() {
this.value._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.value) node.value.walk(visitor);
});
}
}, AST_Jump);
var AST_Return = DEFNODE("Return", null, {
$documentation: "A `return` statement"
$documentation: "A `return` statement",
_validate: function() {
if (this.value != null) must_be_expression(this, "value");
},
}, AST_Exit);
var AST_Throw = DEFNODE("Throw", null, {
$documentation: "A `throw` statement"
$documentation: "A `throw` statement",
_validate: function() {
must_be_expression(this, "value");
},
}, AST_Exit);
var AST_LoopControl = DEFNODE("LoopControl", "label", {
@@ -434,11 +552,17 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", {
$propdoc: {
label: "[AST_LabelRef?] the label, or null if none",
},
_walk: function(visitor) {
return visitor._visit(this, this.label && function() {
this.label._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.label) node.label.walk(visitor);
});
}
},
_validate: function() {
if (this.label != null) {
if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef");
}
},
}, AST_Jump);
var AST_Break = DEFNODE("Break", null, {
@@ -457,13 +581,21 @@ var AST_If = DEFNODE("If", "condition alternative", {
condition: "[AST_Node] the `if` condition",
alternative: "[AST_Statement?] the `else` part, or null if not present"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.condition._walk(visitor);
this.body._walk(visitor);
if (this.alternative) this.alternative._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.condition.walk(visitor);
node.body.walk(visitor);
if (node.alternative) node.alternative.walk(visitor);
});
}
},
_validate: function() {
must_be_expression(this, "condition");
if (this.alternative != null) {
if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement");
if (this.alternative instanceof AST_Function) throw new error("alternative cannot be AST_Function");
}
},
}, AST_StatementWithBody);
/* -----[ SWITCH ]----- */
@@ -473,12 +605,16 @@ var AST_Switch = DEFNODE("Switch", "expression", {
$propdoc: {
expression: "[AST_Node] the `switch` “discriminant”"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
walk_body(this, visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
walk_body(node, visitor);
});
}
},
_validate: function() {
must_be_expression(this, "expression");
},
}, AST_Block);
var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
@@ -494,12 +630,16 @@ var AST_Case = DEFNODE("Case", "expression", {
$propdoc: {
expression: "[AST_Node] the `case` expression"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
walk_body(this, visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
walk_body(node, visitor);
});
}
},
_validate: function() {
must_be_expression(this, "expression");
},
}, AST_SwitchBranch);
/* -----[ EXCEPTIONS ]----- */
@@ -510,13 +650,22 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
bcatch: "[AST_Catch?] the catch block, or null if not present",
bfinally: "[AST_Finally?] the finally block, or null if not present"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
walk_body(this, visitor);
if (this.bcatch) this.bcatch._walk(visitor);
if (this.bfinally) this.bfinally._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
walk_body(node, visitor);
if (node.bcatch) node.bcatch.walk(visitor);
if (node.bfinally) node.bfinally.walk(visitor);
});
}
},
_validate: function() {
if (this.bcatch != null) {
if (!(this.bcatch instanceof AST_Catch)) throw new Error("bcatch must be AST_Catch");
}
if (this.bfinally != null) {
if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally");
}
},
}, AST_Block);
var AST_Catch = DEFNODE("Catch", "argname", {
@@ -524,12 +673,16 @@ var AST_Catch = DEFNODE("Catch", "argname", {
$propdoc: {
argname: "[AST_SymbolCatch] symbol for the exception"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.argname._walk(visitor);
walk_body(this, visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.argname.walk(visitor);
walk_body(node, visitor);
});
}
},
_validate: function() {
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
},
}, AST_Block);
var AST_Finally = DEFNODE("Finally", null, {
@@ -543,17 +696,23 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
$propdoc: {
definitions: "[AST_VarDef*] array of variable definitions"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.definitions.forEach(function(defn) {
defn._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.definitions.forEach(function(defn) {
defn.walk(visitor);
});
});
}
}, AST_Statement);
var AST_Var = DEFNODE("Var", null, {
$documentation: "A `var` statement"
$documentation: "A `var` statement",
_validate: function() {
this.definitions.forEach(function(node) {
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
});
},
}, AST_Definitions);
var AST_VarDef = DEFNODE("VarDef", "name value", {
@@ -562,30 +721,49 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
name: "[AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.name._walk(visitor);
if (this.value) this.value._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.name.walk(visitor);
if (node.value) node.value.walk(visitor);
});
}
},
_validate: function() {
if (!(this.name instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
if (this.value != null) must_be_expression(this, "value");
},
});
/* -----[ OTHER ]----- */
var AST_Call = DEFNODE("Call", "expression args", {
function must_be_expressions(node, prop) {
node[prop].forEach(function(node) {
if (!(node instanceof AST_Node)) throw new Error(prop + " must be AST_Node[]");
if (node instanceof AST_Statement && !(node instanceof AST_Function)) {
throw new Error(prop + " cannot contain AST_Statement");
}
});
}
var AST_Call = DEFNODE("Call", "expression args pure", {
$documentation: "A function call expression",
$propdoc: {
expression: "[AST_Node] expression to invoke as function",
args: "[AST_Node*] array of arguments"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
this.args.forEach(function(node) {
node._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
node.args.forEach(function(arg) {
arg.walk(visitor);
});
});
}
},
_validate: function() {
must_be_expression(this, "expression");
must_be_expressions(this, "args");
},
});
var AST_New = DEFNODE("New", null, {
@@ -597,13 +775,18 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", {
$propdoc: {
expressions: "[AST_Node*] array of expressions (at least two)"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expressions.forEach(function(node) {
node._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expressions.forEach(function(expr) {
expr.walk(visitor);
});
});
}
},
_validate: function() {
if (this.expressions.length < 2) throw new Error("expressions must contain multiple elements");
must_be_expressions(this, "expressions");
},
});
var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
@@ -623,26 +806,37 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
return;
}
return p;
}
},
_validate: function() {
must_be_expression(this, "expression");
},
});
var AST_Dot = DEFNODE("Dot", null, {
$documentation: "A dotted property access expression",
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
});
}
},
_validate: function() {
if (typeof this.property != "string") throw new Error("property must be string");
},
}, AST_PropAccess);
var AST_Sub = DEFNODE("Sub", null, {
$documentation: "Index-style property access, i.e. `a[\"foo\"]`",
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
this.property._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
node.property.walk(visitor);
});
}
},
_validate: function() {
must_be_expression(this, "property");
},
}, AST_PropAccess);
var AST_Unary = DEFNODE("Unary", "operator expression", {
@@ -651,11 +845,16 @@ var AST_Unary = DEFNODE("Unary", "operator expression", {
operator: "[string] the operator",
expression: "[AST_Node] expression that this unary operator applies to"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.expression._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.expression.walk(visitor);
});
}
},
_validate: function() {
if (typeof this.operator != "string") throw new Error("operator must be string");
must_be_expression(this, "expression");
},
});
var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
@@ -673,12 +872,18 @@ var AST_Binary = DEFNODE("Binary", "operator left right", {
operator: "[string] the operator",
right: "[AST_Node] right-hand side expression"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.left._walk(visitor);
this.right._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.left.walk(visitor);
node.right.walk(visitor);
});
}
},
_validate: function() {
must_be_expression(this, "left");
if (typeof this.operator != "string") throw new Error("operator must be string");
must_be_expression(this, "right");
},
});
var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
@@ -688,17 +893,26 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative",
consequent: "[AST_Node]",
alternative: "[AST_Node]"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.condition._walk(visitor);
this.consequent._walk(visitor);
this.alternative._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.condition.walk(visitor);
node.consequent.walk(visitor);
node.alternative.walk(visitor);
});
}
},
_validate: function() {
must_be_expression(this, "condition");
must_be_expression(this, "consequent");
must_be_expression(this, "alternative");
},
});
var AST_Assign = DEFNODE("Assign", null, {
$documentation: "An assignment expression — `a = b + 5`",
_validate: function() {
if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="');
},
}, AST_Binary);
/* -----[ LITERALS ]----- */
@@ -708,13 +922,17 @@ var AST_Array = DEFNODE("Array", "elements", {
$propdoc: {
elements: "[AST_Node*] array of elements"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.elements.forEach(function(element) {
element._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.elements.forEach(function(element) {
element.walk(visitor);
});
});
}
},
_validate: function() {
must_be_expressions(this, "elements");
},
});
var AST_Object = DEFNODE("Object", "properties", {
@@ -722,13 +940,19 @@ var AST_Object = DEFNODE("Object", "properties", {
$propdoc: {
properties: "[AST_ObjectProperty*] array of properties"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.properties.forEach(function(prop) {
prop._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.properties.forEach(function(prop) {
prop.walk(visitor);
});
});
}
},
_validate: function() {
this.properties.forEach(function(node) {
if (!(node instanceof AST_ObjectProperty)) throw new Error("properties must be AST_ObjectProperty[]");
});
},
});
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
@@ -737,9 +961,10 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
},
_walk: function(visitor) {
return visitor._visit(this, function() {
this.value._walk(visitor);
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.value.walk(visitor);
});
}
});
@@ -748,15 +973,27 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
$documentation: "A key: value object property",
$propdoc: {
quote: "[string] the original quote character"
}
},
_validate: function() {
if (typeof this.key != "string") throw new Error("key must be string");
must_be_expression(this, "value");
},
}, AST_ObjectProperty);
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
$documentation: "An object setter property",
_validate: function() {
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
},
}, AST_ObjectProperty);
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
$documentation: "An object getter property",
_validate: function() {
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
},
}, AST_ObjectProperty);
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
@@ -766,6 +1003,9 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
thedef: "[SymbolDef/S] the definition of this symbol"
},
$documentation: "Base class for all symbols",
_validate: function() {
if (typeof this.name != "string") throw new Error("name must be string");
},
});
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
@@ -817,6 +1057,9 @@ var AST_LabelRef = DEFNODE("LabelRef", null, {
var AST_This = DEFNODE("This", null, {
$documentation: "The `this` symbol",
_validate: function() {
if (this.name !== "this") throw new Error('name must be "this"');
},
}, AST_Symbol);
var AST_Constant = DEFNODE("Constant", null, {
@@ -828,21 +1071,30 @@ var AST_String = DEFNODE("String", "value quote", {
$propdoc: {
value: "[string] the contents of this string",
quote: "[string] the original quote character"
}
},
_validate: function() {
if (typeof this.value != "string") throw new Error("value must be string");
},
}, AST_Constant);
var AST_Number = DEFNODE("Number", "value", {
$documentation: "A number literal",
$propdoc: {
value: "[number] the numeric value",
}
},
_validate: function() {
if (typeof this.value != "number") throw new Error("value must be number");
},
}, AST_Constant);
var AST_RegExp = DEFNODE("RegExp", "value", {
$documentation: "A regexp literal",
$propdoc: {
value: "[RegExp] the actual regexp"
}
},
_validate: function() {
if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp");
},
}, AST_Constant);
var AST_Atom = DEFNODE("Atom", null, {
@@ -891,21 +1143,16 @@ var AST_True = DEFNODE("True", null, {
/* -----[ TreeWalker ]----- */
function TreeWalker(callback) {
this.visit = callback;
this.stack = [];
this.callback = callback;
this.directives = Object.create(null);
this.stack = [];
}
TreeWalker.prototype = {
_visit: function(node, descend) {
visit: function(node, descend) {
this.push(node);
var ret = this.visit(node, descend ? function() {
descend.call(node);
} : noop);
if (!ret && descend) {
descend.call(node);
}
var done = this.callback(node, descend || noop);
if (!done && descend) descend();
this.pop();
return ret;
},
parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)];

File diff suppressed because it is too large Load Diff

View File

@@ -85,9 +85,11 @@ function minify(files, options) {
sourceMap: false,
timings: false,
toplevel: false,
validate: false,
warnings: false,
wrap: false,
}, true);
if (options.validate) AST_Node.enable_validation();
var timings = options.timings && {
start: Date.now()
};
@@ -129,6 +131,7 @@ function minify(files, options) {
content: null,
filename: null,
includeSources: false,
names: true,
root: null,
url: null,
}, true);
@@ -138,7 +141,7 @@ function minify(files, options) {
warnings.push(warning);
}, options.warnings == "verbose");
if (timings) timings.parse = Date.now();
var source_maps, toplevel;
var toplevel;
if (files instanceof AST_Toplevel) {
toplevel = files;
} else {
@@ -151,19 +154,17 @@ function minify(files, options) {
if (typeof source_map_content == "string" && source_map_content != "inline") {
source_map_content = parse_source_map(source_map_content);
}
source_maps = source_map_content && Object.create(null);
if (source_map_content) options.sourceMap.orig = Object.create(null);
for (var name in files) if (HOP(files, name)) {
options.parse.filename = name;
options.parse.toplevel = toplevel = parse(files[name], options.parse);
if (source_maps) {
if (source_map_content == "inline") {
var inlined_content = read_source_map(name, toplevel);
if (inlined_content) {
source_maps[name] = parse_source_map(inlined_content);
}
} else {
source_maps[name] = source_map_content;
if (source_map_content == "inline") {
var inlined_content = read_source_map(name, toplevel);
if (inlined_content) {
options.sourceMap.orig[name] = parse_source_map(inlined_content);
}
} else if (source_map_content) {
options.sourceMap.orig[name] = source_map_content;
}
}
}
@@ -202,12 +203,7 @@ function minify(files, options) {
}
if (!HOP(options.output, "code") || options.output.code) {
if (options.sourceMap) {
options.output.source_map = SourceMap({
content: options.sourceMap.includeSources,
file: options.sourceMap.filename,
orig: source_maps,
root: options.sourceMap.root
});
options.output.source_map = SourceMap(options.sourceMap);
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
@@ -259,5 +255,7 @@ function minify(files, options) {
return result;
} catch (ex) {
return { error: ex };
} finally {
AST_Node.disable_validation();
}
}

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -111,7 +111,7 @@
var args = {
start : my_start_token(key),
end : my_end_token(M.value),
key : key.type == "Identifier" ? key.name : key.value,
key : "" + key[key.type == "Identifier" ? "name" : "value"],
value : from_moz(M.value)
};
if (M.kind == "init") return new AST_ObjectKeyVal(args);
@@ -212,7 +212,14 @@
end : my_end_token(M),
name : M.name
});
}
},
ThisExpression: function(M) {
return new AST_This({
start : my_start_token(M),
end : my_end_token(M),
name : "this",
});
},
};
MOZ_TO_ME.UpdateExpression =
@@ -245,7 +252,6 @@
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
map("CatchClause", AST_Catch, "param>argname, body%body");
map("ThisExpression", AST_This);
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
@@ -407,6 +413,10 @@
};
});
def_to_moz(AST_This, function To_Moz_ThisExpression() {
return { type: "ThisExpression" };
});
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var flags = M.value.toString().match(/[gimuy]*$/)[0];
var value = "/" + M.value.raw_source + "/" + flags;

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -783,8 +783,8 @@ function OutputStream(options) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.value;
// https://github.com/mishoo/UglifyJS2/issues/115
// https://github.com/mishoo/UglifyJS2/pull/1009
// https://github.com/mishoo/UglifyJS/issues/115
// https://github.com/mishoo/UglifyJS/pull/1009
if (value < 0 || /^0/.test(make_num(value))) {
return true;
}
@@ -1149,7 +1149,7 @@ function OutputStream(options) {
function parenthesize_for_noin(node, output, noin) {
var parens = false;
// need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60
// https://github.com/mishoo/UglifyJS/issues/60
if (noin) node.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
@@ -1470,7 +1470,6 @@ function OutputStream(options) {
AST_Node,
// since the label symbol will mark it
AST_LabeledStatement,
AST_Toplevel,
], noop);
// XXX: I'm not exactly sure if we need it for all of these nodes,
@@ -1482,7 +1481,6 @@ function OutputStream(options) {
AST_Constant,
AST_Debugger,
AST_Definitions,
AST_Directive,
AST_Finally,
AST_Jump,
AST_Lambda,

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -948,7 +948,7 @@ function parse($TEXT, options) {
if (!(stat instanceof AST_IterationStatement)) {
// check for `continue` that refers to this label.
// those should be reported as syntax errors.
// https://github.com/mishoo/UglifyJS2/issues/287
// https://github.com/mishoo/UglifyJS/issues/287
label.references.forEach(function(ref) {
if (ref instanceof AST_Continue) {
ref = ref.label.start;

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -43,23 +43,21 @@
"use strict";
function SymbolDef(scope, orig, init) {
function SymbolDef(id, scope, orig, init) {
this.eliminated = 0;
this.global = false;
this.id = id;
this.init = init;
this.lambda = orig instanceof AST_SymbolLambda;
this.mangled_name = null;
this.name = orig.name;
this.orig = [ orig ];
this.init = init;
this.eliminated = 0;
this.scope = scope;
this.references = [];
this.replaced = 0;
this.global = false;
this.mangled_name = null;
this.scope = scope;
this.undeclared = false;
this.id = SymbolDef.next_id++;
this.lambda = orig instanceof AST_SymbolLambda;
}
SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
return this.global && !options.toplevel
@@ -151,6 +149,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
scope.def_variable(node).defun = defun;
}
});
self.next_def_id = 0;
self.walk(tw);
// pass 2: find back references and eval
@@ -240,12 +239,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
});
AST_Scope.DEFMETHOD("make_def", function(orig, init) {
var top = this;
while (top.parent_scope) top = top.parent_scope;
return new SymbolDef(++top.next_def_id, this, orig, init);
});
AST_Toplevel.DEFMETHOD("def_global", function(node) {
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, node);
var g = this.make_def(node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
@@ -310,7 +315,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
def.orig.push(symbol);
if (def.init instanceof AST_Function) def.init = init;
} else {
def = new SymbolDef(this, symbol, init);
def = this.make_def(symbol, init);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
}

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -90,14 +90,8 @@ function create_array_map() {
}
function SourceMap(options) {
options = defaults(options, {
content: false,
file: null,
root: null,
orig: null,
}, true);
var sources = create_array_map();
var sources_content = options.content && Object.create(null);
var sources_content = options.includeSources && Object.create(null);
var names = create_array_map();
var mappings = "";
if (options.orig) Object.keys(options.orig).forEach(function(name) {
@@ -119,6 +113,7 @@ function SourceMap(options) {
if (content) sources_content[map.sources[i]] = content;
}
});
var prev_source;
var generated_line = 1;
var generated_column = 0;
var source_index = 0;
@@ -137,11 +132,14 @@ function SourceMap(options) {
if (orig_col >= col) indices = segments[i];
if (orig_col <= col) break;
}
if (!indices || indices.length < 4) return;
source = map.sources[indices[1]];
orig_line = indices[2];
orig_col = indices[3];
if (indices.length > 4) name = map.names[indices[4]];
if (!indices || indices.length < 4) {
source = null;
} else {
source = map.sources[indices[1]];
orig_line = indices[2];
orig_col = indices[3];
if (indices.length > 4) name = map.names[indices[4]];
}
}
add(source, gen_line, gen_col, orig_line, orig_col, name);
} : add,
@@ -151,7 +149,7 @@ function SourceMap(options) {
toString: function() {
return JSON.stringify({
version: 3,
file: options.file || undefined,
file: options.filename || undefined,
sourceRoot: options.root || undefined,
sources: sources,
sourcesContent: sources_content ? sources.map(function(source) {
@@ -164,6 +162,8 @@ function SourceMap(options) {
};
function add(source, gen_line, gen_col, orig_line, orig_col, name) {
if (prev_source == null && source == null) return;
prev_source = source;
if (generated_line < gen_line) {
generated_column = 0;
do {
@@ -174,6 +174,7 @@ function SourceMap(options) {
}
mappings += vlq_encode(gen_col - generated_column);
generated_column = gen_col;
if (source == null) return;
var src_idx = sources.index(source);
mappings += vlq_encode(src_idx - source_index);
source_index = src_idx;
@@ -181,7 +182,7 @@ function SourceMap(options) {
original_line = orig_line;
mappings += vlq_encode(orig_col - original_column);
original_column = orig_col;
if (name != null) {
if (options.names && name != null) {
var name_idx = names.index(name);
mappings += vlq_encode(name_idx - name_index);
name_index = name_idx;

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------

View File

@@ -1,7 +1,7 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
https://github.com/mishoo/UglifyJS
-------------------------------- (C) ---------------------------------
@@ -87,15 +87,13 @@ DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError);
function defaults(args, defs, croak) {
if (args === true) args = {};
var ret = args || {};
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) {
throw new DefaultsError("`" + i + "` is not a supported option", defs);
if (croak) for (var i in args) {
if (HOP(args, i) && !HOP(defs, i)) throw new DefaultsError("`" + i + "` is not a supported option", defs);
}
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
for (var i in args) {
if (HOP(args, i)) defs[i] = args[i];
}
return ret;
return defs;
}
function merge(obj, ext) {
@@ -149,6 +147,9 @@ var List = (function() {
}
return top.concat(ret);
}
List.is_op = function(val) {
return val === skip || val instanceof AtTop || val instanceof Last || val instanceof Splice;
};
List.at_top = function(val) { return new AtTop(val); };
List.splice = function(val) { return new Splice(val); };
List.last = function(val) { return new Last(val); };

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.9.1",
"version": "3.9.3",
"engines": {
"node": ">=0.8.0"
},
@@ -11,7 +11,7 @@
"Alex Lam <alexlamsl@gmail.com>",
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
],
"repository": "mishoo/UglifyJS2",
"repository": "mishoo/UglifyJS",
"main": "tools/node.js",
"bin": {
"uglifyjs": "bin/uglifyjs"

View File

@@ -8,9 +8,9 @@ var fetch = require("./fetch");
var spawn = require("child_process").spawn;
var zlib = require("zlib");
var args = process.argv.slice(2);
args.unshift("bin/uglifyjs");
if (!args.length) args.push("-mc");
args.push("--timings");
args.unshift("bin/uglifyjs");
args.push("--validate", "--timings");
var urls = [
"https://code.jquery.com/jquery-3.4.1.js",
"https://code.angularjs.org/1.7.8/angular.js",

View File

@@ -63,7 +63,7 @@ function make_code(ast, options) {
function parse_test(file) {
var script = fs.readFileSync(file, "utf8");
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS/issues/348
try {
var ast = U.parse(script, {
filename: file
@@ -188,6 +188,7 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
}
});
var options_formatted = JSON.stringify(options, null, 4);
options.validate = true;
var result = U.minify(input_code, options);
if (result.error) {
log([
@@ -251,6 +252,7 @@ function run_code(code, toplevel) {
function test_case(test) {
log(" Running test [{name}]", { name: test.name });
U.AST_Node.enable_validation();
var output_options = test.beautify || {};
var expect;
if (test.expect) {

View File

@@ -91,8 +91,7 @@ asm_mixed: {
function no_asm_GeometricMean(stdlib, foreign, buffer) {
function logSum(start, end) {
start |= 0, end |= 0;
var sum = 0, p = 0, q = 0;
for (p = start << 3, q = end << 3; (0 | p) < (0 | q); p = p + 8 | 0) sum += +log(values[p >> 3]);
for (var sum = 0, p = 0, q = 0, p = start << 3, q = end << 3; (0 | p) < (0 | q); p = p + 8 | 0) sum += +log(values[p >> 3]);
return +sum;
}
function geometricMean(start, end) {

View File

@@ -803,8 +803,7 @@ collapse_vars_assignment: {
expect: {
function log(x) { return console.log(x), x; }
function f0(c) {
var a = 3 / c;
return a = a;
return 3 / c;
}
function f1(c) {
return 1 - 3 / c;
@@ -2012,6 +2011,7 @@ issue_1631_3: {
join_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function g() {
@@ -2031,8 +2031,8 @@ issue_1631_3: {
function f() {
return a = 2, 4;
}
var a = 0, b = 1, t = f();
return b = a + t;
var a = 0, t = f();
return a + t;
}
console.log(g());
}
@@ -2204,8 +2204,8 @@ var_defs: {
}
expect: {
var f1 = function(x, y) {
var r = x + y, a = r * r - r, b = 7;
console.log(a + b);
var r = x + y, z = r * r - r, b = 7;
console.log(z + b);
};
f1("1", 0);
}
@@ -2569,8 +2569,7 @@ chained_3: {
}
expect: {
console.log(function(a, b) {
var c = 1;
c = b;
var c = 1, c = b;
b++;
return c;
}(0, 2));
@@ -2665,8 +2664,8 @@ double_def_1: {
a();
}
expect: {
var a;
(a = (a = x) && y)();
var a = x;
(a = a && y)();
}
}
@@ -2699,8 +2698,8 @@ toplevel_single_reference: {
}
expect: {
for (var b in x) {
var a;
b(a = b);
var a = b;
b(b);
}
}
}
@@ -4243,8 +4242,7 @@ issue_2497: {
if (true)
for (var i = 0; i < 1; ++i)
for (var k = 0; k < 1; ++k) {
value = 1;
value = value ? value + 1 : 0;
value = (value = 1) ? value + 1 : 0;
}
else
for (i = 0; i < 1; ++i)
@@ -7806,3 +7804,214 @@ issue_3744: {
}
expect_stdout: "PASS"
}
assign_value_def: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a) {
while (1) {
var b = a[0], c = a[1];
d = b;
e = c;
if (c[0] - e[0] > c[1] - d[1]) break;
return "PASS";
}
var d, e;
return "FAIL";
}
console.log(f([
[ 1, 2 ],
[ 3, 4 ],
]));
}
expect: {
function f(a) {
while (1) {
var b = a[0], c = a[1];
if (c[0] - c[0] > c[1] - b[1]) break;
return "PASS";
}
return "FAIL";
}
console.log(f([
[ 1, 2 ],
[ 3, 4 ],
]));
}
expect_stdout: "PASS"
}
join_vars_value_def: {
options = {
collapse_vars: true,
join_vars: true,
unused: true,
}
input: {
function f(a) {
while (1) {
var b = a[0], c = a[1];
d = b;
e = c;
if (c[0] - e[0] > c[1] - d[1]) break;
return "PASS";
}
var d, e;
return "FAIL";
}
console.log(f([
[ 1, 2 ],
[ 3, 4 ],
]));
}
expect: {
function f(a) {
while (1) {
var b = a[0], c = a[1];
if (c[0] - c[0] > c[1] - b[1]) break;
return "PASS";
}
return "FAIL";
}
console.log(f([
[ 1, 2 ],
[ 3, 4 ],
]));
}
expect_stdout: "PASS"
}
var_value_def: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a) {
while (1) {
var b = a[0], c = a[1], d = b, e = c;
if (c[0] - e[0] > c[1] - d[1]) break;
return "PASS";
}
var d, e;
return "FAIL";
}
console.log(f([
[ 1, 2 ],
[ 3, 4 ],
]));
}
expect: {
function f(a) {
while (1) {
var b = a[0], c = a[1];
if (c[0] - c[0] > c[1] - b[1]) break;
return "PASS";
}
return "FAIL";
}
console.log(f([
[ 1, 2 ],
[ 3, 4 ],
]));
}
expect_stdout: "PASS"
}
mangleable_var: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a) {
var b = a(), c = a(), d = b;
return c.p(c, d);
}
console.log(f(function() {
return {
p: function() {
return "PASS"
},
};
}));
}
expect: {
function f(a) {
var b = a(), c = a();
return c.p(c, b);
}
console.log(f(function() {
return {
p: function() {
return "PASS";
}
};
}));
}
expect_stdout: "PASS"
}
issue_3884: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 100, b = 1;
{
a++ + a || a;
b <<= a;
}
console.log(a, b);
}
expect: {
var a = 100;
++a;
console.log(a, 32);
}
expect_stdout: "101 32"
}
issue_3891: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function log(a) {
console.log(typeof a);
}
log(function f() {
try {
do {
var b = function() {}();
} while (f = 0, b.p);
} catch (e) {
var f;
b;
}
});
}
expect: {
function log(a) {
console.log(typeof a);
}
log(function() {
try {
do {} while ((void 0).p);
} catch (e) {}
});
}
expect_stdout: "function"
}

View File

@@ -93,6 +93,36 @@ self_comparison_2: {
expect_stdout: "false true"
}
self_comparison_3: {
options = {
comparisons: true,
}
input: {
var a;
function f() {
var b = a;
a = null;
return b;
}
for (var i = 0; i < 2; i++)
console.log(f() === f());
}
expect: {
var a;
function f() {
var b = a;
a = null;
return b;
}
for (var i = 0; i < 2; i++)
console.log(f() === f());
}
expect_stdout: [
"false",
"true",
]
}
issue_2857_1: {
options = {
comparisons: true,

View File

@@ -1384,7 +1384,7 @@ hoist_decl: {
}
expect: {
var a, b;
x() ? y() : z();
(x() ? y : z)();
}
}
@@ -1666,3 +1666,130 @@ issue_3668: {
}
expect_stdout: "undefined"
}
conditional_assignments_1: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a, b, c, d) {
a = b;
if (c) a = d;
return a;
}
function g(a, b, c, d) {
a = b;
if (c); else a = d;
return a;
}
console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL"));
}
expect: {
function f(a, b, c, d) {
return a = c ? d : b, a;
}
function g(a, b, c, d) {
return a = c ? b : d, a;
}
console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL"));
}
expect_stdout: "PASS PASS"
}
conditional_assignments_2: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f1(b, c, d) {
a = b;
if (c) a = d;
return a;
}
function f2(a, c, d) {
a = b;
if (c) a = d;
return a;
}
function f3(a, b, d) {
a = b;
if (c) a = d;
return a;
}
function f4(a, b, c) {
a = b;
if (c) a = d;
return a;
}
}
expect: {
function f1(b, c, d) {
return a = c ? d : b, a;
}
function f2(a, c, d) {
return a = b, c && (a = d), a;
}
function f3(a, b, d) {
return a = b, c && (a = d), a;
}
function f4(a, b, c) {
return a = b, c && (a = d), a;
}
}
}
conditional_assignments_3: {
options = {
conditionals: true,
sequences: true,
}
input: {
console.log(function(a, b) {
a = "PASS";
if (b) a = a;
return a;
}(0, 1));
}
expect: {
console.log(function(a, b) {
return a = "PASS", b && (a = a), a;
}(0, 1));
}
expect_stdout: "PASS"
}
issue_3808_1: {
options = {
conditionals: true,
side_effects: true,
}
input: {
var a;
a = "PASS", [] + "" && (a = "FAIL");
console.log(a);
}
expect: {
var a;
a = [] + "" ? "FAIL" : "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3808_2: {
options = {
conditionals: true,
side_effects: true,
}
input: {
var a;
console.log((a = "PASS", [] + "" && (a = "FAIL")), a);
}
expect: {
var a;
console.log((a = "PASS", [] + "" && (a = "FAIL")), a);
}
expect_stdout: " PASS"
}

View File

@@ -97,6 +97,66 @@ dead_code_constant_boolean_should_warn_more: {
node_version: "<=4"
}
trim_try: {
options = {
dead_code: true,
}
input: {
try {
var a;
} catch (e) {
console.log("FAIL");
} finally {
console.log(a);
}
}
expect: {
var a;
console.log(a);
}
expect_stdout: "undefined"
}
trim_finally_1: {
options = {
dead_code: true,
}
input: {
try {
console.log("PASS");
} finally {
var a;
}
}
expect: {
console.log("PASS");
var a;
}
expect_stdout: "PASS"
}
trim_finally_2: {
options = {
dead_code: true,
}
input: {
try {
console.log("PASS");
} catch (e) {
} finally {
var a;
}
}
expect: {
try {
console.log("PASS");
var a;
} catch (e) {
}
}
expect_stdout: "PASS"
}
try_catch_finally: {
options = {
conditionals: true,
@@ -130,10 +190,7 @@ try_catch_finally: {
a = 3;
console.log("PASS");
}();
try {
console.log(a);
} finally {
}
console.log(a);
}
expect_stdout: [
"PASS",
@@ -892,3 +949,222 @@ issue_3578: {
}
expect_stdout: "PASS"
}
issue_3830_1: {
options = {
dead_code: true,
}
input: {
var o = {
set p(v) {
o = o.p = o = v;
}
};
o.p = "PASS";
console.log(o);
}
expect: {
var o = {
set p(v) {
o = o.p = o = v;
}
};
o.p = "PASS";
console.log(o);
}
expect_stdout: "PASS"
}
issue_3830_2: {
options = {
dead_code: true,
}
input: {
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[a] = a = v;
}
};
o[a] = "PASS";
console.log(a);
}
expect: {
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[a] = a = v;
}
};
o[a] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_3: {
options = {
dead_code: true,
}
input: {
function f() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[f()] = a = v;
}
};
o[f()] = "PASS";
console.log(a);
}
expect: {
function f() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[f()] = a = v;
}
};
o[f()] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_4: {
options = {
dead_code: true,
}
input: {
function f() {
return o;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[a] = a = v;
}
};
f()[a] = "PASS";
console.log(a);
}
expect: {
function f() {
return o;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[a] = a = v;
}
};
f()[a] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_5: {
options = {
dead_code: true,
}
input: {
function f() {
return o;
}
function g() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[g()] = a = v;
}
};
f()[g()] = "PASS";
console.log(a);
}
expect: {
function f() {
return o;
}
function g() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[g()] = a = v;
}
};
f()[g()] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_6: {
options = {
dead_code: true,
}
input: {
function f() {
return o;
}
function g() {
return a;
}
function h(v) {
a = f()[g()] = a = v;
}
var a = "FAIL";
var o = {
set FAIL(v) {
h(v);
}
};
o.FAIL = "PASS";
console.log(a);
}
expect: {
function f() {
return o;
}
function g() {
return a;
}
function h(v) {
a = f()[g()] = a = v;
}
var a = "FAIL";
var o = {
set FAIL(v) {
h(v);
}
};
o.FAIL = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
redundant_assignments: {
options = {
dead_code: true,
}
input: {
var a = a = "PASS", b = "FAIL";
b = b = "PASS";
console.log(a, b);
}
expect: {
var a = "PASS", b = "FAIL";
b = "PASS";
console.log(a, b);
}
expect_stdout: "PASS PASS"
}

View File

@@ -46,7 +46,7 @@ simple_statement_is_not_a_directive: {
drop_lone_use_strict: {
options = {
directives: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
@@ -66,10 +66,8 @@ drop_lone_use_strict: {
function f1() {
}
function f2() {
"use strict";
function f3() {
}
}
(function() {})();
}
}

View File

@@ -2366,7 +2366,7 @@ function_parameter_ie8: {
}
expect: {
(function() {
(function f() {
(function() {
console.log("PASS");
})();
})();
@@ -2444,3 +2444,91 @@ issue_3746: {
}
expect_stdout: "PASS"
}
drop_duplicated_side_effects: {
options = {
toplevel: true,
unused: true,
}
input: {
var a = 0;
for (var i = 1; i--;)
var a = 0, b = ++a;
console.log(a);
}
expect: {
var a = 0;
for (var i = 1; i--;)
a = 0, ++a;
console.log(a);
}
expect_stdout: "1"
}
drop_duplicated_var_catch: {
options = {
unused: true,
}
input: {
function f() {
try {
x();
} catch (a) {
var a, a;
}
}
}
expect: {
function f() {
try {
x();
} catch (a) {
var a;
}
}
}
}
issue_3802_1: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a += 0;
var a = function() {};
console.log(typeof a);
}
expect: {
var a = 0;
a += 0;
a = function() {};
console.log(typeof a);
}
expect_stdout: "function"
}
issue_3802_2: {
options = {
functions: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a += 0;
var a = function() {};
console.log(typeof a);
}
expect: {
0;
function a() {};
console.log(typeof a);
}
expect_stdout: "function"
}

View File

@@ -2190,3 +2190,231 @@ issue_3755: {
}
expect_stdout: "undefined"
}
void_side_effects: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = void console.log("PASS");
console.log(a);
}
expect: {
console.log("PASS");
console.log(void 0);
}
expect_stdout: [
"PASS",
"undefined",
]
}
no_returns: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect: {
(function() {
console.log("PASS");
})();
console.log(void 0);
}
expect_stdout: [
"PASS",
"undefined",
]
}
void_returns: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
function g(b) {
if (b) console.log("FAIL");
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g(e);
}
}
}();
console.log(a);
}
expect: {
(function() {
function g(b) {
if (b) console.log("FAIL");
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g(e);
}
}
})();
console.log(void 0);
}
expect_stdout: [
"PASS",
"undefined",
]
}
void_returns_recursive: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
function g(b) {
return f();
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g(e);
}
}
}();
console.log(a);
}
expect: {
var a = function f() {
function g(b) {
return f();
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g();
}
}
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
issue_3878_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var b = function(a) {
return (a = 0) == (a && this > (a += 0));
}();
console.log(b ? "PASS" : "FAIL");
}
expect: {
var b = function(a) {
return (a = 0) == (a && this > (a += 0));
}();
console.log(b ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_3878_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = "foo";
a++ + a;
a && a;
console.log(a);
}
expect: {
var a = "foo";
a++ + a;
a;
console.log(a);
}
expect_stdout: "NaN"
}
issue_3882: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
return console.log(a++), a && this;
}
var b = f();
console.log(b);
}
expect: {
var b = function(a) {
return console.log(a++), a && this;
}();
console.log(b);
}
expect_stdout: [
"NaN",
"NaN",
]
}
issue_3887: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function(b) {
try {
b-- && console.log("PASS");
} catch (a_2) {}
})(1);
}
expect: {
(function(b) {
try {
b-- && console.log("PASS");
} catch (a_2) {}
})(1);
}
expect_stdout: "PASS"
}

View File

@@ -2877,7 +2877,7 @@ issue_2437: {
}
}
issue_2485: {
issue_2485_1: {
options = {
functions: true,
reduce_funcs: true,
@@ -2926,6 +2926,54 @@ issue_2485: {
expect_stdout: "6"
}
issue_2485_2: {
options = {
functions: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
var foo = function(bar) {
var n = function(a, b) {
return a + b;
};
var sumAll = function(arg) {
return arg.reduce(n, 0);
};
var runSumAll = function(arg) {
return sumAll(arg);
};
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect: {
var foo = function(bar) {
function n(a, b) {
return a + b;
}
function runSumAll(arg) {
return arg.reduce(n, 0);
}
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect_stdout: "6"
}
issue_3364: {
options = {
functions: true,
@@ -3113,9 +3161,7 @@ issue_3400_1: {
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
return [ 42 ].map(h);
}
return e();
}()[0].p);
@@ -3130,7 +3176,7 @@ issue_3400_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
unused: true,
@@ -3158,11 +3204,11 @@ issue_3400_2: {
}
expect: {
void console.log(function g() {
return [ 42 ].map(function(v) {
return o = {
p: v
}, console.log(o[g]), o;
var o;
return [ 42 ].map(function(u) {
var o = {
p: u
};
return console.log(o[g]), o;
});
}()[0].p);
}
@@ -3468,10 +3514,9 @@ hoisted_single_use: {
}
expect: {
function f(a) {
for (var r in a) g(r);
}
function g(a) {
console.log(a);
for (var r in a) (function(a) {
console.log(a);
})(r);
}
(function(a) {
var g = a.bar;
@@ -3568,8 +3613,8 @@ pr_3592_2: {
var g = [ "PASS" ];
console.log((z = "PASS", function(problem) {
return g[problem];
}((y = z, problem(y)))));
var z, y;
}(problem(z))));
var z;
}
expect_stdout: "PASS"
}
@@ -3635,9 +3680,7 @@ pr_3595_1: {
}
console.log((arg = "PASS", function(problem) {
return g[problem];
}(function(arg) {
return problem(arg);
}(arg))));
}(problem(arg))));
var arg;
}
expect_stdout: "PASS"
@@ -3678,9 +3721,7 @@ pr_3595_2: {
}
console.log(function(problem) {
return g[problem];
}(function(arg) {
return problem(arg);
}("PASS")));
}(problem("PASS")));
}
expect_stdout: "PASS"
}
@@ -4062,3 +4103,580 @@ issue_3777_2: {
}
expect_stdout: "PASS"
}
issue_3821_1: {
options = {
inline: true,
}
input: {
var a = 0;
console.log(function(b) {
return +a + b;
}(--a));
}
expect: {
var a = 0;
console.log(function(b) {
return +a + b;
}(--a));
}
expect_stdout: "-2"
}
issue_3821_2: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = "PASS";
function f(g, b) {
return g(), b;
}
console.log(f(function() {
a = "FAIL";
}, a));
}
expect: {
var a = "PASS";
function f(g, b) {
return g(), b;
}
console.log(f(function() {
a = "FAIL";
}, a));
}
expect_stdout: "PASS"
}
substitute: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {};
function f(a) {
return a === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect: {
var o = {};
function f(a) {
return a === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return f;
},
function() {
"use strict";
return f;
},
function() {
return f;
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect_stdout: [
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 2",
]
}
substitute_add_farg: {
options = {
inline: true,
keep_fargs: "strict",
}
input: {
function f(g) {
console.log(g.length);
g(null, "FAIL");
}
f(function() {
return function(a, b) {
return function(c) {
do {
console.log("PASS");
} while (c);
}(a, b);
};
}());
}
expect: {
function f(g) {
console.log(g.length);
g(null, "FAIL");
}
f(function(c, argument_1) {
do {
console.log("PASS");
} while (c);
});
}
expect_stdout: [
"2",
"PASS",
]
}
substitute_arguments: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {};
function f(a) {
return arguments[0] === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect: {
var o = {};
function f(a) {
return arguments[0] === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect_stdout: [
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 2",
]
}
substitute_drop_farg: {
options = {
inline: true,
keep_fargs: false,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {};
function f(a) {
return a === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o));
});
}
expect: {
var o = {};
function f(a) {
return a === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return f;
},
function() {
"use strict";
return f;
},
function() {
return f;
},
function() {
return f;
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o));
});
}
expect_stdout: [
"PASS PASS",
"PASS PASS",
"PASS PASS",
"PASS PASS",
"PASS PASS",
]
}
substitute_this: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {};
function f(a) {
return a === o ? this === o : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect: {
var o = {};
function f(a) {
return a === o ? this === o : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect_stdout: [
"false true 1",
"false false 1",
"false false 1",
"false false 1",
"false false 2",
]
}
substitute_use_strict: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {};
function f(a) {
"use strict";
return a === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return function(b) {
return f(b);
};
},
function() {
"use strict";
return function(c) {
return f(c);
};
},
function() {
return function(c) {
"use strict";
return f(c);
};
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect: {
var o = {};
function f(a) {
"use strict";
return a === o ? "PASS" : "FAIL";
}
[
function() {
return f;
},
function() {
return f;
},
function() {
"use strict";
return f;
},
function() {
return f;
},
function() {
return function(d, e) {
return f(d, e);
};
},
].forEach(function(g) {
console.log(g()(o), g().call(o, o), g().length);
});
}
expect_stdout: [
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 1",
"PASS PASS 2",
]
}
issue_3833: {
options = {
inline: true,
keep_fargs: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
return function() {
while (a);
console.log("PASS");
}();
}
f();
}
expect: {
(function() {
while (a);
console.log("PASS");
})();
var a;
}
expect_stdout: "PASS"
}
issue_3835: {
options = {
inline: true,
reduce_vars: true,
}
input: {
(function f() {
return function() {
return f();
}();
})();
}
expect: {
(function f() {
return f();
})();
}
expect_stdout: true
}
issue_3836: {
options = {
inline: true,
}
input: {
(function() {
return function() {
for (var a in 0)
console.log(k);
}(console.log("PASS"));
})();
}
expect: {
(function() {
for (var a in 0)
console.log(k);
})(console.log("PASS"));
}
expect_stdout: "PASS"
}
issue_3852: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
console.log(function(a) {
return function(b) {
return b && (b[0] = 0), "PASS";
}(a);
}(42));
}
expect: {
console.log(function(a) {
return a && (a[0] = 0), "PASS";
}(42));
}
expect_stdout: "PASS"
}

View File

@@ -767,18 +767,17 @@ issue_3071_1: {
var obj = {};
obj.one = 1;
obj.two = 2;
console.log(obj.one);
console.log(obj.one, obj.two);
})();
}
expect: {
console.log(1);
console.log(1, 2);
}
expect_stdout: "1"
expect_stdout: "1 2"
}
issue_3071_2: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
join_vars: true,
@@ -793,19 +792,18 @@ issue_3071_2: {
obj = {};
obj.one = 1;
obj.two = 2;
console.log(obj.one);
console.log(obj.one, obj.two);
var obj;
})();
}
expect: {
console.log(1);
console.log(1, 2);
}
expect_stdout: "1"
expect_stdout: "1 2"
}
issue_3071_2_toplevel: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
join_vars: true,
@@ -821,14 +819,14 @@ issue_3071_2_toplevel: {
obj = {};
obj.one = 1;
obj.two = 2;
console.log(obj.one);
console.log(obj.one, obj.two);
var obj;
})();
}
expect: {
console.log(1);
console.log(1, 2);
}
expect_stdout: "1"
expect_stdout: "1 2"
}
issue_3071_3: {
@@ -914,3 +912,63 @@ issue_3440: {
}
expect_stdout: "PASS"
}
issue_3868: {
options = {
hoist_props: true,
passes: 2,
reduce_vars: true,
side_effects: true,
}
input: {
(function(t) {
t = {};
({
get p() {},
q: (console.log("PASS"), +t),
}).r;
})();
}
expect: {
(function(t) {
t = {};
({
get p() {},
q: (console.log("PASS"), +t),
}).r;
})();
}
expect_stdout: "PASS"
}
issue_3871: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
console.log(function() {
do {
var b = {
get null() {
c;
}
};
} while (!b);
return "PASS";
}());
}
expect: {
console.log(function() {
do {
var b = {
get null() {
c;
}
};
} while (!b);
return "PASS";
}());
}
expect_stdout: "PASS"
}

View File

@@ -2389,7 +2389,7 @@ issue_3703: {
var a = "PASS";
(function() {
var b;
var c = function g() {
var c = function() {
a = "FAIL";
};
a ? b |= c : b.p;
@@ -2420,3 +2420,72 @@ issue_3750: {
}
expect_stdout: "PASS"
}
issue_3823: {
options = {
ie8: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 1; i++) {
var a = a ? function f() {
f;
} : 0;
console.log("PASS", typeof f);
}
}
expect: {
for (var i = 0; i < 1; i++) {
(function f() {
f;
});
console.log("PASS", typeof f);
}
}
expect_stdout: "PASS undefined"
}
issue_3825: {
options = {
ie8: true,
pure_getters: "strict",
side_effects: true,
}
input: {
console.log({}[void (0..length ? 1 : 2)]);
}
expect: {
console.log({}[void 0]);
}
expect_stdout: "undefined"
}
issue_3889: {
options = {
evaluate: true,
ie8: true,
reduce_vars: true,
}
input: {
function f(a) {
a = 0;
(function a() {
var a;
console.log(a);
})();
}
f();
}
expect: {
function f(a) {
a = 0;
(function a() {
var a;
console.log(a);
})();
}
f();
}
expect_stdout: "undefined"
}

View File

@@ -573,3 +573,24 @@ issue_3600: {
}
expect_stdout: "1"
}
iife_if_return_simple: {
options = {
conditionals: true,
if_return: true,
inline: true,
sequences: true,
side_effects: true,
}
input: {
(function() {
if (console)
return console.log("PASS");
console.log("FAIL");
})();
}
expect: {
console ? console.log("PASS") : console.log("FAIL");
}
expect_stdout: "PASS"
}

View File

@@ -90,13 +90,13 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
"INFO: pass 0: last_count: Infinity, count: 37",
"INFO: pass 0: last_count: Infinity, count: 36",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
"INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
"INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
"INFO: pass 1: last_count: 37, count: 18",
"INFO: pass 1: last_count: 36, count: 18",
]
}
@@ -248,13 +248,13 @@ non_hoisted_function_after_return_2a_strict: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
"INFO: pass 0: last_count: Infinity, count: 48",
"INFO: pass 0: last_count: Infinity, count: 47",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
"INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
"INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
"INFO: pass 1: last_count: 48, count: 29",
"INFO: pass 1: last_count: 47, count: 29",
]
}

View File

@@ -22,8 +22,9 @@ issue_1639_1: {
console.log(a, b);
}
expect: {
for (var a = 100, b = 10, L1 = 5; --L1 > 0;)
if (--b, 0) var ignore = 0;
for (var a = 100, b = 10, L1 = 5, ignore; --L1 > 0;) {
--b;
}
console.log(a, b);
}
expect_stdout: "100 6"

View File

@@ -35,11 +35,7 @@ f7: {
console.log(a, b);
}
expect_exact: [
"var b = 10;",
"",
"!function() {",
" b = 100;",
"}(), console.log(100, b);",
"console.log(100, 100);",
]
expect_stdout: true
expect_stdout: "100 100"
}

1025
test/compress/join_vars.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1226,8 +1226,7 @@ chained_3: {
}
expect: {
console.log(function(b) {
var c = 1;
c = b;
var c = 1, c = b;
b++;
return c;
}(2));

View File

@@ -3,6 +3,7 @@ labels_1: {
conditionals: true,
dead_code: true,
if_return: true,
unused: true,
}
input: {
out: {
@@ -21,6 +22,7 @@ labels_2: {
conditionals: true,
dead_code: true,
if_return: true,
unused: true,
}
input: {
out: {
@@ -61,6 +63,7 @@ labels_4: {
conditionals: true,
dead_code: true,
if_return: true,
unused: true,
}
input: {
out: for (var i = 0; i < 5; ++i) {
@@ -105,6 +108,9 @@ labels_5: {
}
labels_6: {
options = {
dead_code: true,
}
input: {
out: break out;
};
@@ -159,6 +165,7 @@ labels_9: {
conditionals: true,
dead_code: true,
if_return: true,
unused: true,
}
input: {
out: while (foo) {

View File

@@ -549,6 +549,7 @@ issue_2740_2: {
dead_code: true,
loops: true,
passes: 2,
unused: true,
}
input: {
L1: while (x()) {
@@ -564,6 +565,7 @@ issue_2740_3: {
options = {
dead_code: true,
loops: true,
unused: true,
}
input: {
L1: for (var x = 0; x < 3; x++) {
@@ -589,6 +591,7 @@ issue_2740_4: {
dead_code: true,
loops: true,
passes: 2,
unused: true,
}
input: {
L1: for (var x = 0; x < 3; x++) {
@@ -613,6 +616,7 @@ issue_2740_5: {
dead_code: true,
loops: true,
passes: 2,
unused: true,
}
input: {
L1: for (var x = 0; x < 3; x++) {
@@ -763,6 +767,7 @@ issue_3631_1: {
loops: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var c = 0;
@@ -789,6 +794,7 @@ issue_3631_2: {
loops: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
L: for (var a = 1; a--; console.log(b)) {

View File

@@ -1143,558 +1143,6 @@ const_prop_assign_pure: {
}
}
join_object_assignments_1: {
options = {
evaluate: true,
join_vars: true,
}
input: {
console.log(function() {
var x = {
a: 1,
c: (console.log("c"), "C"),
};
x.b = 2;
x[3] = function() {
console.log(x);
},
x["a"] = /foo/,
x.bar = x;
return x;
}());
}
expect: {
console.log(function() {
var x = {
a: 1,
c: (console.log("c"), "C"),
b: 2,
3: function() {
console.log(x);
},
a: /foo/,
};
x.bar = x;
return x;
}());
}
expect_stdout: true
}
join_object_assignments_2: {
options = {
evaluate: true,
hoist_props: true,
join_vars: true,
passes: 3,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
foo: 1,
};
o.bar = 2;
o.baz = 3;
console.log(o.foo, o.bar + o.bar, o.foo * o.bar * o.baz);
}
expect: {
console.log(1, 4, 6);
}
expect_stdout: "1 4 6"
}
join_object_assignments_3: {
options = {
evaluate: true,
join_vars: true,
}
input: {
console.log(function() {
var o = {
a: "PASS",
}, a = o.a;
o.a = "FAIL";
return a;
}());
}
expect: {
console.log(function() {
var o = {
a: "PASS",
}, a = o.a;
o.a = "FAIL";
return a;
}());
}
expect_stdout: "PASS"
}
join_object_assignments_4: {
options = {
join_vars: true,
sequences: true,
}
input: {
var o;
console.log(o);
o = {};
o.a = "foo";
console.log(o.b);
o.b = "bar";
console.log(o.a);
}
expect: {
var o;
console.log(o),
o = {
a: "foo",
},
console.log(o.b),
o.b = "bar",
console.log(o.a);
}
expect_stdout: [
"undefined",
"undefined",
"foo",
]
}
join_object_assignments_return_1: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = "foo";
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: "foo"
};
return o.q;
}());
}
expect_stdout: "foo"
}
join_object_assignments_return_2: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = /foo/,
o.r = "bar";
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: /foo/,
r: "bar"
};
return o.r;
}());
}
expect_stdout: "bar"
}
join_object_assignments_return_3: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = "foo",
o.p += "",
console.log(o.q),
o.p;
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: "foo"
};
return o.p += "",
console.log(o.q),
o.p;
}());
}
expect_stdout: [
"foo",
"3",
]
}
join_object_assignments_for: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
for (o.q = "foo"; console.log(o.q););
return o.p;
}());
}
expect: {
console.log(function() {
for (var o = {
p: 3,
q: "foo"
}; console.log(o.q););
return o.p;
}());
}
expect_stdout: [
"foo",
"3",
]
}
join_object_assignments_if: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {};
if (o.a = "PASS") return o.a;
}())
}
expect: {
console.log(function() {
var o = { a: "PASS" };
if (o.a) return o.a;
}());
}
expect_stdout: "PASS"
}
join_object_assignments_forin: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {};
for (var a in o.a = "PASS", o)
return o[a];
}())
}
expect: {
console.log(function() {
var o = { a: "PASS" };
for (var a in o)
return o[a];
}());
}
expect_stdout: "PASS"
}
join_object_assignments_negative: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[0] = 0;
o[-0] = 1;
o[-1] = 2;
console.log(o[0], o[-0], o[-1]);
}
expect: {
var o = {
0: 0,
0: 1,
"-1": 2
};
console.log(o[0], o[-0], o[-1]);
}
expect_stdout: "1 1 2"
}
join_object_assignments_NaN_1: {
options = {
join_vars: true,
}
input: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect_stdout: "2 2"
}
join_object_assignments_NaN_2: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect: {
var o = {
NaN: 1,
NaN: 2
};
console.log(o.NaN, o.NaN);
}
expect_stdout: "2 2"
}
join_object_assignments_null_0: {
options = {
join_vars: true,
}
input: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect_stdout: "1"
}
join_object_assignments_null_1: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect: {
var o = {
null: 1
};
console.log(o.null);
}
expect_stdout: "1"
}
join_object_assignments_void_0: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var o = {};
o[void 0] = 1;
console.log(o[void 0]);
}
expect: {
var o = {
undefined: 1
};
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_undefined_1: {
options = {
join_vars: true,
}
input: {
var o = {};
o[undefined] = 1;
console.log(o[undefined]);
}
expect: {
var o = {};
o[void 0] = 1;
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_undefined_2: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[undefined] = 1;
console.log(o[undefined]);
}
expect: {
var o = {
undefined : 1
};
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_Infinity: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[Infinity] = 1;
o[1/0] = 2;
o[-Infinity] = 3;
o[-1/0] = 4;
console.log(o[Infinity], o[1/0], o[-Infinity], o[-1/0]);
}
expect: {
var o = {
Infinity: 1,
Infinity: 2,
"-Infinity": 3,
"-Infinity": 4
};
console.log(o[1/0], o[1/0], o[-1/0], o[-1/0]);
}
expect_stdout: "2 2 4 4"
}
join_object_assignments_regex: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[/rx/] = 1;
console.log(o[/rx/]);
}
expect: {
var o = {
"/rx/": 1
};
console.log(o[/rx/]);
}
expect_stdout: "1"
}
issue_2816: {
options = {
join_vars: true,
}
input: {
"use strict";
var o = {
a: 1
};
o.b = 2;
o.a = 3;
o.c = 4;
console.log(o.a, o.b, o.c);
}
expect: {
"use strict";
var o = {
a: 1,
b: 2
};
o.a = 3;
o.c = 4;
console.log(o.a, o.b, o.c);
}
expect_stdout: "3 2 4"
}
issue_2893_1: {
options = {
join_vars: true,
}
input: {
var o = {
get a() {
return "PASS";
},
};
o.a = "FAIL";
console.log(o.a);
}
expect: {
var o = {
get a() {
return "PASS";
},
};
o.a = "FAIL";
console.log(o.a);
}
expect_stdout: "PASS"
}
issue_2893_2: {
options = {
join_vars: true,
}
input: {
var o = {
set a(v) {
this.b = v;
},
b: "FAIL",
};
o.a = "PASS";
console.log(o.b);
}
expect: {
var o = {
set a(v) {
this.b = v;
},
b: "FAIL",
};
o.a = "PASS";
console.log(o.b);
}
expect_stdout: "PASS"
}
issue_869_1: {
mangle = {
properties: {
@@ -1856,36 +1304,6 @@ issue_3188_3: {
expect_stdout: "PASS"
}
join_expr: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var c = "FAIL";
(function() {
var a = 0;
switch ((a = {}) && (a.b = 0)) {
case 0:
c = "PASS";
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
var a = 0;
switch (a = { b: 0 }, a.b) {
case 0:
c = "PASS";
}
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3389: {
options = {
evaluate: true,

View File

@@ -680,3 +680,130 @@ issue_3325_2: {
}
expect_stdout: "PASS"
}
issue_3858: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: "strict",
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {
var f = function(a) {
return function() {
console.log(a);
}();
};
f("PASS");
}
expect_stdout: "PASS"
}
inline_pure_call_1: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {}
}
inline_pure_call_2: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
}
expect: {}
}
inline_pure_call_3: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
keep_fargs: "strict",
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
console.log(a);
}
expect: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
inline_pure_call_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = /*@__PURE__*/ function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect: {
var a = function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect_stdout: [
"PASS",
"42",
]
}

View File

@@ -223,6 +223,25 @@ unsafe_evaluate: {
expect_stdout: true
}
unsafe_evaluate_defun: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function() {
function f() {}
return ++f;
}());
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}
unsafe_evaluate_side_effect_free_1: {
options = {
evaluate: true,
@@ -1178,11 +1197,10 @@ toplevel_on_loops_1: {
while (x);
}
expect: {
function bar() {
console.log("bar:", --x);
}
var x = 3;
for (;bar(), x;);
for (;function() {
console.log("bar:", --x);
}(), x;);
}
expect_stdout: true
}
@@ -1235,10 +1253,9 @@ toplevel_on_loops_2: {
while (x);
}
expect: {
function bar() {
for (;;) (function() {
console.log("bar:");
}
for (;;) bar();
})();
}
}
@@ -4212,13 +4229,12 @@ issue_2450_4: {
}
expect: {
var a;
function f(b) {
console.log(a === b);
a = b;
}
function g() {}
for (var i = 3; --i >= 0;)
f(g);
(function(b) {
console.log(a === b);
a = b;
})(g);
}
expect_stdout: [
"false",
@@ -4319,14 +4335,13 @@ perf_1: {
console.log(sum);
}
expect: {
function indirect_foo(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}
var sum = 0;
for (var i = 0; i < 100; ++i)
sum += indirect_foo(i, i + 1, 3 * i);
sum += function(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}(i, i + 1, 3 * i);
console.log(sum);
}
expect_stdout: "348150"
@@ -4387,14 +4402,13 @@ perf_3: {
console.log(sum);
}
expect: {
var indirect_foo = function(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}
var sum = 0;
for (var i = 0; i < 100; ++i)
sum += indirect_foo(i, i + 1, 3 * i);
sum += function(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}(i, i + 1, 3 * i);
console.log(sum);
}
expect_stdout: "348150"
@@ -4456,14 +4470,13 @@ perf_5: {
console.log(sum);
}
expect: {
function indirect_foo(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}
var sum = 0;
for (var i = 0; i < 100; ++i)
sum += indirect_foo(i, i + 1, 3 * i);
sum += function(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}(i, i + 1, 3 * i);
console.log(sum);
}
expect_stdout: "348150"
@@ -4524,14 +4537,13 @@ perf_7: {
console.log(sum);
}
expect: {
var indirect_foo = function(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}
var sum = 0;
for (var i = 0; i < 100; ++i)
sum += indirect_foo(i, i + 1, 3 * i);
sum += function(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}(i, i + 1, 3 * i);
console.log(sum);
}
expect_stdout: "348150"
@@ -4570,7 +4582,7 @@ perf_8: {
expect_stdout: "348150"
}
issue_2485: {
issue_2485_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
@@ -4618,6 +4630,53 @@ issue_2485: {
expect_stdout: "6"
}
issue_2485_2: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
var foo = function(bar) {
var n = function(a, b) {
return a + b;
};
var sumAll = function(arg) {
return arg.reduce(n, 0);
};
var runSumAll = function(arg) {
return sumAll(arg);
};
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect: {
var foo = function(bar) {
var n = function(a, b) {
return a + b;
};
var runSumAll = function(arg) {
return arg.reduce(n, 0);
};
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect_stdout: "6"
}
issue_2455: {
options = {
reduce_vars: true,
@@ -5434,8 +5493,7 @@ lvalues_def_1: {
}
expect: {
var b = 1;
var a = b++;
b = NaN;
var a = b++, b = NaN;
console.log(a, b);
}
expect_stdout: "1 NaN"
@@ -5454,8 +5512,7 @@ lvalues_def_2: {
}
expect: {
var b = 1;
var a = b += 1;
b = NaN;
var a = b += 1, b = NaN;
console.log(a, b);
}
expect_stdout: "2 NaN"
@@ -6867,9 +6924,9 @@ issue_3666: {
} finally {
for (;!a;)
a++;
var b = a = "PASS";
a = "PASS";
}
console.log(a, b);
console.log(a, "PASS");
}
expect_stdout: "PASS PASS"
}
@@ -6912,3 +6969,102 @@ issue_3774: {
}
expect_stdout: "PASS"
}
flatten_iife: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL";
while (!console);
a++;
(function() {
while (!console);
a = "PASS";
})();
console.log(a);
}
expect: {
var a;
while (!console);
0;
(function() {
while (!console);
a = "PASS";
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_3844: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
console.log(function() {
if (!console) switch (A = 0) {
case console.log("FAIL"):
return;
}
return typeof A;
}());
}
expect: {
console.log(function() {
if (!console) switch (A = 0) {
case console.log("FAIL"):
return;
}
return typeof A;
}());
}
expect_stdout: "undefined"
}
issue_3866: {
options = {
dead_code: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
{
return "PASS";
var a = 0;
}
return --a;
}());
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_3880: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
while (a.var ^= 1);
console.log("PASS");
})(function() {});
}
expect: {
(function(a) {
while (a.var ^= 1);
console.log("PASS");
})(function() {});
}
expect_stdout: "PASS"
}

View File

@@ -69,6 +69,7 @@ label_if_break: {
dead_code: true,
evaluate: true,
side_effects: true,
unused: true,
}
input: {
L: if (true) {

View File

@@ -435,3 +435,25 @@ emberjs_global: {
}
expect_stdout: Error("PASS")
}
issue_3817: {
options = {
comparisons: true,
conditionals: true,
passes: 2,
typeofs: true,
}
input: {
if ("A" == typeof A || !console.log("PASS")) switch (false) {
case "undefined" == typeof A:
console.log("FAIL");
}
}
expect: {
if ("A" == typeof A || !console.log("PASS")) switch (false) {
case "undefined" == typeof A:
console.log("FAIL");
}
}
expect_stdout: "PASS"
}

View File

@@ -1,2 +1,41 @@
function _toConsumableArray(arr){if(Array.isArray(arr)){for(var i=0,arr2=Array(arr.length);i<arr.length;i++){arr2[i]=arr[i]}return arr2}else{return Array.from(arr)}}var _require=require("bar"),foo=_require.foo;var _require2=require("world"),hello=_require2.hello;foo.x.apply(foo,_toConsumableArray(foo.y(hello.z)));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImlucHV0Mi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCB7Zm9vfSA9IHJlcXVpcmUoXCJiYXJcIik7XG5jb25zdCB7aGVsbG99ID0gcmVxdWlyZShcIndvcmxkXCIpO1xuXG5mb28ueCguLi5mb28ueShoZWxsby56KSk7XG4iXSwibmFtZXMiOlsicmVxdWlyZSIsImFyciJdLCJtYXBwaW5ncyI6IjBKQUFjQSxLQUFRQyJ9
"use strict";
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) {
arr2[i] = arr[i];
}
return arr2;
}
var _require = require("bar"), foo = _require.foo;
var _require2 = require("world"), hello = _require2.hello;
foo.x.apply(foo, _toConsumableArray(foo.y(hello.z)));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImlucHV0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IHtmb299ID0gcmVxdWlyZShcImJhclwiKTtcbmNvbnN0IHtoZWxsb30gPSByZXF1aXJlKFwid29ybGRcIik7XG5cbmZvby54KC4uLmZvby55KGhlbGxvLnopKTtcbiJdLCJuYW1lcyI6WyJyZXF1aXJlIiwiZm9vIiwiaGVsbG8iLCJ4IiwiYXBwbHkiLCJfdG9Db25zdW1hYmxlQXJyYXkiLCJ5IiwieiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7ZUFBY0EsUUFBUSxRQUFmQyxNLFNBQUFBOztBLGdCQUNTRCxRQUFRLFVBQWpCRSxRLFVBQUFBOztBQUVQRCxJQUFJRSxFQUFKQyxNQUFBSCxLQUFHSSxtQkFBTUosSUFBSUssRUFBRUosTUFBTUsifQ==

View File

@@ -1,4 +1,16 @@
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
"use strict";
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
var _require = require("bar"),
foo = _require.foo;

View File

@@ -1 +1,11 @@
{"version":3,"sources":["input2.js"],"names":["require","foo","hello","x","apply","_toConsumableArray","y","z"],"mappings":"kLAAcA,QAAQ,OAAfC,aAAAA,kBACSD,QAAQ,SAAjBE,gBAAAA,MAEPD,IAAIE,EAAJC,MAAAH,IAAAI,mBAASJ,IAAIK,EAAEJ,MAAMK","sourcesContent":["const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"]}
{
"version": 3,
"sources": [
"input.js"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH",
"sourcesContent": [
"const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"
]
}

View File

@@ -2,4 +2,4 @@ function test(a){
"aaaaaaaaaaaaaaaa"
;a(err,data),a(err,
data)}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQztBQUFLQyJ9
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7O0NBRVZBLEVBQVNDLElBQUtDLE1BQ2RGLEVBQVNDO0FBQUtDIn0=

View File

@@ -14,9 +14,9 @@ if (typeof phantom == "undefined") {
args.splice(debug, 1);
debug = true;
}
args.unshift("bin/uglifyjs");
if (!args.length) args.push("-mcb", "beautify=false,webkit");
args.push("--timings");
args.unshift("bin/uglifyjs");
args.push("--validate", "--timings");
var child_process = require("child_process");
var fetch = require("./fetch");
var http = require("http");
@@ -41,7 +41,7 @@ if (typeof phantom == "undefined") {
var uglifyjs = child_process.spawn(process.argv[0], args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
console.log("uglifyjs", url.slice(site.length + 1), args.slice(1).join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});

View File

@@ -2,6 +2,7 @@ var assert = require("assert");
var exec = require("child_process").exec;
var fs = require("fs");
var run_code = require("../sandbox").run_code;
var to_ascii = require("../node").to_ascii;
function read(path) {
return fs.readFileSync(path, "utf8");
@@ -48,6 +49,62 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should work with --source-map names=true", function(done) {
exec([
uglifyjscmd,
"--beautify",
"--source-map", [
"names=true",
"url=inline",
].join(","),
].join(" "), function(err, stdout) {
if (err) throw err;
var expected = [
"var obj = {",
" p: a,",
" q: b",
"};",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,",
].join("\n")
assert.strictEqual(stdout.slice(0, expected.length), expected);
var map = JSON.parse(to_ascii(stdout.slice(expected.length).trim()));
assert.deepEqual(map.names, [ "obj", "p", "a", "q", "b" ]);
done();
}).stdin.end([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"));
});
it("Should work with --source-map names=false", function(done) {
exec([
uglifyjscmd,
"--beautify",
"--source-map", [
"names=false",
"url=inline",
].join(","),
].join(" "), function(err, stdout) {
if (err) throw err;
var expected = [
"var obj = {",
" p: a,",
" q: b",
"};",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,",
].join("\n")
assert.strictEqual(stdout.slice(0, expected.length), expected);
var map = JSON.parse(to_ascii(stdout.slice(expected.length).trim()));
assert.deepEqual(map.names, []);
done();
}).stdin.end([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"));
});
it("Should give sensible error against invalid input source map", function(done) {
var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline --verbose";
exec(command, function(err, stdout, stderr) {
@@ -109,9 +166,12 @@ describe("bin/uglifyjs", function() {
}
var command = [
uglifyjscmd,
"--source-map", "content=" + mapFile,
"--source-map", "includeSources=true",
"--source-map", "url=inline",
"--beautify",
"--source-map", [
"content=" + mapFile,
"includeSources",
"url=inline",
].join(","),
].join(" ");
var child = exec(command, function(err, stdout) {

View File

@@ -1,7 +1,7 @@
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
var UglifyJS = require("../../");
var UglifyJS = require("../..");
function read(path) {
return readFileSync(path, "utf8");
@@ -9,18 +9,30 @@ function read(path) {
describe("minify", function() {
it("Should test basic sanity of minify with default options", function() {
var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }';
var js = "function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }";
var result = UglifyJS.minify(js);
assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
if (result.error) throw result.error;
assert.strictEqual(result.code, "function foo(n){return n?3:7}");
});
it("Should not mutate minify `options`", function() {
var options = {
compress: true,
mangle: false,
output: {},
};
var value = JSON.stringify(options);
var result = UglifyJS.minify("print(6 * 7);", options);
if (result.error) throw result.error;
assert.strictEqual(result.code, "print(42);");
assert.strictEqual(JSON.stringify(options), value);
})
it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)";
var result = UglifyJS.minify(files);
if (result.error) throw result.error;
assert.strictEqual(result.code, "alert(2);");
});
it("Should work with mangle.cache", function() {
var cache = {};
var original = "";
@@ -53,7 +65,6 @@ describe("minify", function() {
].join(""));
assert.strictEqual(run_code(compressed, true), run_code(original, true));
});
it("Should work with nameCache", function() {
var cache = {};
var original = "";
@@ -86,7 +97,6 @@ describe("minify", function() {
].join(""));
assert.strictEqual(run_code(compressed, true), run_code(original, true));
});
it("Should avoid cached names when mangling top-level variables", function() {
var cache = {};
var original = "";
@@ -115,7 +125,6 @@ describe("minify", function() {
].join(""));
assert.strictEqual(run_code(compressed, true), run_code(original, true));
});
it("Should avoid cached names when mangling inner-scoped variables", function() {
var cache = {};
var original = "";
@@ -139,7 +148,6 @@ describe("minify", function() {
].join(""));
assert.strictEqual(run_code(compressed, true), run_code(original, true));
});
it("Should not parse invalid use of reserved words", function() {
assert.strictEqual(UglifyJS.minify("function enum(){}").error, undefined);
assert.strictEqual(UglifyJS.minify("function static(){}").error, undefined);
@@ -155,7 +163,6 @@ describe("minify", function() {
}});
assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};');
});
it("Should preserve quote styles when quote_style is 3", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = UglifyJS.minify(js, {
@@ -165,7 +172,6 @@ describe("minify", function() {
}});
assert.strictEqual(result.code, 'var foo={"x":1,y:2,\'z\':3};');
});
it("Should not preserve quotes in object literals when disabled", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = UglifyJS.minify(js, {

View File

@@ -246,4 +246,54 @@ describe("test/reduce.js", function() {
"// }",
].join("\n"));
});
it("Should reduce test case which differs only in Error.message", function() {
var code = [
"var a=0;",
"try{",
"null[function(){}]",
"}catch(e){",
"for(var i in e.toString())",
"a++,console.log()",
"}",
"console.log(a);",
].join("");
var result = reduce_test(code, {
compress: false,
mangle: false,
output: {
beautify: true,
},
});
if (result.error) throw result.error;
assert.deepEqual(result.warnings, []);
assert.strictEqual(result.code.replace(/function \(/g, "function("), (semver.satisfies(process.version, "<=0.10") ? [
"// Can't reproduce test failure",
"// minify options: {",
'// "compress": false,',
'// "mangle": false,',
'// "output": {',
'// "beautify": true',
"// }",
"// }",
] : [
[
"try{",
"null[function(){}]",
"}catch(e){",
"console.log(e)",
"}",
].join(""),
"// output: TypeError: Cannot read property 'function(){}' of null",
"// ",
"// minify: TypeError: Cannot read property 'function() {}' of null",
"// ",
"// options: {",
'// "compress": false,',
'// "mangle": false,',
'// "output": {',
'// "beautify": true',
"// }",
"// }",
]).join("\n"));
});
});

View File

@@ -7,11 +7,13 @@ function read(path) {
}
function source_map(code) {
return JSON.parse(UglifyJS.minify(code, {
var result = UglifyJS.minify(code, {
compress: false,
mangle: false,
sourceMap: true,
}).map);
});
if (result.error) throw result.error;
return JSON.parse(result.map);
}
function get_map() {
@@ -65,6 +67,40 @@ describe("sourcemaps", function() {
].join("\n"));
assert.deepEqual(map.names, [ "enabled", "x" ]);
});
it("Should work with sourceMap.names=true", function() {
var result = UglifyJS.minify([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"), {
compress: false,
mangle: false,
sourceMap: {
names: true,
},
});
if (result.error) throw result.error;
var map = JSON.parse(result.map);
assert.deepEqual(map.names, [ "obj", "p", "a", "q", "b" ]);
});
it("Should work with sourceMap.names=false", function() {
var result = UglifyJS.minify([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"), {
compress: false,
mangle: false,
sourceMap: {
names: false,
},
});
if (result.error) throw result.error;
var map = JSON.parse(result.map);
assert.deepEqual(map.names, []);
});
it("Should mark array/object literals", function() {
var result = UglifyJS.minify([
"var obj = {};",

View File

@@ -24,11 +24,22 @@ function try_beautify(code) {
}
}
function test(original, estree, description) {
var transformed = UglifyJS.minify(UglifyJS.AST_Node.from_mozilla_ast(estree), {
function validate(ast) {
try {
ast.walk(new UglifyJS.TreeWalker(function(node) {
node.validate();
}));
} catch (e) {
return { error: e };
}
return UglifyJS.minify(ast, {
compress: false,
mangle: false
mangle: false,
});
}
function test(original, estree, description) {
var transformed = validate(UglifyJS.AST_Node.from_mozilla_ast(estree));
if (transformed.error || original !== transformed.code) {
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);

View File

@@ -1,5 +1,5 @@
var crypto = require("crypto");
var U = require("./node");
var U = require("..");
var List = U.List;
var os = require("os");
var sandbox = require("./sandbox");
@@ -18,20 +18,30 @@ var sandbox = require("./sandbox");
// will produce different output from the code minified with `minify_options`.
// Returns a `minify` result object with an additonal boolean property `reduced`.
Error.stackTraceLimit = Infinity;
module.exports = function reduce_test(testcase, minify_options, reduce_options) {
if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string();
minify_options = minify_options || {};
reduce_options = reduce_options || {};
var max_iterations = reduce_options.max_iterations || 1000;
var max_timeout = reduce_options.max_timeout || 10000;
var warnings = [];
var log = reduce_options.log || function(msg) {
warnings.push(msg);
};
var verbose = reduce_options.verbose;
var minify_options_json = JSON.stringify(minify_options, null, 2);
var result_cache = Object.create(null);
var test_for_diff = compare_run_code;
// the initial timeout to assess the viability of the test case must be large
var differs = producesDifferentResultWhenMinified(result_cache, testcase, minify_options, max_timeout);
var differs = test_for_diff(testcase, minify_options, result_cache, max_timeout);
if (verbose) {
console.error("// Node.js " + process.version + " on " + os.platform() + " " + os.arch());
log("// Node.js " + process.version + " on " + os.platform() + " " + os.arch());
}
if (differs.error && [ "DefaultsError", "SyntaxError" ].indexOf(differs.error.name) < 0) {
test_for_diff = test_minify;
differs = test_for_diff(testcase, minify_options, result_cache, max_timeout);
}
if (!differs) {
// same stdout result produced when minified
@@ -39,16 +49,19 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
code: [
"// Can't reproduce test failure",
"// minify options: " + to_comment(minify_options_json)
].join("\n")
].join("\n"),
warnings: warnings,
};
} else if (differs.timed_out) {
return {
code: [
"// Can't reproduce test failure within " + max_timeout + "ms",
"// minify options: " + to_comment(minify_options_json)
].join("\n")
].join("\n"),
warnings: warnings,
};
} else if (differs.error) {
differs.warnings = warnings;
return differs;
} else if (is_error(differs.unminified_result)
&& is_error(differs.minified_result)
@@ -57,7 +70,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
code: [
"// No differences except in error message",
"// minify options: " + to_comment(minify_options_json)
].join("\n")
].join("\n"),
warnings: warnings,
};
} else {
max_timeout = Math.min(100 * differs.elapsed, max_timeout);
@@ -125,40 +139,13 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
}
else if (node instanceof U.AST_Binary) {
CHANGED = true;
var permute = ((node.start._permute += step) * steps | 0) % 4;
var expr = [
node.left,
node.right,
][ permute & 1 ];
if (permute < 2) return expr;
// wrap with console.log()
return new U.AST_Call({
expression: new U.AST_Dot({
expression: new U.AST_SymbolRef({
name: "console",
start: {},
}),
property: "log",
start: {},
}),
args: [ expr ],
start: {},
});
}
else if (node instanceof U.AST_Catch || node instanceof U.AST_Finally) {
// drop catch or finally block
node.start._permute++;
CHANGED = true;
return null;
}
else if (node instanceof U.AST_Conditional) {
CHANGED = true;
return [
node.condition,
node.consequent,
node.alternative,
][ ((node.start._permute += step) * steps | 0) % 3 ];
return permute < 2 ? expr : wrap_with_console_log(expr);
}
else if (node instanceof U.AST_BlockStatement) {
if (in_list) {
@@ -193,12 +180,26 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_sequence(seq);
}
}
else if (node instanceof U.AST_Catch) {
// drop catch block
node.start._permute++;
CHANGED = true;
return null;
}
else if (node instanceof U.AST_Conditional) {
CHANGED = true;
return [
node.condition,
node.consequent,
node.alternative,
][ ((node.start._permute += step) * steps | 0) % 3 ];
}
else if (node instanceof U.AST_Defun) {
switch (((node.start._permute += step) * steps | 0) % 2) {
case 0:
CHANGED = true;
return List.skip;
case 1:
default:
if (!has_exit(node)) {
// hoist function declaration body
var body = node.body;
@@ -230,15 +231,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(expr);
}
}
else if (node instanceof U.AST_PropAccess) {
var expr = [
node.expression,
node.property instanceof U.AST_Node && node.property,
][ node.start._permute++ % 2 ];
if (expr) {
CHANGED = true;
return expr;
}
else if (node instanceof U.AST_Finally) {
// drop finally block
node.start._permute++;
CHANGED = true;
return null;
}
else if (node instanceof U.AST_For) {
var expr = [
@@ -287,6 +284,16 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return expr;
}
}
else if (node instanceof U.AST_PropAccess) {
var expr = [
node.expression,
node.property instanceof U.AST_Node && node.property,
][ node.start._permute++ % 2 ];
if (expr) {
CHANGED = true;
return expr;
}
}
else if (node instanceof U.AST_SimpleStatement) {
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body
@@ -414,19 +421,41 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
});
var diff_error_message;
for (var pass = 1; pass <= 3; ++pass) {
var testcase_ast = U.parse(testcase);
if (diff_error_message === testcase) {
// only difference detected is in error message, so expose that and try again
testcase_ast.transform(new U.TreeTransformer(function(node, descend) {
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
return to_sequence(node.args);
}
if (node instanceof U.AST_Catch) {
descend(node, this);
node.body.unshift(new U.AST_SimpleStatement({
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
start: {},
}));
return node;
}
}));
var code = testcase_ast.print_to_string();
if (diff = test_for_diff(code, minify_options, result_cache, max_timeout)) {
testcase = code;
differs = diff;
} else {
testcase_ast = U.parse(testcase);
}
}
diff_error_message = null;
testcase_ast.walk(new U.TreeWalker(function(node) {
// unshare start props to retain visit data between iterations
node.start = JSON.parse(JSON.stringify(node.start));
node.start._permute = 0;
}));
for (var c = 0; c < max_iterations; ++c) {
if (verbose) {
if (pass == 1 && c % 25 == 0) {
console.error("// reduce test pass "
+ pass + ", iteration " + c + ": " + testcase.length + " bytes");
}
if (verbose && pass == 1 && c % 25 == 0) {
log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
}
var CHANGED = false;
var code_ast = testcase_ast.clone(true).transform(tt);
@@ -436,12 +465,12 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} catch (ex) {
// AST is not well formed.
// no harm done - just log the error, ignore latest change and continue iterating.
console.error("*** Error generating code from AST.");
console.error(ex);
console.error("*** Discarding permutation and continuing.");
log("*** Error generating code from AST.");
log(ex.stack);
log("*** Discarding permutation and continuing.");
continue;
}
var diff = producesDifferentResultWhenMinified(result_cache, code, minify_options, max_timeout);
var diff = test_for_diff(code, minify_options, result_cache, max_timeout);
if (diff) {
if (diff.timed_out) {
// can't trust the validity of `code_ast` and `code` when timed out.
@@ -449,14 +478,15 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} else if (diff.error) {
// something went wrong during minify() - could be malformed AST or genuine bug.
// no harm done - just log code & error, ignore latest change and continue iterating.
console.error("*** Error during minification.");
console.error(code);
console.error(diff.error);
console.error("*** Discarding permutation and continuing.");
log("*** Error during minification.");
log(code);
log(diff.error.stack);
log("*** Discarding permutation and continuing.");
} else if (is_error(diff.unminified_result)
&& is_error(diff.minified_result)
&& diff.unminified_result.name == diff.minified_result.name) {
// ignore difference in error messages caused by minification
diff_error_message = testcase;
} else {
// latest permutation is valid, so use it as the basis of new changes
testcase_ast = code_ast;
@@ -467,27 +497,32 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
if (c == 0) break;
if (verbose) {
console.error("// reduce test pass " + pass + ": " + testcase.length + " bytes");
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
}
}
testcase = try_beautify(result_cache, testcase, minify_options, differs.unminified_result, max_timeout);
testcase = try_beautify(testcase, minify_options, differs.unminified_result, result_cache, max_timeout);
var lines = [ "" ];
var unminified_result = strip_color_codes(differs.unminified_result);
var minified_result = strip_color_codes(differs.minified_result);
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
lines.push(
"// (stringified)",
"// output: " + JSON.stringify(unminified_result),
"// minify: " + JSON.stringify(minified_result)
);
if (isNaN(max_timeout)) {
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack)));
} else {
lines.push(
"// output: " + to_comment(unminified_result),
"// minify: " + to_comment(minified_result)
);
var unminified_result = strip_color_codes(differs.unminified_result);
var minified_result = strip_color_codes(differs.minified_result);
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
lines.push(
"// (stringified)",
"// output: " + JSON.stringify(unminified_result),
"// minify: " + JSON.stringify(minified_result)
);
} else {
lines.push(
"// output: " + to_comment(unminified_result),
"// minify: " + to_comment(minified_result)
);
}
}
lines.push("// options: " + to_comment(minify_options_json));
testcase.code += lines.join("\n");
testcase.warnings = warnings;
return testcase;
}
};
@@ -504,7 +539,7 @@ function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, "");
}
function try_beautify(result_cache, testcase, minify_options, expected, timeout) {
function try_beautify(testcase, minify_options, expected, result_cache, timeout) {
var result = U.minify(testcase, {
compress: false,
mangle: false,
@@ -518,10 +553,16 @@ function try_beautify(result_cache, testcase, minify_options, expected, timeout)
code: testcase,
};
var toplevel = sandbox.has_toplevel(minify_options);
var actual = run_code(result_cache, result.code, toplevel, timeout);
if (!sandbox.same_stdout(expected, actual)) return {
code: testcase,
};
if (isNaN(timeout)) {
if (!U.minify(result.code, minify_options).error) return {
code: testcase,
};
} else {
var actual = run_code(result.code, toplevel, result_cache, timeout);
if (!sandbox.same_stdout(expected, actual)) return {
code: testcase,
};
}
result.code = "// (beautified)\n" + result.code;
return result;
}
@@ -592,21 +633,37 @@ function to_statement(node) {
});
}
function run_code(result_cache, code, toplevel, timeout) {
function wrap_with_console_log(node) {
// wrap with console.log()
return new U.AST_Call({
expression: new U.AST_Dot({
expression: new U.AST_SymbolRef({
name: "console",
start: {},
}),
property: "log",
start: {},
}),
args: [ node ],
start: {},
});
}
function run_code(code, toplevel, result_cache, timeout) {
var key = crypto.createHash("sha1").update(code).digest("base64");
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout));
}
function producesDifferentResultWhenMinified(result_cache, code, minify_options, max_timeout) {
function compare_run_code(code, minify_options, result_cache, max_timeout) {
var minified = U.minify(code, minify_options);
if (minified.error) return minified;
var toplevel = sandbox.has_toplevel(minify_options);
var elapsed = Date.now();
var unminified_result = run_code(result_cache, code, toplevel, max_timeout);
var unminified_result = run_code(code, toplevel, result_cache, max_timeout);
elapsed = Date.now() - elapsed;
var timeout = Math.min(100 * elapsed, max_timeout);
var minified_result = run_code(result_cache, minified.code, toplevel, timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout);
if (sandbox.same_stdout(unminified_result, minified_result)) {
return is_timed_out(unminified_result) && is_timed_out(minified_result) && {
@@ -619,4 +676,10 @@ function producesDifferentResultWhenMinified(result_cache, code, minify_options,
elapsed: elapsed,
};
}
Error.stackTraceLimit = Infinity;
function test_minify(code, minify_options) {
var minified = U.minify(code, minify_options);
return minified.error && {
minified_result: minified.error,
};
}

View File

@@ -764,20 +764,22 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
case p++:
var name = getVarName();
return name + " && " + name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++:
var name = getVarName();
return name + " && " + name + "." + getDotKey();
var s = name + "." + getDotKey();
return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++:
case p++:
var name = getVarName();
var s = name + "." + getDotKey();
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++:
case p++:
case p++:
case p++:
if (rng(16) == 0) {
var name = getVarName();
var fn = name + "." + getDotKey();
called[name] = true;
return name + " && " + "typeof " + fn + ' == "function" && --_calls_ >= 0 && ' + fn + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
}
var name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
called[name] = true;
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
@@ -1018,7 +1020,7 @@ function log_suspects(minify_options, component) {
var defs = default_options[component];
var suspects = Object.keys(defs).filter(function(name) {
var flip = name == "keep_fargs";
if (flip === !(name in options ? options : defs)[name]) {
if (flip !== (name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options));
o[name] = flip;
@@ -1029,7 +1031,7 @@ function log_suspects(minify_options, component) {
errorln(result.error);
} else {
var r = sandbox.run_code(result.code, sandbox.has_toplevel(m));
return sandbox.same_stdout(original_result, r);
return !sandbox.same_stdout(uglify_result, r);
}
}
});
@@ -1043,10 +1045,8 @@ function log_suspects(minify_options, component) {
}
function log_suspects_global(options) {
var o = {};
UglifyJS.minify("", o);
var suspects = Object.keys(o).filter(function(component) {
return typeof o[component] != "object";
var suspects = Object.keys(default_options).filter(function(component) {
return typeof default_options[component] != "object";
}).filter(function(component) {
var m = JSON.parse(options);
m[component] = false;
@@ -1056,7 +1056,7 @@ function log_suspects_global(options) {
errorln(result.error);
} else {
var r = sandbox.run_code(result.code, sandbox.has_toplevel(m));
return sandbox.same_stdout(original_result, r);
return !sandbox.same_stdout(uglify_result, r);
}
});
if (suspects.length > 0) {
@@ -1113,7 +1113,10 @@ function log(options) {
errorln(JSON.stringify(JSON.parse(options), null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, JSON.parse(options)));
Object.keys(default_options).filter(function(component) {
var defs = default_options[component];
return defs && typeof defs == "object";
}).forEach(log_suspects.bind(null, JSON.parse(options)));
log_suspects_global(options);
errorln("!!!!!! Failed... round " + round);
}
@@ -1133,11 +1136,50 @@ function fuzzy_match(original, uglified) {
return true;
}
function skip_infinite_recursion(orig, toplevel) {
var code = orig;
var tries = [];
var offset = 0;
var re = /(?:(?:^|[\s{};])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g;
var match;
while (match = re.exec(code)) {
if (/}\s*finally\s*$/.test(match[0])) {
tries.shift();
continue;
}
if (tries.length && tries[0].catch) tries.shift();
var index = match.index + match[0].length + 1;
if (/(?:^|[\s{};])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset });
continue;
}
tries[0].catch = index;
var insert = "throw " + match[1] + ".ufuzz_skip || (" + match[1] + ".ufuzz_skip = " + tries[0].try + "), " + match[1] + ";";
var new_code = code.slice(0, index) + insert + code.slice(index);
var result = sandbox.run_code(new_code, toplevel);
if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") {
offset += insert.length;
code = new_code;
} else if (result.name == "RangeError" && result.message == "Maximum call stack size exceeded") {
index = result.ufuzz_skip;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
}
}
}
var fallback_options = [ JSON.stringify({
compress: false,
mangle: false
}) ];
var minify_options = require("./options.json").map(JSON.stringify);
var sort_globals = [
"Object.keys(this).sort().forEach(function(name) {",
" var value = this[name];",
" delete this[name];",
" this[name] = value;",
"});",
"",
].join("\n");
var original_code, original_result, errored;
var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) {
@@ -1150,24 +1192,36 @@ for (var round = 1; round <= num_iterations; round++) {
(errored ? fallback_options : minify_options).forEach(function(options) {
var o = JSON.parse(options);
var toplevel = sandbox.has_toplevel(o);
o.validate = true;
uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[toplevel ? 1 : 0];
if (!uglify_code.error) {
uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code, toplevel);
ok = sandbox.same_stdout(original_result, uglify_result);
if (!ok && typeof uglify_result == "string" && o.compress.unsafe_math) {
// ignore declaration order of global variables
if (!ok && !toplevel) {
ok = sandbox.same_stdout(sandbox.run_code(sort_globals + original_code), sandbox.run_code(sort_globals + uglify_code));
}
// ignore numerical imprecision caused by `unsafe_math`
if (!ok && typeof uglify_result == "string" && o.compress && o.compress.unsafe_math) {
ok = fuzzy_match(original_result, uglify_result);
if (!ok) {
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel);
ok = sandbox.same_stdout(fuzzy_result, uglify_result);
}
}
// ignore difference in depth of termination caused by infinite recursion
if (!ok) {
var orig_skipped = skip_infinite_recursion(original_code, toplevel);
var uglify_skipped = skip_infinite_recursion(uglify_code, toplevel);
if (orig_skipped && uglify_skipped) {
ok = sandbox.same_stdout(sandbox.run_code(orig_skipped, toplevel), sandbox.run_code(uglify_skipped, toplevel));
}
}
} else {
uglify_code = uglify_code.error;
if (errored) {
ok = uglify_code.name == original_result.name;
}
ok = errored && uglify_code.name == original_result.name;
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (errored) {

View File

@@ -66,8 +66,8 @@ function infer_options(options) {
}
exports.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var defs = infer_options({ 0: 0 });
Object.keys(defs).forEach(function(component) {
var options = {};
options[component] = { 0: 0 };
if (options = infer_options(options)) {