Compare commits

..

128 Commits

Author SHA1 Message Date
Alex Lam S.L
8e595171b9 harmony-v3.3.9 2018-01-27 22:56:06 +08:00
alexlamsl
6973abbfe1 Merge branch 'master' into harmony-v3.3.9 2018-01-27 20:58:49 +08:00
Alex Lam S.L
4eb4cb656c v3.3.9 2018-01-27 12:56:34 +00:00
Alex Lam S.L
193612ac67 fix accounting after conversion to assignment (#2847)
Missing reference to `AST_SymbolRef` created by `unused` causes `collapse_vars` to misbehave.

fixes #2846
2018-01-26 14:21:11 +08:00
Alex Lam S.L
95cfce68ea backport of #2835 (#2841) 2018-01-23 05:45:45 +08:00
kzc
e0461dc3c8 fix for-in/of regression with let or const loop variable (#2840)
fixes #2835
2018-01-23 05:45:02 +08:00
Alex Lam S.L
ec4202590d drop assignments to constant expressions only (#2839)
fixes #2838
2018-01-23 02:49:54 +08:00
Alex Lam S.L
5e2cd07d6f handle duplicate function declarations correctly (#2837)
fixes #2836
2018-01-23 01:28:09 +08:00
kzc
bea9dbd812 enable reminify on harmony branch to avoid regressions (#2834)
- can skip known test failures with `reminify: false`
2018-01-22 18:20:29 +08:00
Alex Lam S.L
bc01a85ba0 add parenthesis around sequence in yield (#2833)
fixes #2832
2018-01-22 14:57:23 +08:00
Alex Lam S.L
c7c7960b5f harmony-v3.3.8 2018-01-21 16:44:17 +08:00
alexlamsl
fc98d212db allow duplicate property names in object literals for ES6+ 2018-01-21 15:58:14 +08:00
alexlamsl
13accdd745 fix tests 2018-01-21 15:53:32 +08:00
alexlamsl
287ec730f7 Merge branch 'master' into harmony-v3.3.8 2018-01-21 15:52:25 +08:00
Alex Lam S.L
06166df999 v3.3.8 2018-01-21 07:08:01 +00:00
Alex Lam S.L
e2dc9cf091 fix unsafe evaluate of AST_Array (#2825)
fixes #2822
2018-01-21 01:39:44 +08:00
Alex Lam S.L
069df27bf1 enable unsafe for test/ufuzz.js (#2819)
- introduce `unsafe_undefined`
- safer `.toString()` compression

Miscellaneous
- rename `unsafe_Function`
2018-01-19 23:47:42 +08:00
Alex Lam S.L
3e7873217c improve unused on built-in functions (#2817) 2018-01-19 20:41:57 +08:00
Alex Lam S.L
e21bab7ce6 avoid duplicate property names in object literals under "use strict" (#2818)
fixes #2816
2018-01-19 20:13:50 +08:00
Alex Lam S.L
ac9a168fba fix & improve test/ufuzz.js (#2815)
- use correct `options` when testing `rename`
- mask arbitrarily assigned function IDs to reduce rate of false positives
2018-01-19 16:51:59 +08:00
Alex Lam S.L
81b64549ce fix time-out for respawned test/ufuzz.js (#2814) 2018-01-19 06:11:19 +08:00
Alex Lam S.L
082e004b87 compress undefined property names (#2811)
- enforce property names as string
- handle `void 0` as `undefined` in `hoist_props` & `reduce_vars`
2018-01-19 00:36:30 +08:00
kzc
983e69128b fix join_vars property assignment for negative array index (#2810)
fixes #2790
2018-01-18 21:52:54 +08:00
Alex Lam S.L
b335912e86 enhance test/ufuzz.js (#2808)
- standalone test for `rename`
- handle `keep_fargs` & `rename` upon failure
2018-01-18 14:08:05 +08:00
Alex Lam S.L
cc07f3b806 faster output of comments (#2806) 2018-01-18 02:57:33 +08:00
Alex Lam S.L
07e4b64f3a fix AST_Scope.clone() (#2803)
fixes #2799
2018-01-17 21:33:13 +08:00
Alex Lam S.L
d3ce2bc9e7 suppress unsafe_proto for LHS expressions (#2804) 2018-01-17 20:41:51 +08:00
Alex Lam S.L
cff3bf4914 configure rename with CLI (#2802) 2018-01-17 15:12:22 +08:00
kzc
4f57d8746b fix various for-of bugs (#2800)
- disable `rename` pass on harmony due to problem with for-of loops

fixes #2794
2018-01-17 14:46:23 +08:00
Alex Lam S.L
79cfac77bd extend join_vars & sequences (#2798) 2018-01-17 13:58:27 +08:00
Alex Lam S.L
224c14d49d improve mocha tests (#2797)
- workaround sporadic delays from Travis CI
2018-01-16 17:51:25 +08:00
Alex Lam S.L
7857354d85 improve test/travis-ufuzz.js (#2795)
- print usage
- support concurrent jobs
- improve instance utilisation
- resume after V8 self-destruct
2018-01-16 17:33:21 +08:00
Alex Lam S.L
b4aef753e7 general improvements around AST_ForIn (#2796)
- compress using `collapse_vars`
- remove unused `name`
- simplify `loop_body`
2018-01-16 17:03:12 +08:00
Joël Galeran
424173d311 fix typo in README (#2792) 2018-01-16 10:29:38 +08:00
Alex Lam S.L
ec7cd1dcf7 handle VM failure gracefully (#2791) 2018-01-15 23:41:39 +08:00
Alex Lam S.L
7def684730 improve test/travis-ufuzz.js (#2789)
- wait for instance to boot
- run on forked repositories
- workaround `request_limit_reached`
2018-01-15 19:18:21 +08:00
Alex Lam S.L
10f961c27b enhance collapse_vars (#2788) 2018-01-15 18:47:23 +08:00
Alex Lam S.L
b483678ca7 avoid suboptimal termination in passes (#2787) 2018-01-15 16:42:31 +08:00
Alex Lam S.L
cbbe6fad60 avoid double counting within single-use functions (#2785)
fixes #2783
2018-01-15 16:42:15 +08:00
Alex Lam S.L
f96929c031 improve test/travis-ufuzz.js (#2786)
- use more RAM
- show progress in console
- report failure as job status
2018-01-15 15:08:35 +08:00
Alex Lam S.L
2b6657e967 run test/ufuzz.js when Travis CI is idle (#2784) 2018-01-15 08:52:11 +08:00
Alex Lam S.L
1b2e6b81a2 harmony-v3.3.7 2018-01-14 19:24:49 +08:00
alexlamsl
f9e9898dc1 Merge branch 'master' into harmony-v3.3.7 2018-01-14 17:15:16 +08:00
Alex Lam S.L
7c0c92943f v3.3.7 2018-01-14 09:13:26 +00:00
Alex Lam S.L
62a66dfff4 fix & extend join_vars for object assigments (#2781) 2018-01-14 17:11:31 +08:00
kzc
c44d78db55 add block_scope to --output ast (#2780) 2018-01-14 15:37:18 +08:00
Alex Lam S.L
8c763bf2b5 fix mangle of block-scoped variables (#2779)
fixes #2762
2018-01-14 12:12:29 +08:00
kzc
2cab348341 improve SymbolDef info in --output ast (#2778)
* SymbolDef info (a.k.a. `thedef`) is now represented as a string containing `"ID name [mangled_name]"`. 
* Enhance display of `globals`, `variables`, `functions` and `enclosed`.
* `SymbolDef.next_id` starts at `1` and the `id` is adjusted for `-o ast` display.
2018-01-14 01:40:51 +08:00
Alex Lam S.L
aa1786dedf harmony-v3.3.6 2018-01-13 23:40:14 +08:00
alexlamsl
0d5df271a1 add tests for #2740 2018-01-13 22:59:01 +08:00
alexlamsl
b56e1f178f add test for #2747 2018-01-13 22:58:52 +08:00
alexlamsl
9acace2cb6 fix test 2018-01-13 13:57:05 +08:00
alexlamsl
0f2be1456c Merge branch 'master' into harmony-v3.3.6 2018-01-13 13:53:31 +08:00
Alex Lam S.L
460218a3f8 v3.3.6 2018-01-13 05:37:42 +00:00
Alex Lam S.L
e49416e4aa fix reduce_vars on AST_Accessor (#2776)
fixes #2774
2018-01-13 02:46:14 +08:00
kzc
d4d7d99b70 add SymbolDef IDs to --output ast (#2772) 2018-01-12 15:41:09 +08:00
Alex Lam S.L
6a696d0a7b fix output of imported AST (#2771) 2018-01-12 01:05:49 +08:00
Alex Lam S.L
1c9e13f47d update dependencies (#2770)
- acorn@5.3.0
- commander@2.13.0
2018-01-12 00:32:17 +08:00
Alex Lam S.L
b757450cd8 fix nested unused assignments (#2769)
fixes #2768
2018-01-11 23:13:44 +08:00
Alex Lam S.L
23ec484806 fix corner case in #2763 (#2766) 2018-01-11 21:18:08 +08:00
Alex Lam S.L
f1e1bb419a join object assignments (#2763) 2018-01-11 17:08:21 +08:00
Alex Lam S.L
6a0af85c8b skip only vars in if_return (#2759)
fixes #2747
2018-01-10 19:08:46 +08:00
Alex Lam S.L
1eb15f46f1 fix reduce_vars with uninitialized let variables (#2760)
fixes #2757
2018-01-10 18:40:54 +08:00
Alex Lam S.L
09269be974 enhance conditionals (#2758)
`x ? y || z : z` --> `x && y || z`
2018-01-10 16:59:57 +08:00
kzc
137cb73d1f have parser trap const declaration without value (#2756)
fixes #2751
2018-01-10 12:31:46 +08:00
Alex Lam S.L
bf832cde16 improve synergy between compress and rename (#2755) 2018-01-09 17:55:41 +08:00
Alex Lam S.L
2972d58dbb patch variable declaractions extracted within catch (#2753)
fixes #2749
2018-01-09 13:54:35 +08:00
Alex Lam S.L
2e22d38a02 improve rename reproducibility (#2754)
fixes #2752
2018-01-09 13:53:05 +08:00
Alex Lam S.L
ce27bcd69a compress loops with immediate break (#2746)
fixes #2740
2018-01-08 14:30:18 +08:00
Alex Lam S.L
1f3f8f25eb harmony-v3.3.5 2018-01-08 00:30:45 +08:00
alexlamsl
4b1799ecdd disable reminify()
fix-up for #2708
2018-01-07 22:32:29 +08:00
alexlamsl
7a8eff62ca Merge branch 'master' into harmony-v3.3.5 2018-01-07 20:35:53 +08:00
Alex Lam S.L
9336cc8247 v3.3.5 2018-01-07 11:51:36 +00:00
Alex Lam S.L
9809567dfc improve process.exit() workaround (#2741)
- use public API
- fix issue with Node.js 0.10 on WIndows
2018-01-07 17:53:50 +08:00
Alex Lam S.L
1ee8be8d91 fix recursive function inline (#2738)
fixes #2737
2018-01-07 15:31:24 +08:00
Alex Lam S.L
8430c2f9f8 enable AppVeyor CI (#2739) 2018-01-07 14:14:58 +08:00
Alex Lam S.L
659c8a7632 handle trailing line comments correctly (#2736)
fixes #2734
2018-01-06 21:05:21 +08:00
Alex Lam S.L
3564b4f20d compress RegExp() in unsafe (#2735) 2018-01-06 21:04:47 +08:00
Alex Lam S.L
3505a3604a enhance unsafe_proto (#2733) 2018-01-06 06:16:51 +08:00
kzc
9b1bc6c014 ufuzz: add strings "a", "b", "c" to VALUES (#2732) 2018-01-06 05:54:53 +08:00
Alex Lam S.L
9f23185f2b fix corner case with arguments as function name (#2729)
fixes #2728
2018-01-05 22:21:18 +08:00
Alex Lam S.L
b82feb9302 improve if_return (#2727) 2018-01-05 20:24:30 +08:00
Alex Lam S.L
7f2a591c7e warn on deprecated features (#2726)
- `function.arguments`
- `function.callers`

fixes #2719
2018-01-05 19:36:02 +08:00
Alex Lam S.L
f76b5e0f43 fix hoist_props on const (#2724)
fixes #2710
2018-01-05 06:23:53 +08:00
Alex Lam S.L
afbcebddf6 fix mangle name collision across files (#2722) 2018-01-05 05:08:09 +08:00
Alex Lam S.L
484e484571 fix corner case in inline (#2720) 2018-01-04 23:38:37 +08:00
Alex Lam S.L
6f3f21233f reminify tests upon expect_stdout (#2716) 2018-01-04 20:13:05 +08:00
Alex Lam S.L
a6873a3859 forbid block-scoped AST_Defun in strict mode (#2718) 2018-01-04 18:45:51 +08:00
Alex Lam S.L
7a6d452b54 preserve constant modification under strict mode (#2717) 2018-01-04 15:53:53 +08:00
Alex Lam S.L
9b58b54e2d extend inline (#2714)
- compress `function` with variables within loops
- restrict to `AST_Var` for better compatibility with ES6+
2018-01-04 12:58:40 +08:00
Alex Lam S.L
c598a12af9 apply collapse_vars to loop conditions (#2712) 2018-01-04 03:18:28 +08:00
Alex Lam S.L
cfe3a98ce5 drop unused assignment based on reduce_vars (#2709) 2018-01-04 01:03:33 +08:00
Alex Lam S.L
14778e049b fix reduce_vars on AST_Defun (#2708) 2018-01-03 17:18:38 +08:00
Alex Lam S.L
446fb0198b extend __PURE__ to AST_New (#2706)
fixes #2705
2018-01-03 04:48:07 +08:00
Alex Lam S.L
7d3cddf9d6 inline functions with AST_Var (#2688) 2018-01-03 01:54:44 +08:00
Alex Lam S.L
6dead95eb3 enhance collapse_vars (#2704) 2018-01-02 18:42:15 +08:00
Alex Lam S.L
cc931b3ad8 enhance if_return (#2703) 2018-01-02 15:09:51 +08:00
Alex Lam S.L
ffc64e2279 mark AST_Var out of block scopes (#2700) 2018-01-02 01:37:59 +08:00
Alex Lam S.L
d838b4b52e reset argument value within loop after inline (#2699) 2018-01-02 01:24:53 +08:00
Alex Lam S.L
2f3bddbaca scan within IIFEs of assigned values (#2702)
fixes #2701
2018-01-02 01:24:23 +08:00
Alex Lam S.L
673b071637 enhance join_vars & sequences (#2697)
- nudge declarations without assignments
  - within `AST_BlockStatement`
  - across `AST_If`
2018-01-01 00:09:26 +08:00
Alex Lam S.L
da82fa59a7 fix inline on duplicate argument names (#2698) 2017-12-31 20:59:58 +08:00
Alex Lam S.L
333792352e reduce hoisting declarations (#2687) 2017-12-31 16:15:00 +08:00
Alex Lam S.L
a6653e2102 harmony-v3.3.4 2017-12-31 01:52:35 +08:00
alexlamsl
3bbe839518 Merge branch 'master' into harmony-v3.3.4 2017-12-31 00:05:32 +08:00
Alex Lam S.L
e2ec270b04 v3.3.4 2017-12-31 00:01:14 +08:00
Alex Lam S.L
ace272f0c5 improve test for #2689 (#2694) 2017-12-30 15:49:49 +08:00
Alex Lam S.L
ed7a0a454e fix dead_code on escaped return assignment (#2693)
fixes #2692
2017-12-30 15:20:25 +08:00
Alex Lam S.L
725aac8b46 fix parse and output of yield (#2690)
fixes #2689
2017-12-30 03:27:26 +08:00
Alex Lam S.L
d819559a01 minor clean-ups (#2686) 2017-12-29 14:04:52 +08:00
Alex Lam S.L
53600e9869 harmony-v3.3.3 2017-12-29 04:36:27 +08:00
alexlamsl
7dea749f58 Merge branch 'master' into harmony-v3.3.3 2017-12-29 03:30:53 +08:00
Alex Lam S.L
8ca49155a8 v3.3.3 2017-12-29 03:07:39 +08:00
Alex Lam S.L
b95e3338d9 fix pure_getters on AST_Binary (#2681)
fixes #2678
2017-12-28 17:01:01 +08:00
Alex Lam S.L
e40a0ee9c6 improve assignment variations (#2671) 2017-12-28 15:36:55 +08:00
Alex Lam S.L
0a3fac6e68 fix parenthesis output of AST_ClassExpression (#2677)
fixes #2676
2017-12-28 05:07:19 +08:00
Alex Lam S.L
cb62bd98d3 fix function inlining within loops (#2675)
fixes #2663
2017-12-28 02:53:14 +08:00
Alex Lam S.L
9f7d1f7ac6 fix reduce_vars on AST_Destructuring (#2672)
fixes #2669
fixes #2670
fixes #2673
2017-12-27 16:30:57 +08:00
Alex Lam S.L
f30790b11b fix dead_code on return assignments (#2668)
fixes #2666
2017-12-27 07:40:34 +08:00
Alex Lam S.L
5205dbcbf4 retain recursive function names (#2667)
fixes #2665
2017-12-27 07:00:12 +08:00
Alex Lam S.L
3ff625de7e fix bugs on substituted AST_Defun (#2661)
fixes #2660
2017-12-27 05:31:37 +08:00
Alex Lam S.L
4832bc5d88 replace single-use recursive functions (#2659)
fixes #2628
2017-12-26 21:25:35 +08:00
Alex Lam S.L
7f342cb3e3 suppress inline within substituted AST_Scope (#2658)
fixes #2657
2017-12-26 18:56:59 +08:00
Alex Lam S.L
05e7d34ed4 improve unused over duplicate variable names (#2656) 2017-12-26 18:29:28 +08:00
Alex Lam S.L
ef74f2eaaf harmony-v3.3.2 2017-12-26 05:21:31 +08:00
alexlamsl
96082f7a9b Merge branch 'master' into harmony-v3.3.2 2017-12-26 01:46:22 +08:00
Alex Lam S.L
86607156e3 v3.3.2 2017-12-26 01:38:56 +08:00
Alex Lam S.L
0fe259e9c5 parse LF & comment correctly (#2653)
fixes #2652
2017-12-26 01:38:01 +08:00
54 changed files with 5420 additions and 852 deletions

View File

@@ -67,7 +67,7 @@ a double dash to prevent input files being used as option arguments:
`debug` Add debug prefix and suffix.
`domprops` Mangle property names that overlaps
with DOM properties.
`keep_quoted` Only mangle unquoted properies.
`keep_quoted` Only mangle unquoted properties.
`regex` Only mangle matched property names.
`reserved` List of names that should not be mangled.
-b, --beautify [options] Beautify output/specify output options:
@@ -675,7 +675,13 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `inline` (default: `true`) -- embed simple functions
- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
- `false` -- same as `0`
- `0` -- disabled inlining
- `1` -- inline simple functions
- `2` -- inline functions with arguments
- `3` -- inline functions with arguments and variables
- `true` -- same as `3`
- `join_vars` (default: `true`) -- join consecutive `var` statements
@@ -776,7 +782,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)`
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
@@ -795,6 +801,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unsafe_undefined` (default: `false`) -- substitute `void 0` if there is a
variable named `undefined` in scope (variable name will be mangled, typically
reduced to a single character)
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`)
@@ -987,9 +997,6 @@ when this flag is on:
- `new Object()``{}`
- `String(exp)` or `exp.toString()``"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `void 0``undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
### Conditional compilation

View File

@@ -1,24 +1,20 @@
environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4.0"
- nodejs_version: "6.0"
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "8"
install:
- ps: Install-Product node $env:nodejs_version
- set UGLIFYJS_TEST_ALL=1
- npm install
build: off
cache:
- tmp
matrix:
fast_finish: true
platform:
- x86
- x64
install:
- ps: Install-Product node $env:nodejs_version $env:platform
- npm install
test_script:
- node --version
- npm --version
- npm test
build: off

View File

@@ -3,11 +3,7 @@
"use strict";
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
require("../tools/exit");
var fs = require("fs");
var info = require("../package.json");
@@ -15,7 +11,7 @@ var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "enclosed", "inlined", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var files = {};
var options = {
compress: false,
@@ -49,6 +45,7 @@ program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-classnames", "Do not mangle/drop class names.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--rename", "Force symbol expansion.");
program.option("--no-rename", "Disable symbol expansion.");
program.option("--safari10", "Support non-standard Safari 10.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
@@ -69,14 +66,12 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
"compress",
"ie8",
"mangle",
"rename",
"safari10",
"sourceMap",
"toplevel",
"wrap"
].forEach(function(name) {
if (name in program) {
if (name == "rename" && program[name]) return;
options[name] = program[name];
}
});
@@ -136,6 +131,11 @@ if (program.parse) {
fatal("ERROR: inline source map only works with built-in parser");
}
}
if (~program.rawArgs.indexOf("--rename")) {
options.rename = true;
} else if (!program.rename) {
options.rename = false;
}
var convert_path = function(name) {
return name;
};
@@ -238,7 +238,20 @@ function run() {
}
fatal(ex);
} else if (program.output == "ast") {
if (!options.compress && !options.mangle) {
result.ast.figure_out_scope({});
}
print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) {
case "thedef":
return symdef(value);
case "enclosed":
return value.length ? value.map(symdef) : undefined;
case "variables":
case "functions":
case "globals":
return value.size() ? value.map(symdef) : undefined;
}
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
@@ -246,6 +259,11 @@ function run() {
var result = {
_class: "AST_" + value.TYPE
};
if (value.block_scope) {
result.variables = value.block_scope.variables;
result.functions = value.block_scope.functions;
result.enclosed = value.block_scope.enclosed;
}
value.CTOR.PROPS.forEach(function(prop) {
result[prop] = value[prop];
});
@@ -392,6 +410,12 @@ function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}
function symdef(def) {
var ret = (1e6 + def.id) + " " + def.name;
if (def.mangled_name) ret += " " + def.mangled_name;
return ret;
}
function format_object(obj) {
var lines = [];
var padding = "";

View File

@@ -267,11 +267,10 @@ var AST_For = DEFNODE("For", "init condition step", {
}
}, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", "init name object", {
var AST_ForIn = DEFNODE("ForIn", "init object", {
$documentation: "A `for ... in` statement",
$propdoc: {
init: "[AST_Node] the `for/in` initialization code",
name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
object: "[AST_Node] the object that we're looping through"
},
_walk: function(visitor) {
@@ -319,6 +318,13 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
self = self.parent_scope;
}
return self;
},
clone: function(deep) {
var node = this._clone(deep);
if (this.variables) node.variables = this.variables.clone();
if (this.functions) node.functions = this.functions.clone();
if (this.enclosed) node.enclosed = this.enclosed.slice();
return node;
}
}, AST_Block);
@@ -863,8 +869,8 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$propdoc: {
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
key: "[string|AST_Node] property name. For ObjectKeyVal this is a string. For getters, setters and computed property this is an AST_Node.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
},
_walk: function(visitor) {
return visitor._visit(this, function(){

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,6 @@ function set_shorthand(name, options, keys) {
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
@@ -39,7 +38,6 @@ function init_cache(cache) {
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}
@@ -153,7 +151,9 @@ function minify(files, options) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (timings) timings.rename = Date.now();
if (options.rename) {
// disable rename on harmony due to expand_names bug in for-of loops
// https://github.com/mishoo/UglifyJS2/issues/2794
if (0 && options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
}

View File

@@ -472,83 +472,87 @@ function OutputStream(options) {
return OUTPUT;
};
function has_nlb() {
var index = OUTPUT.lastIndexOf("\n");
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function prepend_comments(node) {
var self = this;
var start = node.start;
if (!start) return;
if (!(start.comments_before && start.comments_before._dumped === self)) {
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
}
comments._dumped = self;
if (start.comments_before && start.comments_before._dumped === self) return;
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
}
comments._dumped = self;
if (node instanceof AST_Exit && node.value) {
var tw = new TreeWalker(function(node) {
var parent = tw.parent();
if (parent instanceof AST_Exit
|| parent instanceof AST_Binary && parent.left === node
|| parent.TYPE == "Call" && parent.expression === node
|| parent instanceof AST_Conditional && parent.condition === node
|| parent instanceof AST_Dot && parent.expression === node
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) {
if (!node.start) return;
var text = node.start.comments_before;
if (text && text._dumped !== self) {
text._dumped = self;
comments = comments.concat(text);
}
} else {
return true;
if (node instanceof AST_Exit && node.value) {
var tw = new TreeWalker(function(node) {
var parent = tw.parent();
if (parent instanceof AST_Exit
|| parent instanceof AST_Binary && parent.left === node
|| parent.TYPE == "Call" && parent.expression === node
|| parent instanceof AST_Conditional && parent.condition === node
|| parent instanceof AST_Dot && parent.expression === node
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) {
if (!node.start) return;
var text = node.start.comments_before;
if (text && text._dumped !== self) {
text._dumped = self;
comments = comments.concat(text);
}
});
tw.push(node);
node.value.walk(tw);
}
if (current_pos == 0) {
if (comments.length > 0 && options.shebang && comments[0].type == "comment5") {
print("#!" + comments.shift().value + "\n");
indent();
}
var preamble = options.preamble;
if (preamble) {
print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
}
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = /(^|\n) *$/.test(OUTPUT);
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
print("\n");
indent();
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
last_nlb = false;
} else {
return true;
}
});
tw.push(node);
node.value.walk(tw);
}
if (current_pos == 0) {
if (comments.length > 0 && options.shebang && comments[0].type == "comment5") {
print("#!" + comments.shift().value + "\n");
indent();
}
var preamble = options.preamble;
if (preamble) {
print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
}
}
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (start.nlb) {
if (c.nlb) {
print("\n");
indent();
} else {
last_nlb = true;
} else if (i > 0) {
space();
}
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n");
indent();
last_nlb = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
last_nlb = false;
}
});
if (!last_nlb) {
if (start.nlb) {
print("\n");
indent();
} else {
space();
}
}
}
@@ -557,31 +561,33 @@ function OutputStream(options) {
var token = node.end;
if (!token) return;
var comments = token[tail ? "comments_before" : "comments_after"];
if (comments && comments._dumped !== self) {
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\n");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !/(^|\n) *$/.test(OUTPUT))) {
print("\n");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
need_space = true;
}
});
if (OUTPUT.length > insert) newline_insert = insert;
}
if (!comments || comments._dumped === self) return;
if (!(node instanceof AST_Statement || all(comments, function(c) {
return !/comment[134]/.test(c.type);
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
need_space = false;
if (need_newline_indented) {
print("\n");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\n");
indent();
} else if (i > 0 || !tail) {
space();
}
if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
need_newline_indented = true;
} else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
need_space = true;
}
});
if (OUTPUT.length > insert) newline_insert = insert;
}
var stack = [];
@@ -627,7 +633,7 @@ function OutputStream(options) {
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly ? noop : append_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
@@ -699,9 +705,7 @@ function OutputStream(options) {
}
};
PARENS(AST_Node, function(){
return false;
});
PARENS(AST_Node, return_false);
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
@@ -730,15 +734,9 @@ function OutputStream(options) {
return p instanceof AST_PropAccess && p.expression === this;
});
PARENS(AST_ClassExpression, function(output){
return output.parent() instanceof AST_SimpleStatement;
});
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
return first_in_statement(output);
});
PARENS([ AST_ClassExpression, AST_Object ], first_in_statement);
PARENS(AST_Unary, function(output){
var p = output.parent();
@@ -773,6 +771,8 @@ function OutputStream(options) {
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
|| p instanceof AST_Expansion // [...(a, b)]
|| p instanceof AST_ForOf && this === p.object // for (e of (foo, bar)) {}
|| p instanceof AST_Yield // yield (foo, bar)
;
});
@@ -805,6 +805,10 @@ function OutputStream(options) {
// a = yield 3
if (p instanceof AST_Binary && p.operator !== "=")
return true;
// (yield 1)()
// new (yield 1)()
if (p instanceof AST_Call && p.expression === this)
return true;
// (yield 1) ? yield 2 : yield 3
if (p instanceof AST_Conditional && p.condition === this)
return true;
@@ -1049,11 +1053,7 @@ function OutputStream(options) {
output.with_parens(function(){
self.init.print(output);
output.space();
if (self instanceof AST_ForOf) {
output.print("of");
} else {
output.print("in");
}
output.print(self instanceof AST_ForOf ? "of" : "in");
output.space();
self.object.print(output);
});
@@ -1683,11 +1683,8 @@ function OutputStream(options) {
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
|| !output.option("beautify")
&& +key + "" == key)
&& parseFloat(key) >= 0) {
output.print_string(key);
} else if ("" + +key == key && key >= 0) {
output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {

View File

@@ -934,10 +934,15 @@ function parse($TEXT, options) {
function expect(punc) { return expect_token("punc", punc); };
function has_newline_before(token) {
return token.nlb || !all(token.comments_before, function(comment) {
return !comment.nlb;
});
}
function can_insert_semicolon() {
return !options.strict && (
S.token.nlb || is("eof") || is("punc", "}")
);
return !options.strict
&& (is("eof") || is("punc", "}") || has_newline_before(S.token));
};
function is_in_generator() {
@@ -985,10 +990,10 @@ function parse($TEXT, options) {
if (S.in_directives) {
var token = peek();
if (S.token.raw.indexOf("\\") == -1
&& (token.nlb
|| is_token(token, "eof")
|| is_token(token, "punc", ";")
|| is_token(token, "punc", "}"))) {
&& (is_token(token, "punc", ";")
|| is_token(token, "punc", "}")
|| has_newline_before(token)
|| is_token(token, "eof"))) {
S.input.add_directive(S.token.value);
} else {
S.in_directives = false;
@@ -1111,7 +1116,7 @@ function parse($TEXT, options) {
case "throw":
next();
if (S.token.nlb)
if (has_newline_before(S.token))
croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
@@ -1163,10 +1168,6 @@ function parse($TEXT, options) {
function labeled_statement() {
var label = as_symbol(AST_Label);
if (label.name === "yield" && is_in_generator()) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors
token_error(S.prev, "Yield cannot be used as label inside generators");
}
if (label.name === "await" && is_in_async()) {
token_error(S.prev, "await cannot be used as label inside async function");
}
@@ -1275,19 +1276,17 @@ function parse($TEXT, options) {
};
function for_in(init) {
var lhs = init instanceof AST_Definitions ? init.definitions[0].name : null;
var obj = expression(true);
expect(")");
return new AST_ForIn({
init : init,
name : lhs,
object : obj,
body : in_loop(statement)
});
};
var arrow_function = function(start, argnames, is_async) {
if (S.token.nlb) {
if (has_newline_before(S.token)) {
croak("Unexpected newline before arrow (=>)");
}
@@ -1326,7 +1325,7 @@ function parse($TEXT, options) {
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
unexpected(prev());
var args = parameters();
var args = [];
var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
return new ctor({
start : args.start,
@@ -1397,9 +1396,8 @@ function parse($TEXT, options) {
return tracker;
}
function parameters() {
function parameters(params) {
var start = S.token;
var params = [];
var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
expect("(");
@@ -1419,7 +1417,6 @@ function parse($TEXT, options) {
}
next();
return params;
}
function parameter(used_parameters, symbol_type) {
@@ -1506,12 +1503,7 @@ function parse($TEXT, options) {
}
} else if (is("name")) {
used_parameters.add_parameter(S.token);
elements.push(new symbol_type({
start: S.token,
name: S.token.value,
end: S.token
}));
next();
elements.push(as_symbol(symbol_type));
} else {
croak("Invalid function parameter");
}
@@ -1561,11 +1553,8 @@ function parse($TEXT, options) {
}
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
used_parameters.add_parameter(S.token);
var value = new symbol_type({
start: S.token,
name: S.token.value,
end: S.token,
});
var start = prev();
var value = as_symbol(symbol_type);
if (is_expand) {
elements.push(new AST_Expansion({
start: expand_token,
@@ -1574,13 +1563,12 @@ function parse($TEXT, options) {
}));
} else {
elements.push(new AST_ObjectKeyVal({
start: prev(),
key: S.token.value,
start: start,
key: value.name,
value: value,
end: value.end,
}));
}
next();
} else if (is("punc", "}")) {
continue; // Allow trailing hole
} else {
@@ -1637,12 +1625,7 @@ function parse($TEXT, options) {
});
} else if (is("name")) {
used_parameters.add_parameter(S.token);
next();
return new symbol_type({
start: prev(),
name: prev().value,
end: prev()
});
return as_symbol(symbol_type);
} else {
croak("Invalid function parameter");
}
@@ -1696,6 +1679,7 @@ function parse($TEXT, options) {
S.in_generator = S.in_function;
if (is_async)
S.in_async = S.in_function;
if (args) parameters(args);
if (block)
S.in_directives = true;
S.in_loop = 0;
@@ -1703,10 +1687,8 @@ function parse($TEXT, options) {
if (block) {
S.input.push_directives_stack();
var a = block_();
if (S.input.has_directive("use strict")) {
if (name) strict_verify_symbol(name);
if (args) args.forEach(strict_verify_symbol);
}
if (name) _verify_symbol(name);
if (args) args.forEach(_verify_symbol);
S.input.pop_directives_stack();
} else {
var a = expression(false);
@@ -1878,7 +1860,7 @@ function parse($TEXT, options) {
name : as_symbol(sym_type),
value : is("operator", "=")
? (next(), expression(false, no_in))
: !no_in && kind === "const" && S.input.has_directive("use strict")
: !no_in && kind === "const"
? croak("Missing initializer in const declaration") : null,
end : prev()
});
@@ -1934,12 +1916,14 @@ function parse($TEXT, options) {
} else {
args = [];
}
return subscripts(new AST_New({
var call = new AST_New({
start : start,
expression : newexp,
args : args,
end : prev()
}), allow_calls);
});
mark_pure(call);
return subscripts(call, allow_calls);
};
function as_atom_node() {
@@ -2240,7 +2224,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectKeyVal({
start: start,
quote: start.quote,
key: name,
key: name instanceof AST_Node ? name : "" + name,
value: value,
end: prev()
}));
@@ -2297,7 +2281,7 @@ function parse($TEXT, options) {
if (typeof name === "string" || typeof name === "number") {
return new AST_SymbolMethod({
start: token,
name: name,
name: "" + name,
end: prev()
});
} else if (name === null) {
@@ -2588,9 +2572,14 @@ function parse($TEXT, options) {
unexpected(tmp);
}
case "name":
if (tmp.value == "yield" && !is_token(peek(), "punc", ":") && !is_token(peek(), "punc", "(")
&& S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "Unexpected yield identifier inside strict mode");
if (tmp.value == "yield") {
if (is_in_generator()) {
token_error(tmp, "Yield cannot be used as identifier inside generators");
} else if (!is_token(peek(), "punc", ":")
&& !is_token(peek(), "punc", "(")
&& S.input.has_directive("use strict")) {
token_error(tmp, "Unexpected yield identifier inside strict mode");
}
}
case "string":
case "num":
@@ -2621,9 +2610,19 @@ function parse($TEXT, options) {
});
};
function strict_verify_symbol(sym) {
if (sym.name == "arguments" || sym.name == "eval")
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
function _verify_symbol(sym) {
var name = sym.name;
if (is_in_generator() && name == "yield") {
token_error(sym.start, "Yield cannot be used as identifier inside generators");
}
if (S.input.has_directive("use strict")) {
if (name == "yield") {
token_error(sym.start, "Unexpected yield identifier inside strict mode");
}
if (sym instanceof AST_SymbolDeclaration && (name == "arguments" || name == "eval")) {
token_error(sym.start, "Unexpected " + name + " in strict mode");
}
}
}
function as_symbol(type, noerror) {
@@ -2631,13 +2630,8 @@ function parse($TEXT, options) {
if (!noerror) croak("Name expected");
return null;
}
if (is("name", "yield") && S.input.has_directive("use strict")) {
token_error(S.prev, "Unexpected yield identifier inside strict mode");
}
var sym = _make_symbol(type);
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
strict_verify_symbol(sym);
}
_verify_symbol(sym);
next();
return sym;
};
@@ -2738,7 +2732,7 @@ function parse($TEXT, options) {
return ex;
}
var val = expr_atom(allow_calls, allow_arrows);
while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
while (is("operator") && UNARY_POSTFIX(S.token.value) && !has_newline_before(S.token)) {
if (val instanceof AST_Arrow) unexpected();
val = make_unary(AST_UnaryPostfix, S.token, val);
val.start = start;
@@ -2865,7 +2859,7 @@ function parse($TEXT, options) {
next();
return _yield_expression();
} else if (S.input.has_directive("use strict")) {
token_error(S.token, "Unexpected yield identifier inside strict mode")
token_error(S.token, "Unexpected yield identifier inside strict mode");
}
}

View File

@@ -130,12 +130,15 @@ function mangle_properties(ast, options) {
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var cache = options.cache;
if (cache == null) {
cache = {
cname: -1,
props: new Dictionary()
};
var cname = -1;
var cache;
if (options.cache) {
cache = options.cache.props;
cache.each(function(mangled_name) {
push_uniq(reserved, mangled_name);
});
} else {
cache = new Dictionary();
}
var regex = options.regex;
@@ -192,7 +195,7 @@ function mangle_properties(ast, options) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.props.has(name);
return cache.has(name);
}
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true;
@@ -201,7 +204,7 @@ function mangle_properties(ast, options) {
function should_mangle(name) {
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
return cache.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
@@ -219,7 +222,7 @@ function mangle_properties(ast, options) {
return name;
}
var mangled = cache.props.get(name);
var mangled = cache.get(name);
if (!mangled) {
if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
@@ -233,11 +236,11 @@ function mangle_properties(ast, options) {
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
do {
mangled = base54(++cache.cname);
mangled = base54(++cname);
} while (!can_mangle(mangled));
}
cache.props.set(name, mangled);
cache.set(name, mangled);
}
return mangled;
}

View File

@@ -43,9 +43,10 @@
"use strict";
function SymbolDef(scope, orig) {
function SymbolDef(scope, orig, init) {
this.name = orig.name;
this.orig = [ orig ];
this.init = init;
this.eliminated = 0;
this.scope = scope;
this.references = [];
@@ -175,7 +176,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
@@ -183,10 +184,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node), 1);
mark_export((node.scope = defun.parent_scope.get_defun_scope()).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolClass) {
mark_export(defun.def_variable(node), 1);
mark_export(defun.def_variable(node, defun), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node);
@@ -194,12 +195,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
mark_export((node.scope = defun.parent_scope).def_function(node), 1);
mark_export((node.scope = defun.parent_scope).def_function(node, defun), 1);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node);
var def;
if (node instanceof AST_SymbolBlockDeclaration) {
def = scope.def_variable(node, null);
} else {
def = defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
}
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
@@ -284,6 +290,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
node.thedef = sym;
node.reference(options);
if (node.scope.is_block_scope()
&& !(sym.orig[0] instanceof AST_SymbolBlockDeclaration)) {
node.scope = node.scope.get_defun_scope();
}
return true;
}
// ensure mangling works if catch reuses a scope variable
@@ -330,10 +340,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
});
}
}
if (options.cache) {
this.cname = options.cache.cname;
}
});
AST_Toplevel.DEFMETHOD("def_global", function(node){
@@ -403,29 +409,32 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
var def = this.def_variable(symbol);
AST_Scope.DEFMETHOD("def_function", function(symbol, init){
var def = this.def_variable(symbol, init);
if (!def.init || def.init instanceof AST_Defun) def.init = init;
this.functions.set(symbol.name, def);
return def;
});
AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, symbol);
AST_Scope.DEFMETHOD("def_variable", function(symbol, init){
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) {
def.init = init;
}
} else {
def = new SymbolDef(this, symbol, init);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
}
return symbol.thedef = def;
});
AST_Scope.DEFMETHOD("next_mangled", function(options){
var ext = this.enclosed;
function next_mangled(scope, options) {
var ext = scope.enclosed;
out: while (true) {
var m = base54(++this.cname);
var m = base54(++scope.cname);
if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
@@ -442,6 +451,18 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
}
return m;
}
}
AST_Scope.DEFMETHOD("next_mangled", function(options){
return next_mangled(this, options);
});
AST_Toplevel.DEFMETHOD("next_mangled", function(options){
var name;
do {
name = next_mangled(this, options);
} while (member(name, this.mangled_names));
return name;
});
AST_Function.DEFMETHOD("next_mangled", function(options, def){
@@ -455,7 +476,7 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
var name = next_mangled(this, options);
if (!tricky_name || tricky_name != name)
return name;
}
@@ -507,8 +528,14 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var lname = -1;
var to_mangle = [];
var mangled_names = this.mangled_names = [];
if (options.cache) {
this.globals.each(collect);
if (options.cache.props) {
options.cache.props.each(function(mangled_name) {
push_uniq(mangled_names, mangled_name);
});
}
}
var tw = new TreeWalker(function(node, descend){
@@ -523,28 +550,23 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.variables.each(collect);
return;
}
if (node.is_block_scope()) {
node.block_scope.variables.each(collect);
return;
}
if (node instanceof AST_Label) {
var name;
do name = base54(++lname); while (!is_identifier(name));
node.mangled_name = name;
return true;
}
var mangle_with_block_scope =
(!options.ie8 && node instanceof AST_SymbolCatch) ||
node instanceof AST_SymbolBlockDeclaration;
if (mangle_with_block_scope && options.reserved.indexOf(node.name) < 0) {
if (!options.ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition());
return;
}
});
this.walk(tw);
to_mangle.forEach(function(def){
def.mangle(options);
});
if (options.cache) {
options.cache.cname = this.cname;
}
to_mangle.forEach(function(def){ def.mangle(options) });
function collect(symbol) {
if (!member(symbol.name, options.reserved)) {
@@ -553,59 +575,55 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
}
});
AST_Toplevel.DEFMETHOD("find_unique_prefix", function(options) {
var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props;
var prefixes = Object.create(null);
options.reserved.forEach(add_prefix);
var avoid = Object.create(null);
options.reserved.forEach(to_avoid);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
}));
var prefix, i = 0;
do {
prefix = create_name(i++);
} while (prefixes[prefix]);
return prefix;
return avoid;
function add_prefix(name) {
if (/[0-9]$/.test(name)) {
prefixes[name.replace(/[0-9]+$/, "")] = true;
}
function to_avoid(name) {
avoid[name] = true;
}
function add_def(def) {
var name = def.name;
if (def.global && cache && cache.has(name)) name = cache.get(name);
else if (!def.unmangleable(options)) return;
add_prefix(name);
}
function create_name(num) {
var name = "";
do {
name += letters[num % letters.length];
num = Math.floor(num / letters.length);
} while (num);
return name;
to_avoid(name);
}
});
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
base54.reset();
base54.sort();
options = this._default_mangler_options(options);
var prefix = this.find_unique_prefix(options);
var avoid = this.find_colliding_names(options);
var cname = 0;
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
}));
function next_name() {
var name;
do {
name = base54(cname++);
} while (avoid[name] || !is_identifier(name));
return name;
}
function rename(def) {
if (def.global || def.unmangleable(options)) return;
if (def.global && options.cache) return;
if (def.unmangleable(options)) return;
if (member(def.name, options.reserved)) return;
var d = def.redefined();
def.name = d ? d.name : prefix + def.id;
def.name = d ? d.name : next_name();
def.orig.forEach(function(sym) {
sym.name = def.name;
});

View File

@@ -303,6 +303,13 @@ Dictionary.prototype = {
ret.push(f(this._values[i], i.substr(1)));
return ret;
},
clone: function() {
var ret = new Dictionary();
for (var i in this._values)
ret._values[i] = this._values[i];
ret._size = this._size;
return ret;
},
toObject: function() { return this._values }
};
Dictionary.fromObject = function(obj) {

View File

@@ -4,17 +4,15 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.3.1",
"version": "3.3.9",
"engines": {
"node": ">=0.8.0"
},
"maintainers": [
"Alex Lam <alexlamsl@gmail.com>",
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
],
"repository": "git+https://github.com/mishoo/UglifyJS2.git#harmony",
"bugs": {
"url": "https://github.com/mishoo/UglifyJS2/issues"
},
"repository": "https://github.com/mishoo/UglifyJS2.git#harmony",
"main": "tools/node.js",
"bin": {
"uglifyjs": "bin/uglifyjs"
@@ -26,11 +24,11 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.12.1",
"commander": "~2.13.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.2.1",
"acorn": "~5.3.0",
"mocha": "~3.5.1",
"semver": "~5.4.1"
},

View File

@@ -41,7 +41,7 @@ do_not_remove_anon_blocks_if_they_have_decls: {
var x;
}
{
const y;
const y = 1;
class Zee {};
}
}
@@ -59,7 +59,7 @@ do_not_remove_anon_blocks_if_they_have_decls: {
}
var x;
{
const y;
const y = 1;
class Zee {}
}
}
@@ -77,12 +77,12 @@ remove_unused_in_global_block: {
input: {
{
let x;
const y;
const y = 1;
class Zee {};
var w;
}
let ex;
const why;
const why = 2;
class Zed {};
var wut;
console.log(x, y, Zee);
@@ -90,7 +90,7 @@ remove_unused_in_global_block: {
expect: {
var w;
let ex;
const why;
const why = 2;
class Zed {};
var wut;
console.log(x, y, Zee);

View File

@@ -75,6 +75,7 @@ issue_1664: {
}
expect_stdout: "1"
node_version: ">=6"
reminify: false // FIXME - block scoped function
}
issue_1672_for: {

View File

@@ -2296,6 +2296,7 @@ toplevel_single_reference: {
unused_orig: {
options = {
collapse_vars: true,
dead_code: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
@@ -2323,7 +2324,7 @@ unused_orig: {
var c = b;
for (var d in c) {
var a;
return --b + (a = c[0]);
return --b + c[0];
}
a && a.NaN;
}([2]), a);
@@ -4180,10 +4181,9 @@ recursive_function_replacement: {
console.log(f(c));
}
expect: {
function f(n) {
return x(y(f(n)));
}
console.log(f(c));
console.log(function n(o) {
return x(y(n(o)));
}(c));
}
}
@@ -4314,3 +4314,103 @@ replace_all_var: {
}
expect_stdout: "PASS"
}
cascade_statement: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
var c;
if (a)
return c = b, c || a;
else
c = a, c(b);
}
function f2(a, b) {
var c;
while (a)
c = b, a = c + b;
do
throw c = a + b, c;
while (c);
}
function f3(a, b) {
for (; a < b; a++)
if (c = a, c && b)
var c = (c = b(a), c);
}
}
expect: {
function f1(a, b) {
var c;
if (a)
return (c = b) || a;
else
(c = a)(b);
}
function f2(a, b) {
var c;
while (a)
a = (c = b) + b;
do
throw c = a + b;
while (c);
}
function f3(a, b) {
for (; a < b; a++)
if ((c = a) && b)
var c = c = b(a);
}
}
}
cascade_forin: {
options = {
collapse_vars: true,
}
input: {
var a;
function f(b) {
return [ b, b, b ];
}
for (var c in a = console, f(a))
console.log(c);
}
expect: {
var a;
function f(b) {
return [ b, b, b ];
}
for (var c in f(a = console))
console.log(c);
}
expect_stdout: [
"0",
"1",
"2",
]
}
unsafe_builtin: {
options = {
collapse_vars: true,
pure_getters: "strict",
unsafe: true,
unused: true,
}
input: {
function f(a) {
var b = Math.abs(a);
return Math.pow(b, 2);
}
console.log(f(-1), f(2));
}
expect: {
function f(a) {
return Math.pow(Math.abs(a), 2);
}
console.log(f(-1), f(2));
}
expect_stdout: "1 4"
}

View File

@@ -1203,3 +1203,67 @@ issue_2560: {
"2",
]
}
hoist_decl: {
options = {
conditionals: true,
join_vars: true,
sequences: true,
}
input: {
if (x()) {
var a;
y();
} else {
z();
var b;
}
}
expect: {
var a, b;
x() ? y() : z();
}
}
to_and_or: {
options = {
conditionals: true,
}
input: {
var values = [
0,
null,
true,
"foo",
false,
-1 / 0,
void 0,
];
values.forEach(function(x) {
values.forEach(function(y) {
values.forEach(function(z) {
console.log(x ? y || z : z);
});
});
});
}
expect: {
var values = [
0,
null,
true,
"foo",
false,
-1 / 0,
void 0,
];
values.forEach(function(x) {
values.forEach(function(y) {
values.forEach(function(z) {
console.log(x && y || z);
});
});
});
}
expect_stdout: true
}

View File

@@ -60,6 +60,7 @@ dead_code_2_should_warn: {
}
expect_stdout: true
node_version: ">=6"
reminify: false // FIXME - block scoped function
}
dead_code_2_should_warn_strict: {
@@ -100,6 +101,7 @@ dead_code_2_should_warn_strict: {
}
expect_stdout: true
node_version: ">=4"
reminify: false // FIXME - block scoped function
}
dead_code_constant_boolean_should_warn_more: {
@@ -135,6 +137,7 @@ dead_code_constant_boolean_should_warn_more: {
}
expect_stdout: true
node_version: ">=6"
reminify: false // FIXME - block scoped function
}
dead_code_constant_boolean_should_warn_more_strict: {
@@ -171,6 +174,7 @@ dead_code_constant_boolean_should_warn_more_strict: {
}
expect_stdout: true
node_version: ">=4"
reminify: false // FIXME - block scoped function
}
dead_code_block_decls_die: {
@@ -1047,3 +1051,130 @@ issue_2597: {
}
expect_stdout: "PASS"
}
issue_2666: {
options = {
dead_code: true,
}
input: {
function f(a) {
return a = {
p: function() {
return a;
}
};
}
console.log(typeof f().p());
}
expect: {
function f(a) {
return a = {
p: function() {
return a;
}
};
}
console.log(typeof f().p());
}
expect_stdout: "object"
}
issue_2692: {
options = {
dead_code: true,
reduce_vars: false,
}
input: {
function f(a) {
return a = g;
function g() {
return a;
}
}
console.log(typeof f()());
}
expect: {
function f(a) {
return a = g;
function g() {
return a;
}
}
console.log(typeof f()());
}
expect_stdout: "function"
}
issue_2701: {
options = {
dead_code: true,
inline: false,
}
input: {
function f(a) {
return a = function() {
return function() {
return a;
};
}();
}
console.log(typeof f()());
}
expect: {
function f(a) {
return a = function() {
return function() {
return a;
};
}();
}
console.log(typeof f()());
}
expect_stdout: "function"
}
issue_2749: {
options = {
dead_code: true,
inline: true,
toplevel: true,
unused: true,
}
input: {
var a = 2, c = "PASS";
while (a--)
(function() {
return b ? c = "FAIL" : b = 1;
try {
} catch (b) {
var b;
}
})();
console.log(c);
}
expect: {
var a = 2, c = "PASS";
while (a--)
b = void 0, b ? c = "FAIL" : b = 1;
var b;
console.log(c);
}
expect_stdout: "PASS"
}
unsafe_builtin: {
options = {
side_effects: true,
unsafe: true,
}
input: {
(!w).constructor(x);
Math.abs(y);
[ 1, 2, z ].valueOf();
}
expect: {
w, x;
y;
z;
}
}

View File

@@ -215,7 +215,7 @@ unused_block_decls: {
input: {
function foo() {
{
const x;
const x = 1;
}
{
let y;
@@ -1237,6 +1237,7 @@ var_catch_toplevel: {
options = {
conditionals: true,
negate_iife: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
@@ -1786,3 +1787,309 @@ issue_2418_5: {
(function f() {});
}
}
defun_lambda_same_name: {
options = {
toplevel: true,
unused: true,
}
input: {
function f(n) {
return n ? n * f(n - 1) : 1;
}
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect: {
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect_stdout: "120"
}
issue_2660_1: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 2;
function f(b) {
return b && f() || a--;
}
f(1);
console.log(a);
}
expect: {
var a = 2;
(function f(b) {
return b && f() || a--;
})(1);
console.log(a);
}
expect_stdout: "1"
}
issue_2660_2: {
options = {
collapse_vars: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
function f(b) {
b && f();
--a, a.toString();
}
f();
console.log(a);
}
expect: {
var a = 1;
(function f(b) {
b && f(),
(--a).toString();
})(),
console.log(a);
}
expect_stdout: "0"
}
issue_2665: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var a = 1;
function g() {
a-- && g();
}
typeof h == "function" && h();
function h() {
typeof g == "function" && g();
}
console.log(a);
}
expect: {
var a = 1;
!function g() {
a-- && g();
}();
console.log(a);
}
expect_stdout: "-1"
}
double_assign_1: {
options = {
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var a = {};
var a = [];
return a;
}
function f2() {
var a = {};
a = [];
return a;
}
function f3() {
a = {};
var a = [];
return a;
}
function f4(a) {
a = {};
a = [];
return a;
}
function f5(a) {
var a = {};
a = [];
return a;
}
function f6(a) {
a = {};
var a = [];
return a;
}
console.log(f1(), f2(), f3(), f4(), f5(), f6());
}
expect: {
function f1() {
return [];
}
function f2() {
var a;
a = [];
return a;
}
function f3() {
return [];
}
function f4(a) {
a = [];
return a;
}
function f5(a) {
a = [];
return a;
}
function f6(a) {
a = [];
return a;
}
console.log(f1(), f2(), f3(), f4(), f5(), f6());
}
expect_stdout: true
}
double_assign_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 2; i++)
a = void 0, a = {}, console.log(a);
var a;
}
expect: {
for (var i = 0; i < 2; i++)
void 0, a = {}, console.log(a);
var a;
}
}
double_assign_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 2; i++)
a = void 0, a = { a: a }, console.log(a);
var a;
}
expect: {
for (var i = 0; i < 2; i++)
a = void 0, a = { a: a }, console.log(a);
var a;
}
}
cascade_drop_assign: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a, b = a = "PASS";
console.log(b);
}
expect: {
var b = "PASS";
console.log(b);
}
expect_stdout: "PASS"
}
chained_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect: {
console.log(function(a, b) {
var c = b;
b++;
return c;
}(0, 2));
}
expect_stdout: "2"
}
issue_2768: {
options = {
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL", c = 1;
var c = function(b) {
var d = b = a;
var e = --b + (d && (a = "PASS"));
}();
console.log(a, typeof c);
}
expect: {
var a = "FAIL";
var c = (d = a, 0, void (d && (a = "PASS")));
var d;
console.log(a, typeof c);
}
expect_stdout: "PASS undefined"
}
issue_2846: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
var a = 0;
b && b(a);
return a++;
}
var c = f();
console.log(c);
}
expect: {
var c = function(a, b) {
a = 0;
b && b(a);
return a++;
}();
console.log(c);
}
expect_stdout: "0"
}

View File

@@ -1287,6 +1287,9 @@ issue_2231_1: {
console.log(Object.keys(void 0));
}
expect_stdout: true
expect_warnings: [
"WARN: Error evaluating Object.keys(void 0) [test/compress/evaluate.js:1284,20]",
]
}
issue_2231_2: {
@@ -1301,6 +1304,23 @@ issue_2231_2: {
console.log(Object.getOwnPropertyNames(null));
}
expect_stdout: true
expect_warnings: [
"WARN: Error evaluating Object.getOwnPropertyNames(null) [test/compress/evaluate.js:1301,20]",
]
}
issue_2231_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.keys({ foo: "bar" })[0]);
}
expect: {
console.log("foo");
}
expect_stdout: "foo"
}
self_comparison_1: {
@@ -1423,13 +1443,27 @@ issue_2535_3: {
}
expect_stdout: true
expect_warnings: [
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1409,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1410,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1411,20]",
"WARN: Condition left of && always false [test/compress/evaluate.js:1411,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1412,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1413,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1414,20]",
"WARN: Condition left of || always true [test/compress/evaluate.js:1414,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1429,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1430,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1431,20]",
"WARN: Condition left of && always false [test/compress/evaluate.js:1431,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1432,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1433,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1434,20]",
"WARN: Condition left of || always true [test/compress/evaluate.js:1434,20]",
]
}
issue_2822: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([ function() {}, "PASS", "FAIL" ][1]);
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -258,7 +258,7 @@ issue_203: {
options = {
keep_fargs: false,
side_effects: true,
unsafe_Func: true,
unsafe_Function: true,
unused: true,
}
input: {
@@ -800,12 +800,12 @@ issue_2601_1: {
expect: {
var a = "FAIL";
(function() {
var b;
b = "foo",
function(b) {
b && b();
}(),
b && (a = "PASS");
var b;
})(),
console.log(a);
}
@@ -1318,7 +1318,7 @@ issue_2630_1: {
expect: {
var c = 0;
(function() {
while (c++, void (c = 1 + c));
while (void (c = 1 + ++c));
})(),
console.log(c);
}
@@ -1332,6 +1332,7 @@ issue_2630_2: {
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
@@ -1349,7 +1350,7 @@ issue_2630_2: {
expect: {
var c = 0;
!function() {
while (c += 1, void (c = 1 + c));
while (void (c = 1 + (c += 1)));
}(), console.log(c);
}
expect_stdout: "2"
@@ -1378,11 +1379,10 @@ issue_2630_3: {
expect: {
var x = 2, a = 1;
(function() {
function f1(a) {
(function f1(a) {
f2();
--x >= 0 && f1({});
}
f1(a++);
})(a++);
function f2() {
a++;
}
@@ -1450,7 +1450,7 @@ issue_2630_5: {
!function() {
do {
c *= 10;
} while (c += 3, (c = 2 + c) < 100);
} while ((c = 2 + (c += 3)) < 100);
}();
console.log(c);
}
@@ -1516,7 +1516,8 @@ issue_2647_2: {
}
expect: {
(function() {
console.log("pass".toUpperCase());
console.log((() => (x = "pass", x.toUpperCase()))());
var x;
})();
}
expect_stdout: "PASS"
@@ -1549,7 +1550,7 @@ issue_2647_3: {
node_version: ">=4"
}
recursive_inline: {
recursive_inline_1: {
options = {
inline: true,
reduce_funcs: true,
@@ -1572,3 +1573,585 @@ recursive_inline: {
}
expect: {}
}
recursive_inline_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(n) {
return n ? n * f(n - 1) : 1;
}
console.log(f(5));
}
expect: {
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect_stdout: "120"
}
issue_2657: {
options = {
inline: true,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
"use strict";
console.log(function f() {
return h;
function g(b) {
return b || b();
}
function h(a) {
g(a);
return a;
}
}()(42));
}
expect: {
"use strict";
console.log(function(a) {
return b = a, b || b(), a;
var b;
}(42));
}
expect_stdout: "42"
}
issue_2663_1: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var i, o = {};
function createFn(j) {
return function() {
console.log(j);
};
}
for (i in { a: 1, b: 2, c: 3 })
o[i] = createFn(i);
for (i in o)
o[i]();
})();
}
expect: {
(function() {
var i, o = {};
function createFn(j) {
return function() {
console.log(j);
};
}
for (i in { a: 1, b: 2, c: 3 })
o[i] = createFn(i);
for (i in o)
o[i]();
})();
}
expect_stdout: [
"a",
"b",
"c",
]
}
issue_2663_2: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var i;
function fn(j) {
return function() {
console.log(j);
}();
}
for (i in { a: 1, b: 2, c: 3 })
fn(i);
})();
}
expect: {
(function() {
var i;
for (i in { a: 1, b: 2, c: 3 })
j = i, console.log(j);
var j;
})();
}
expect_stdout: [
"a",
"b",
"c",
]
}
issue_2663_3: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function () {
var outputs = [
{ type: 0, target: null, eventName: "ngSubmit", propName: null },
{ type: 0, target: null, eventName: "submit", propName: null },
{ type: 0, target: null, eventName: "reset", propName: null },
];
function listenToElementOutputs(outputs) {
var handlers = [];
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var handleEventClosure = renderEventHandlerClosure(output.eventName);
handlers.push(handleEventClosure)
}
var target, name;
return handlers;
}
function renderEventHandlerClosure(eventName) {
return function () {
return console.log(eventName);
};
}
listenToElementOutputs(outputs).forEach(function (handler) {
return handler()
});
})();
}
expect: {
(function() {
function renderEventHandlerClosure(eventName) {
return function() {
return console.log(eventName);
};
}
(function(outputs) {
var handlers = [];
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var handleEventClosure = renderEventHandlerClosure(output.eventName);
handlers.push(handleEventClosure);
}
return handlers;
})([ {
type: 0,
target: null,
eventName: "ngSubmit",
propName: null
}, {
type: 0,
target: null,
eventName: "submit",
propName: null
}, {
type: 0,
target: null,
eventName: "reset",
propName: null
} ]).forEach(function(handler) {
return handler();
});
})();
}
expect_stdout: [
"ngSubmit",
"submit",
"reset",
]
}
duplicate_argnames: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f(b, b, b) {
b && (a = "FAIL");
}
f(0, console);
console.log(a);
}
expect: {
var a = "PASS";
console, b && (a = "FAIL");
var b;
console.log(a);
}
expect_stdout: "PASS"
}
loop_init_arg: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
for (var k in "12") (function (b) {
(b >>= 1) && (a = "FAIL"), b = 2;
})();
console.log(a);
}
expect: {
var a = "PASS";
for (var k in "12")
b = void 0, (b >>= 1) && (a = "FAIL"), b = 2;
var b;
console.log(a);
}
expect_stdout: "PASS"
}
inline_false: {
options = {
inline: false,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_0: {
options = {
inline: 0,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_1: {
options = {
inline: 1,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_2: {
options = {
inline: 2,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
a = 2, console.log(a);
var a;
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_3: {
options = {
inline: 3,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
a = 2, console.log(a);
var a;
b = 3, c = b, console.log(c);
var b, c;
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_true: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
a = 2, console.log(a);
var a;
b = 3, c = b, console.log(c);
var b, c;
}
expect_stdout: [
"1",
"2",
"3",
]
}
use_before_init_in_loop: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
for (var b = 2; --b >= 0;) (function() {
var c = function() {
return 1;
}(c && (a = "FAIL"));
})();
console.log(a);
}
expect: {
var a = "PASS";
for (var b = 2; --b >= 0;)
c = void 0, c = (c && (a = "FAIL"), 1);
var c;
console.log(a);
}
expect_stdout: "PASS"
}
duplicate_arg_var: {
options = {
inline: true,
toplevel: true,
}
input: {
console.log(function(b) {
return b;
var b;
}("PASS"));
}
expect: {
console.log((b = "PASS", b));
var b;
}
expect_stdout: "PASS"
}
issue_2737_1: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
while (a());
})(function f() {
console.log(typeof f);
});
}
expect: {
(function(a) {
while (a());
})(function f() {
console.log(typeof f);
});
}
expect_stdout: "function"
}
issue_2737_2: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(bar) {
for (;bar(); ) break;
})(function qux() {
return console.log("PASS"), qux;
});
}
expect: {
(function(bar) {
for (;bar(); ) break;
})(function qux() {
return console.log("PASS"), qux;
});
}
expect_stdout: "PASS"
}
issue_2783: {
options = {
collapse_vars: true,
conditionals: true,
if_return: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
return g;
function f(a) {
var b = a.b;
if (b) return b;
return a;
}
function g(o, i) {
while (i--) {
console.log(f(o));
}
}
})()({ b: "PASS" }, 1);
}
expect: {
(function() {
return function(o,i) {
while (i--) console.log(f(o));
};
function f(a) {
var b = a.b;
return b || a;
}
})()({ b: "PASS" },1);
}
expect_stdout: "PASS"
}

View File

@@ -1197,3 +1197,245 @@ class_method_using_arguments: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_2676: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
class A {}
A.a = 42;
}
expect: {
(class {}).a = 42;
}
}
issue_2762: {
mangle = {}
input: {
var bar = 1, T = true;
(function() {
if (T) {
const a = function() {
var foo = bar;
console.log(foo, a.prop, b.prop);
};
a.prop = 2;
const b = { prop: 3 };
a();
}
})();
}
expect: {
var bar = 1, T = true;
(function() {
if (T) {
const o = function() {
var p = bar;
console.log(p, o.prop, r.prop);
};
o.prop = 2;
const r = { prop: 3 };
o();
}
})();
}
expect_stdout: "1 2 3"
}
issue_2794_1: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 1,
properties: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: false,
side_effects: true,
unused: true,
}
input: {
function foo() {
for (const a of func(value)) {
console.log(a);
}
function func(va) {
return doSomething(va);
}
}
function doSomething(x) {
return [ x, 2 * x, 3 * x ];
}
const value = 10;
foo();
}
expect: {
function foo() {
for (const a of doSomething(value)) console.log(a);
}
function doSomething(x) {
return [ x, 2 * x, 3 * x ];
}
const value = 10;
foo();
}
expect_stdout: [
"10",
"20",
"30",
]
node_version: ">=6"
}
issue_2794_2: {
mangle = {
toplevel: false,
}
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 1,
properties: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: false,
side_effects: true,
unused: true,
}
input: {
function foo() {
for (const a of func(value)) {
console.log(a);
}
function func(va) {
return doSomething(va);
}
}
function doSomething(x) {
return [ x, 2 * x, 3 * x ];
}
const value = 10;
foo();
}
expect: {
function foo() {
for (const o of doSomething(value)) console.log(o);
}
function doSomething(o) {
return [ o, 2 * o, 3 * o ];
}
const value = 10;
foo();
}
expect_stdout: [
"10",
"20",
"30",
]
node_version: ">=6"
}
issue_2794_3: {
mangle = {
toplevel: true,
}
options = {
collapse_vars: true,
evaluate: true,
inline: 3,
passes: 3,
properties: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
side_effects: true,
unused: true,
}
input: {
function foo() {
for (const a of func(value)) {
console.log(a);
}
function func(va) {
return doSomething(va);
}
}
function doSomething(x) {
return [ x, 2 * x, 3 * x ];
}
const value = 10;
foo();
}
expect: {
(function() {
for (const o of [ 10, 20, 30 ]) console.log(o);
})();
}
expect_stdout: [
"10",
"20",
"30",
]
node_version: ">=6"
}
issue_2794_4: {
options = {}
input: {
for (var x of ([1, 2], [3, 4])) {
console.log(x);
}
}
expect_exact: "for(var x of([1,2],[3,4]))console.log(x);"
expect_stdout: [
"3",
"4",
]
node_version: ">=6"
}
issue_2794_5: {
mangle = {}
options = {
evaluate: true,
passes: 1,
side_effects: true,
unused: true,
}
input: {
for (var x of ([1, 2], [3, 4])) {
console.log(x);
}
}
expect_exact: "for(var x of[3,4])console.log(x);"
expect_stdout: [
"3",
"4",
]
node_version: ">=6"
}
issue_2794_6: {
options = {
}
input: {
// TODO (or not): have parser flag invalid for-of expression.
// Consider it an uglify extension in the meantime.
for (let e of [1,2], [3,4,5]) {
console.log(e);
}
}
expect_exact: "for(let e of([1,2],[3,4,5]))console.log(e);"
expect_stdout: [
"3",
"4",
"5",
]
node_version: ">=6"
}

View File

@@ -827,3 +827,95 @@ issue_2519: {
}
expect_stdout: "5.5"
}
toplevel_const: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: false,
}
input: {
const a = {
b: 1,
c: 2
};
console.log(a.b + a.c);
}
expect: {
const a = {
b: 1,
c: 2
};
console.log(a.b + a.c);
}
expect_stdout: "3"
}
toplevel_let: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: false,
}
input: {
let a = {
b: 1,
c: 2
};
console.log(a.b + a.c);
}
expect: {
let a = {
b: 1,
c: 2
};
console.log(a.b + a.c);
}
expect_stdout: "3"
node_version: ">=6"
}
toplevel_var: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: false,
}
input: {
var a = {
b: 1,
c: 2
};
console.log(a.b + a.c);
}
expect: {
var a = {
b: 1,
c: 2
};
console.log(a.b + a.c);
}
expect_stdout: "3"
}
undefined_key: {
options = {
evaluate: true,
hoist_props: true,
join_vars: true,
passes: 4,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a, o = {};
o[a] = 1;
o.b = 2;
console.log(o[a] + o.b);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}

View File

@@ -384,3 +384,109 @@ issue_1317_strict: {
expect_stdout: "1"
node_version: ">=4"
}
if_var_return: {
options = {
conditionals: true,
if_return: true,
join_vars: true,
sequences: true,
}
input: {
function f() {
var a;
return;
var b;
}
function g() {
var a;
if (u()) {
var b;
return v();
var c;
}
var d;
if (w()) {
var e;
return x();
var f;
} else {
var g;
y();
var h;
}
var i;
z();
var j;
}
}
expect: {
function f() {
var a, b;
}
function g() {
var a, b, c, d, e, f, g, h, i, j;
return u() ? v() : w() ? x() : (y(), z(), void 0);
}
}
}
if_if_return_return: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f(a, b) {
if (a) {
if (b)
return b;
return;
}
g();
}
}
expect: {
function f(a, b) {
if (a)
return b || void 0;
g();
}
}
}
issue_2747: {
options = {
conditionals: true,
if_return: true,
sequences: true,
unused: true,
}
input: {
"use strict";
function f(baz) {
if (baz === 0) {
return null;
}
let r;
if (baz > 2) {
r = 4;
} else {
r = 5;
}
return r;
}
console.log(f(0), f(1), f(3));
}
expect: {
"use strict";
function f(baz) {
if (0 === baz) return null;
let r;
return r = baz > 2 ? 4 : 5, r;
}
console.log(f(0), f(1), f(3));
}
expect_stdout: "null 5 4"
node_version: ">=4"
}

View File

@@ -4,7 +4,7 @@ unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true
unsafe_undefined: true,
}
mangle = {}
input: {
@@ -30,7 +30,7 @@ keep_fnames: {
options = {
conditionals: true,
if_return: true,
unsafe: true
unsafe_undefined: true,
}
mangle = {
keep_fnames: true

View File

@@ -38,6 +38,7 @@ same_variable_in_multiple_for_loop: {
}
}
expect_stdout: true
node_version: ">=6"
}
same_variable_in_multiple_forOf: {
@@ -79,6 +80,7 @@ same_variable_in_multiple_forOf: {
}
}
expect_stdout: true
node_version: ">=6"
}
same_variable_in_multiple_forIn: {
@@ -120,6 +122,7 @@ same_variable_in_multiple_forIn: {
}
}
expect_stdout: true
node_version: ">=6"
}
different_variable_in_multiple_for_loop: {
@@ -162,6 +165,7 @@ different_variable_in_multiple_for_loop: {
}
}
expect_stdout: true
node_version: ">=6"
}
different_variable_in_multiple_forOf: {
@@ -203,6 +207,7 @@ different_variable_in_multiple_forOf: {
}
}
expect_stdout: true
node_version: ">=6"
}
different_variable_in_multiple_forIn: {
@@ -244,6 +249,175 @@ different_variable_in_multiple_forIn: {
}
}
expect_stdout: true
node_version: ">=6"
}
same_variable_in_multiple_forOf_sequences_let: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
sequences: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp of test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let tmp of dd) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o of test) {
let e;
console.log(o), e = [ "e", "f", "g" ];
for (let o of e)
console.log(o);
}
}
expect_stdout: true
node_version: ">=6"
}
same_variable_in_multiple_forOf_sequences_const: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
sequences: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (const tmp of test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (const tmp of dd) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (const o of test) {
let t;
console.log(o), t = [ "e", "f", "g" ];
for (const o of t)
console.log(o);
}
}
expect_stdout: true
node_version: ">=6"
}
same_variable_in_multiple_forIn_sequences_let: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
sequences: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp in test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let tmp in test) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let e in test) {
let t;
console.log(e), t = [ "e", "f", "g" ];
for (let e in test)
console.log(e);
}
}
expect_stdout: true
node_version: ">=6"
}
same_variable_in_multiple_forIn_sequences_const: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
sequences: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (const tmp in test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (const tmp in test) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (const o in test) {
let t;
console.log(o), t = [ "e", "f", "g" ];
for (const o in test)
console.log(o);
}
}
expect_stdout: true
node_version: ">=6"
}
more_variable_in_multiple_for: {
@@ -281,4 +455,5 @@ more_variable_in_multiple_for: {
}
}
expect_stdout: true
node_version: ">=6"
}

View File

@@ -61,7 +61,7 @@ unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true,
unsafe_undefined: true,
}
mangle = {}
input: {

View File

@@ -84,12 +84,12 @@ numeric_literal: {
' 0: 0,',
' "-0": 1,',
' 42: 2,',
' "42": 3,',
' 42: 3,',
' 37: 4,',
' o: 5,',
' 1e42: 6,',
' b: 7,',
' "1e+42": 8',
' 1e42: 8',
'};',
'',
'console.log(obj[-0], obj[-""], obj["-0"]);',

View File

@@ -2,7 +2,7 @@
compress_new_function: {
options = {
unsafe: true,
unsafe_Func: true,
unsafe_Function: true,
}
input: {
new Function("aa, bb", 'return aa;');
@@ -15,7 +15,7 @@ compress_new_function: {
compress_new_function_with_destruct: {
options = {
unsafe: true,
unsafe_Func: true,
unsafe_Function: true,
ecma: 6
}
beautify = {
@@ -38,7 +38,7 @@ compress_new_function_with_destruct_arrows: {
arrows: true,
unsafe_arrows: true,
unsafe: true,
unsafe_Func: true,
unsafe_Function: true,
ecma: 6,
}
beautify = {

View File

@@ -0,0 +1,33 @@
insert_semicolon: {
beautify = {
beautify: true,
comments: "all",
}
input: {
var a
/* foo */ var b
}
expect_exact: [
"var a",
"/* foo */;",
"",
"var b;",
]
}
unary_postfix: {
beautify = {
beautify: true,
comments: "all",
}
input: {
a
/* foo */++b
}
expect_exact: [
"a",
"/* foo */;",
"",
"++b;",
]
}

View File

@@ -64,3 +64,27 @@ strings_concat: {
);
}
}
regexp: {
options = {
evaluate: true,
unsafe: true,
}
input: {
RegExp("foo");
RegExp("bar", "ig");
RegExp(foo);
RegExp("bar", ig);
RegExp("should", "fail");
}
expect: {
/foo/;
/bar/ig;
RegExp(foo);
RegExp("bar", ig);
RegExp("should", "fail");
}
expect_warnings: [
'WARN: Error converting RegExp("should","fail") [test/compress/issue-269.js:78,2]',
]
}

View File

@@ -0,0 +1,32 @@
warn: {
options = {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return g();
}
function g() {
return g["call" + "er"].arguments;
}
// 3
console.log(f(1, 2, 3).length);
}
expect: {
// TypeError: Cannot read property 'arguments' of null
console.log(function g() {
return g.caller.arguments;
}().length);
}
expect_warnings: [
"WARN: Function.protoype.caller not supported [test/compress/issue-2719.js:17,19]",
"WARN: Function.protoype.arguments not supported [test/compress/issue-2719.js:17,19]",
]
}

View File

@@ -536,3 +536,182 @@ dead_code_condition: {
}
expect_stdout: "1"
}
issue_2740_1: {
options = {
dead_code: true,
loops: true,
}
input: {
for (; ; ) break;
for (a(); ; ) break;
for (; b(); ) break;
for (c(); d(); ) break;
for (; ; e()) break;
for (f(); ; g()) break;
for (; h(); i()) break;
for (j(); k(); l()) break;
}
expect: {
a();
b();
c();
d();
f();
h();
j();
k();
}
}
issue_2740_2: {
options = {
dead_code: true,
loops: true,
passes: 2,
}
input: {
L1: while (x()) {
break L1;
}
}
expect: {
x();
}
}
issue_2740_3: {
options = {
dead_code: true,
loops: true,
}
input: {
L1: for (var x = 0; x < 3; x++) {
L2: for (var y = 0; y < 2; y++) {
break L1;
}
}
console.log(x, y);
}
expect: {
L1: for (var x = 0; x < 3; x++)
for (var y = 0; y < 2; y++)
break L1;
console.log(x, y);
}
expect_stdout: "0 0"
}
issue_2740_4: {
options = {
dead_code: true,
loops: true,
passes: 2,
}
input: {
L1: for (var x = 0; x < 3; x++) {
L2: for (var y = 0; y < 2; y++) {
break L2;
}
}
console.log(x, y);
}
expect: {
for (var x = 0; x < 3; x++) {
var y = 0;
y < 2;
}
console.log(x, y);
}
expect_stdout: "3 0"
}
issue_2740_5: {
options = {
dead_code: true,
loops: true,
passes: 2,
}
input: {
L1: for (var x = 0; x < 3; x++) {
break L1;
L2: for (var y = 0; y < 2; y++) {
break L2;
}
}
console.log(x, y);
}
expect: {
var x = 0;
x < 3;
var y;
console.log(x,y);
}
expect_stdout: "0 undefined"
}
issue_2740_6: {
options = {
dead_code: true,
loops: true,
}
input: {
const a = 9, b = 0;
for (const a = 1; a < 3; ++b) break;
console.log(a, b);
}
expect: {
const a = 9, b = 0;
{
const a = 1;
a < 3;
}
console.log(a, b);
}
expect_stdout: "9 0"
node_version: ">=6"
}
issue_2740_7: {
options = {
dead_code: true,
loops: true,
}
input: {
let a = 9, b = 0;
for (const a = 1; a < 3; ++b) break;
console.log(a, b);
}
expect: {
let a = 9, b = 0;
{
const a = 1;
a < 3;
}
console.log(a, b);
}
expect_stdout: "9 0"
node_version: ">=6"
}
issue_2740_8: {
options = {
dead_code: true,
loops: true,
}
input: {
var a = 9, b = 0;
for (const a = 1; a < 3; ++b) break;
console.log(a, b);
}
expect: {
var a = 9, b = 0;
{
const a = 1;
a < 3;
}
console.log(a, b);
}
expect_stdout: "9 0"
node_version: ">=6"
}

View File

@@ -569,16 +569,41 @@ native_prototype: {
}
input: {
Array.prototype.splice.apply(a, [1, 2, b, c]);
Function.prototype.call.apply(console.log, console, [ "foo" ]);
Number.prototype.toFixed.call(Math.PI, 2);
Object.prototype.hasOwnProperty.call(d, "foo");
RegExp.prototype.test.call(/foo/, "bar");
String.prototype.indexOf.call(e, "bar");
}
expect: {
[].splice.apply(a, [1, 2, b, c]);
(function() {}).call.apply(console.log, console, [ "foo" ]);
0..toFixed.call(Math.PI, 2);
({}).hasOwnProperty.call(d, "foo");
/t/.test.call(/foo/, "bar");
"".indexOf.call(e, "bar");
}
}
native_prototype_lhs: {
options = {
unsafe_proto: true,
}
input: {
console.log(function() {
Function.prototype.bar = "PASS";
return function() {};
}().bar);
}
expect: {
console.log(function() {
Function.prototype.bar = "PASS";
return function() {};
}().bar);
}
expect_stdout: "PASS"
}
accessor_boolean: {
input: {
var a = 1;
@@ -1371,3 +1396,501 @@ const_prop_assign_pure: {
x();
}
}
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_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_2816_ecma6: {
options = {
ecma: "6",
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,
a: 3,
c: 4
};
console.log(o.a, o.b, o.c);
}
expect_stdout: "3 2 4"
node_version: ">=4"
}

View File

@@ -414,3 +414,124 @@ issue_2638: {
"/* */(a()||b())(c(),d());",
]
}
issue_2705_1: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a();
/*@__PURE__*/ (new b());
new (/*@__PURE__*/ c)();
(/*@__PURE__*/ new d());
}
expect_exact: [
"new/* */c;",
]
}
issue_2705_2: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a(1)(2)(3);
/*@__PURE__*/ new (b(1))(2)(3);
/*@__PURE__*/ new (c(1)(2))(3);
/*@__PURE__*/ new (d(1)(2)(3));
new (/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ new f(1))(2)(3);
(/*@__PURE__*/ new g(1)(2))(3);
(/*@__PURE__*/ new h(1)(2)(3));
}
expect_exact: [
"new/* */e(1)(2)(3);",
"/* */new f(1)(2)(3);",
"/* */new g(1)(2)(3);",
]
}
issue_2705_3: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a.x(1).y(2).z(3);
/*@__PURE__*/ new (b.x)(1).y(2).z(3);
/*@__PURE__*/ new (c.x(1)).y(2).z(3);
/*@__PURE__*/ new (d.x(1).y)(2).z(3);
/*@__PURE__*/ new (e.x(1).y(2)).z(3);
/*@__PURE__*/ new (f.x(1).y(2).z)(3);
/*@__PURE__*/ new (g.x(1).y(2).z(3));
new (/*@__PURE__*/ h).x(1).y(2).z(3);
/* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ new j.x(1)).y(2).z(3);
(/*@__PURE__*/ new k.x(1).y)(2).z(3);
(/*@__PURE__*/ new l.x(1).y(2)).z(3);
(/*@__PURE__*/ new m.x(1).y(2).z)(3);
(/*@__PURE__*/ new n.x(1).y(2).z(3));
}
expect_exact: [
"new/* */h.x(1).y(2).z(3);",
"/* */new/* */i.x(1).y(2).z(3);",
"/* */new j.x(1).y(2).z(3);",
"/* */new k.x(1).y(2).z(3);",
"/* */new l.x(1).y(2).z(3);",
"/* */new m.x(1).y(2).z(3);",
]
}
issue_2705_4: {
options = {
side_effects: true,
}
input: {
(/*@__PURE__*/ new x(), y());
(w(), /*@__PURE__*/ new x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2705_5: {
options = {
side_effects: true,
}
input: {
[ /*@__PURE__*/ new x() ];
[ /*@__PURE__*/ new x(), y() ];
[ w(), /*@__PURE__*/ new x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2705_6: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/new (g() || h())(x(), y());
/* */ new (/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"/* */x(),y();",
"/* */new(/* */a()||b())(c(),d());",
]
}

View File

@@ -348,6 +348,57 @@ set_immutable_4: {
expect_stdout: true
}
set_immutable_5: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
1..foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_immutable_6: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
@@ -735,3 +786,62 @@ issue_2313_7: {
expect_stdout: "2 1"
node_version: ">=6"
}
issue_2678: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a = 1, c = "FAIL";
(function f() {
(a-- && f()).p;
return {
get p() {
c = "PASS";
}
};
})();
console.log(c);
}
expect: {
var a = 1, c = "FAIL";
(function f() {
(a-- && f()).p;
return {
get p() {
c = "PASS";
}
};
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_2838: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
function f(a, b) {
(a || b).c = "PASS";
(function() {
return f(a, b);
}).prototype.foo = "bar";
}
var o = {};
f(null, o);
console.log(o.c);
}
expect: {
function f(a, b) {
(a || b).c = "PASS";
}
var o = {};
f(null, o);
console.log(o.c);
}
expect_stdout: "PASS"
}

View File

@@ -299,7 +299,7 @@ unsafe_evaluate_modified: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());
@@ -1355,10 +1355,9 @@ defun_inline_1: {
function f() {
return function(b) {
return b;
}(2) + h();
function h() {
}(2) + function h() {
return h();
}
}();
}
}
}
@@ -1382,12 +1381,11 @@ defun_inline_2: {
}
expect: {
function f() {
function h() {
return h();
}
return function(b) {
return b;
}(2) + h();
}(2) + function h() {
return h();
}();
}
}
}
@@ -1557,10 +1555,10 @@ defun_label: {
!function() {
console.log(function(a) {
L: {
if (a) break L;
if (2) break L;
return 1;
}
}(2));
}());
}();
}
expect_stdout: true
@@ -2314,8 +2312,7 @@ delay_def: {
}
expect: {
function f() {
return a;
var a;
return;
}
function g() {
return a;
@@ -2326,6 +2323,28 @@ delay_def: {
expect_stdout: true
}
delay_def_lhs: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
long_name++;
return long_name;
var long_name;
}());
}
expect: {
console.log(function() {
long_name++;
return long_name;
var long_name;
}());
}
expect_stdout: "NaN"
}
booleans: {
options = {
booleans: true,
@@ -3368,9 +3387,7 @@ obj_for_1: {
console.log(i);
}
expect: {
// TODO make this work on harmony
var o = { a: 1 };
for (var i = o.a--; i; i--)
for (var i = { a: 1 }.a--; i; i--)
console.log(i);
}
expect_stdout: "1"
@@ -3409,9 +3426,7 @@ array_forin_1: {
console.log(b);
}
expect: {
// TODO make this work on harmony
var a = [ 1, 2, 3 ];
for (var b in a)
for (var b in [ 1, 2, 3 ])
console.log(b);
}
expect_stdout: [
@@ -3456,9 +3471,7 @@ array_forof_1: {
console.log(b);
}
expect: {
// TODO make this work on harmony
var a = [ 1, 2, 3 ];
for (var b of a)
for (var b of [ 1, 2, 3 ])
console.log(b);
}
expect_stdout: [
@@ -5162,10 +5175,8 @@ escape_local_throw: {
}
expect: {
function baz() {
// TODO: improve to match `master`
function foo() {}
try {
throw foo;
throw function() {};
} catch (bar) {
return bar;
}
@@ -5446,3 +5457,519 @@ issue_2598: {
}
expect_stdout: "true"
}
issue_2669: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
let foo;
console.log(([ foo ] = [ "PASS" ]) && foo);
}
expect: {
let foo;
console.log(([ foo ] = [ "PASS" ]) && foo);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_2670: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
const obj = {};
obj.prop = "PASS";
const {prop: value} = obj;
console.log(value);
}
expect: {
const obj = {};
obj.prop = "PASS";
const {prop: value} = obj;
console.log(value);
}
expect_stdout: "PASS"
node_version: ">=6"
}
var_if: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
if (x()) {
var a;
if (!g) a = true;
if (a) g();
}
}
}
expect: {
function f() {
if (x()) {
var a;
if (!g) a = true;
if (a) g();
}
}
}
}
defun_assign: {
options = {
reduce_vars: true,
toplevel: true,
}
input: {
console.log(typeof a);
a = 42;
console.log(typeof a);
function a() {}
console.log(typeof a);
}
expect: {
console.log(typeof a);
a = 42;
console.log(typeof a);
function a() {}
console.log(typeof a);
}
expect_stdout: [
"function",
"number",
"number",
]
}
defun_var_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var a = 42, b;
function a() {}
function b() {}
console.log(typeof a, typeof b);
}
expect: {
var a = 42;
function a() {}
console.log(typeof a, "function");
}
expect_stdout: "number function"
}
defun_var_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function a() {}
function b() {}
var a = 42, b;
console.log(typeof a, typeof b);
}
expect: {
function a() {}
var a = 42;
console.log(typeof a, "function");
}
expect_stdout: "number function"
}
defun_var_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function a() {}
function b() {}
console.log(typeof a, typeof b);
var a = 42, b;
}
expect: {
function a() {}
console.log(typeof a, "function");
var a = 42;
}
expect_stdout: "function function"
}
defun_catch_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function a() {}
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect: {
// TODO: drop unused AST_Defun
function a() {}
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
defun_catch_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
function a() {}
throw 42;
} catch (a) {
console.log(a);
}
}
expect: {
try {
// TODO: drop unused AST_Defun
function a() {}
throw 42;
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
defun_catch_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
function a() {}
} catch (a) {
console.log(a);
}
}
expect: {
try {
throw 42;
// TODO: drop unused AST_Defun
function a() {}
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
defun_catch_4: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect_stdout: true
}
defun_catch_5: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect_stdout: true
}
defun_catch_6: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (a) {
console.log(a);
}
function a() {}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
}
// TODO: drop unused AST_Defun
function a() {}
}
expect_stdout: "42"
}
duplicate_lambda_defun_name_1: {
options = {
reduce_vars: true,
}
input: {
console.log(function f(a) {
function f() {}
return f.length;
}());
}
expect: {
console.log(function f(a) {
function f() {}
return f.length;
}());
}
expect_stdout: "0"
}
duplicate_lambda_defun_name_2: {
options = {
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
console.log(function f(a) {
function f() {}
return f.length;
}());
}
expect: {
console.log(function(a) {
return function() {}.length;
}());
}
expect_stdout: "0"
}
issue_2757_1: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
let u;
(function() {
let v;
console.log(u, v);
})();
}
expect: {
let u;
console.log(u, void 0);
}
expect_stdout: "undefined undefined"
node_version: ">=6"
}
issue_2757_2: {
options = {
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
let bar;
const unused = function() {
bar = true;
};
if (!bar) {
console.log(1);
}
console.log(2);
}());
}
expect: {
console.log(1),
console.log(2);
}
expect_stdout: [
"1",
"2",
]
node_version: ">=6"
}
issue_2774: {
options = {
reduce_vars: true,
unused: true,
}
input: {
console.log({
get a() {
var b;
(b = true) && b.c;
b = void 0;
}
}.a);
}
expect: {
console.log({
get a() {
var b;
(b = true) && b.c;
b = void 0;
}
}.a);
}
expect_stdout: "undefined"
}
issue_2799_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
return f;
function f(n) {
function g(i) {
return i && i + g(i - 1);
}
function h(j) {
return g(j);
}
return h(n);
}
}()(5));
}
expect: {
console.log(function() {
return function(n) {
return function(j) {
return function g(i) {
return i && i + g(i - 1);
}(j);
}(n);
}
}()(5));
}
expect_stdout: "15"
}
issue_2799_2: {
options = {
reduce_vars: true,
unsafe_proto: true,
unused: true,
}
input: {
(function() {
function foo() {
Function.prototype.call.apply(console.log, [ null, "PASS" ]);
}
foo();
})();
}
expect: {
(function() {
(function() {
(function() {}).call.apply(console.log, [ null, "PASS" ]);
})();
})();
}
expect_stdout: "PASS"
}
issue_2836: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return "FAIL";
}
console.log(f());
function f() {
return "PASS";
}
}
expect: {
console.log(function() {
return "PASS";
}());
}
expect_stdout: "PASS"
}

View File

@@ -288,7 +288,7 @@ unsafe_undefined: {
if_return: true,
sequences: true,
side_effects: true,
unsafe: true,
unsafe_undefined: true,
}
input: {
function f(undefined) {
@@ -820,3 +820,84 @@ cascade_assignment_in_return: {
}
}
}
hoist_defun: {
options = {
join_vars: true,
sequences: true,
}
input: {
x();
function f() {}
y();
}
expect: {
function f() {}
x(), y();
}
}
hoist_decl: {
options = {
join_vars: true,
sequences: true,
}
input: {
var a;
w();
var b = x();
y();
for (var c; 0;) z();
var d;
}
expect: {
var a;
w();
var b = x(), c, d;
for (y(); 0;) z();
}
}
for_init_var: {
options = {
join_vars: true,
unused: false,
}
input: {
var a = "PASS";
(function() {
var b = 42;
for (var c = 5; c > 0;) c--;
a = "FAIL";
var a;
})();
console.log(a);
}
expect: {
var a = "PASS";
(function() {
for (var b = 42, c = 5, a; c > 0;) c--;
a = "FAIL";
})();
console.log(a);
}
expect_stdout: "PASS"
}
forin: {
options = {
sequences: true,
}
input: {
var o = [];
o.push("PASS");
for (var a in o)
console.log(o[a]);
}
expect: {
var o = [];
for (var a in o.push("PASS"), o)
console.log(o[a]);
}
expect_stdout: "PASS"
}

View File

@@ -100,7 +100,7 @@ typeof_defun_1: {
g = 42;
console.log("YES");
"function" == typeof g && g();
h();
"function" == typeof h && h();
}
expect_stdout: [
"YES",
@@ -138,3 +138,166 @@ typeof_defun_2: {
"2",
]
}
duplicate_defun_arg_name: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
function long_name(long_name) {
return typeof long_name;
}
console.log(typeof long_name, long_name());
}
expect: {
function long_name(long_name) {
return typeof long_name;
}
console.log(typeof long_name, long_name());
}
expect_stdout: "function undefined"
}
duplicate_lambda_arg_name: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
console.log(function long_name(long_name) {
return typeof long_name;
}());
}
expect: {
console.log(function long_name(long_name) {
return typeof long_name;
}());
}
expect_stdout: "undefined"
}
issue_2728_1: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
(function arguments() {
console.log(typeof arguments);
})();
}
expect: {
(function arguments() {
console.log(typeof arguments);
})();
}
expect_stdout: "object"
}
issue_2728_2: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
function arguments() {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect: {
function arguments() {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect_stdout: "function object"
}
issue_2728_3: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
(function() {
function arguments() {
}
console.log(typeof arguments);
})();
}
expect: {
(function() {
function arguments() {
}
console.log("function");
})();
}
expect_stdout: "function"
}
issue_2728_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
}
input: {
function arguments() {
}
console.log(typeof arguments);
}
expect: {
function arguments() {
}
console.log("function");
}
expect_stdout: "function"
}
issue_2728_5: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
(function arguments(arguments) {
console.log(typeof arguments);
})();
}
expect: {
(function arguments(arguments) {
console.log(typeof arguments);
})();
}
expect_stdout: "undefined"
}
issue_2728_6: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
function arguments(arguments) {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect: {
function arguments(arguments) {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect_stdout: "function undefined"
}

View File

@@ -88,9 +88,8 @@ yield_before_punctuators: {
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} } // Added return to avoid {} drop
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
function* g3() { yield, yield; }
function* g4() { (yield) ? yield : yield; }
}
expect: {
iter = (function*() {
@@ -98,9 +97,8 @@ yield_before_punctuators: {
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} }
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
function* g3() { yield, yield; }
function* g4() { (yield) ? yield : yield; }
}
}
@@ -199,3 +197,57 @@ yield_as_ES5_property: {
expect_exact: '"use strict";console.log({yield:42}.yield);'
expect_stdout: "42"
}
issue_2689: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function* y() {
var t = yield x();
return new t();
}
}
expect_exact: "function*y(){return new(yield x())}"
}
issue_2832: {
beautify = {
beautify: true,
}
input: {
function* gen(i) {
const result = yield (x = i, -x);
var x;
console.log(x);
console.log(result);
yield 2;
}
var x = gen(1);
console.log(x.next("first").value);
console.log(x.next("second").value);
}
expect_exact: [
"function* gen(i) {",
" const result = yield (x = i, -x);",
" var x;",
" console.log(x);",
" console.log(result);",
" yield 2;",
"}",
"",
"var x = gen(1);",
"",
'console.log(x.next("first").value);',
"",
'console.log(x.next("second").value);',
]
expect_stdout: [
"-1",
"1",
"second",
"2",
]
node_version: ">=4"
}

View File

@@ -1,8 +1,3 @@
function f() {
const a;
}
function g() {
"use strict";
const a;
}

View File

@@ -1,4 +1,4 @@
var a = bar(1+2);
var b = baz(3+9);
print('q' + 'u' + 'x', a, b);
var x = bar(1+2);
var y = baz(3+9);
print('q' + 'u' + 'x', x, y);
foo(5+6);

View File

@@ -0,0 +1,6 @@
function f(x) {
return g(x);
function g(x) {
return x;
}
}

View File

@@ -5,11 +5,7 @@
var site = "http://browserbench.org/JetStream";
if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
require("../tools/exit");
var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {

View File

@@ -1,29 +1,24 @@
var Mocha = require('mocha'),
fs = require('fs'),
path = require('path');
var fs = require("fs");
var Mocha = require("mocha");
var path = require("path");
// Instantiate a Mocha instance.
var mocha = new Mocha({});
// Instantiate a Mocha instance
var mocha = new Mocha({
timeout: 5000
});
var testDir = __dirname + "/mocha/";
var testDir = __dirname + '/mocha/';
// Add each .js file to the mocha instance
fs.readdirSync(testDir).filter(function(file){
// Only keep the .js files
return file.substr(-3) === '.js';
}).forEach(function(file){
mocha.addFile(
path.join(testDir, file)
);
// Add each .js file to the Mocha instance
fs.readdirSync(testDir).filter(function(file) {
return /\.js$/.test(file);
}).forEach(function(file) {
mocha.addFile(path.join(testDir, file));
});
module.exports = function() {
mocha.run(function(failures) {
if (failures !== 0) {
process.on('exit', function () {
process.exit(failures);
});
}
if (failures) process.on("exit", function() {
process.exit(failures);
});
});
};

View File

@@ -408,7 +408,7 @@ describe("bin/uglifyjs", function () {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/const.js:7,11",
"Parse error at test/input/invalid/const.js:2,11",
" const a;",
" ^",
"ERROR: Missing initializer in const declaration"

View File

@@ -220,4 +220,24 @@ describe("Comment", function() {
if (result.error) throw result.error;
assert.strictEqual(result.code, "/*a*/ /*b*/(function(){/*c*/}/*d*/ /*e*/)();");
});
it("Should output line comments after statements", function() {
var result = uglify.minify([
"x()//foo",
"{y()//bar",
"}",
].join("\n"), {
compress: false,
mangle: false,
output: {
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"x();//foo",
"{y();//bar",
"}",
].join("\n"));
});
});

View File

@@ -42,8 +42,15 @@ describe("minify", function() {
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 20), '{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}var c=console.log.bind(console);function l(o){c("Foo:",2*o)}var f=n(3),i=r(12);c("qux",f,i),l(11);');
assert.strictEqual(JSON.stringify(cache).slice(0, 10), '{"props":{');
assert.strictEqual(compressed, [
"function n(n){return 3*n}",
"function r(n){return n/2}",
"var o=console.log.bind(console);",
'function c(n){o("Foo:",2*n)}',
"var a=n(3),b=r(12);",
'o("qux",a,b),c(11);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
@@ -68,12 +75,48 @@ describe("minify", function() {
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 28), '{"vars":{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}var c=console.log.bind(console);function l(o){c("Foo:",2*o)}var f=n(3),i=r(12);c("qux",f,i),l(11);');
assert.strictEqual(JSON.stringify(cache).slice(0, 18), '{"vars":{"props":{');
assert.strictEqual(compressed, [
"function n(n){return 3*n}",
"function r(n){return n/2}",
"var o=console.log.bind(console);",
'function c(n){o("Foo:",2*n)}',
"var a=n(3),b=r(12);",
'o("qux",a,b),c(11);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
it("should not parse invalid use of reserved words", function() {
it("Should avoid mangled names in cache", function() {
var cache = {};
var original = "";
var compressed = "";
[
'"xxxyy";var i={s:1};',
'"xxyyy";var j={t:2,u:3},k=4;',
'console.log(i.s,j.t,j.u,k);',
].forEach(function(code) {
var result = Uglify.minify(code, {
compress: false,
mangle: {
properties: true,
toplevel: true
},
nameCache: cache
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(compressed, [
'"xxxyy";var x={x:1};',
'"xxyyy";var y={y:2,a:3},a=4;',
'console.log(x.x,y.y,y.a,a);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
it("Should not parse invalid use of reserved words", function() {
assert.strictEqual(Uglify.minify("function enum(){}").error, undefined);
assert.strictEqual(Uglify.minify("function static(){}").error, undefined);
assert.strictEqual(Uglify.minify("function super(){}").error.message, "Unexpected token: name (super)");
@@ -361,4 +404,19 @@ describe("minify", function() {
assert.strictEqual(stat.print_to_string(), "a=x()");
});
});
// rename is disabled on harmony due to expand_names bug in for-of loops
if (0) describe("rename", function() {
it("Should be repeatable", function() {
var code = "!function(x){return x(x)}(y);";
for (var i = 0; i < 2; i++) {
assert.strictEqual(Uglify.minify(code, {
compress: {
toplevel: true,
},
rename: true,
}).code, "var a;(a=y)(a);");
}
});
});
});

View File

@@ -8,18 +8,6 @@ describe("Yield", function() {
assert.strictEqual(result.code, 'function*foo(e){return yield 1,yield 2,3}');
});
it("Should not allow yield as labelIdentifier within generators", function() {
var js = "function* g() {yield: 1}"
var test = function() {
UglifyJS.parse(js);
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Yield cannot be used as label inside generators";
}
assert.throws(test, expect);
});
it("Should not allow yield* followed by a semicolon in generators", function() {
var js = "function* test() {yield*\n;}";
var test = function() {
@@ -65,42 +53,68 @@ describe("Yield", function() {
);
});
var identifiers = [
// Fail in as_symbol
'import yield from "bar";',
'yield = 123;',
'yield: "123";',
'for(;;){break yield;}',
'for(;;){continue yield;}',
'function yield(){}',
'try { new Error("")} catch (yield) {}',
'var yield = "foo";',
'class yield {}',
// Fail in as_property_name
'var foo = {yield};',
];
it("Should not allow yield to be used as symbol, identifier or shorthand property outside generators in strict mode", function() {
var tests = [
// Fail in as_symbol
'"use strict"; import yield from "bar";',
'"use strict"; yield = 123;',
'"use strict"; yield: "123";',
'"use strict"; for(;;){break yield;}',
'"use strict"; for(;;){continue yield;}',
'"use strict"; function yield(){}',
'"use strict"; function foo(...yield){}',
'"use strict"; try { new Error("")} catch (yield) {}',
'"use strict"; var yield = "foo";',
'"use strict"; class yield {}',
// Fail in maybe_assign
'"use strict"; var foo = yield;',
'"use strict"; var foo = bar = yield',
// Fail in as_property_name
'"use strict"; var foo = {yield};',
];
var fail = function(e) {
function fail(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
/^Unexpected yield identifier (?:as parameter )?inside strict mode/.test(e.message);
}
var test = function(input) {
function test(input) {
return function() {
UglifyJS.parse(input);
}
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), fail, tests[i]);
identifiers.concat([
// Fail in as_symbol
"function foo(...yield){}",
// Fail in maybe_assign
'var foo = yield;',
'var foo = bar = yield',
]).map(function(code) {
return '"use strict"; ' + code;
}).forEach(function(code) {
assert.throws(test(code), fail, code);
});
});
it("Should not allow yield to be used as symbol, identifier or shorthand property inside generators", function() {
function fail(e) {
return e instanceof UglifyJS.JS_Parse_Error && [
"Unexpected token: operator (=)",
"Yield cannot be used as identifier inside generators",
].indexOf(e.message) >= 0;
}
function test(input) {
return function() {
UglifyJS.parse(input);
}
}
identifiers.map(function(code) {
return "function* f() { " + code + " }";
}).concat([
// Fail in as_symbol
"function* f(yield) {}",
]).forEach(function(code) {
assert.throws(test(code), fail, code);
});
});
it("Should allow yield to be used as class/object property name", function() {

View File

@@ -10,6 +10,7 @@ var semver = require("semver");
var tests_dir = path.dirname(module.filename);
var failures = 0;
var failed_files = {};
var minify_options = require("./ufuzz.json").map(JSON.stringify);
run_compress_tests();
if (failures) {
@@ -100,6 +101,15 @@ function run_compress_tests() {
quote_style: 3,
keep_quoted_props: true
});
try {
U.parse(input_code);
} catch (ex) {
log("!!! Cannot parse input\n---INPUT---\n{input}\n--PARSE ERROR--\n{error}\n\n", {
input: input_formatted,
error: ex,
});
return false;
}
var options = U.defaults(test.options, {
warnings: false
});
@@ -139,78 +149,77 @@ function run_compress_tests() {
output: output,
expected: expect
});
failures++;
failed_files[file] = 1;
return false;
}
else {
// expect == output
try {
var reparsed_ast = U.parse(output);
} catch (ex) {
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
// expect == output
try {
U.parse(output);
} catch (ex) {
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
input: input_formatted,
output: output,
error: ex,
});
return false;
}
if (test.expect_warnings) {
U.AST_Node.warn_function = original_warn_function;
var expected_warnings = make_code(test.expect_warnings, {
beautify: false,
quote_style: 2, // force double quote to match JSON
});
warnings_emitted = warnings_emitted.map(function(input) {
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
});
var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
input: input_formatted,
output: output,
error: ex.toString(),
expected_warnings: expected_warnings,
actual_warnings: actual_warnings,
});
failures++;
failed_files[file] = 1;
}
if (test.expect_warnings) {
U.AST_Node.warn_function = original_warn_function;
var expected_warnings = make_code(test.expect_warnings, {
beautify: false,
quote_style: 2, // force double quote to match JSON
});
warnings_emitted = warnings_emitted.map(function(input) {
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
});
var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
input: input_formatted,
expected_warnings: expected_warnings,
actual_warnings: actual_warnings,
});
failures++;
failed_files[file] = 1;
}
}
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
}
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
} else {
stdout = sandbox.run_code(output);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
}
}
return false;
}
}
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
}
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
return false;
}
stdout = sandbox.run_code(output);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
return false;
}
if (test.reminify && !reminify(test.options, input_code, input_formatted, test.expect_stdout)) {
return false;
}
}
return true;
}
var tests = parse_test(path.resolve(dir, file));
for (var i in tests) if (tests.hasOwnProperty(i)) {
test_case(tests[i]);
if (!test_case(tests[i])) {
failures++;
failed_files[file] = 1;
}
}
}
files.forEach(function(file){
@@ -255,6 +264,16 @@ function parse_test(file) {
}));
}
function read_boolean(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
if (body instanceof U.AST_Boolean) {
return body.value;
}
}
throw new Error("Should be boolean");
}
function read_string(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
@@ -273,7 +292,11 @@ function parse_test(file) {
}
function get_one_test(name, block) {
var test = { name: name, options: {} };
var test = {
name: name,
options: {},
reminify: true,
};
var tw = new U.TreeWalker(function(node, descend){
if (node instanceof U.AST_Assign) {
if (!(node.left instanceof U.AST_SymbolRef)) {
@@ -293,6 +316,7 @@ function parse_test(file) {
"expect_warnings",
"expect_stdout",
"node_version",
"reminify",
].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name,
@@ -303,6 +327,9 @@ function parse_test(file) {
var stat = node.body;
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "reminify") {
var value = read_boolean(stat);
test.reminify = value == null || value;
} else if (label.name == "expect_stdout") {
var body = stat.body;
if (body instanceof U.AST_Boolean) {
@@ -346,3 +373,49 @@ function evaluate(code) {
code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")();
}
// Try to reminify original input with standard options
// to see if it matches expect_stdout.
function reminify(orig_options, input_code, input_formatted, expect_stdout) {
for (var i = 0; i < minify_options.length; i++) {
var options = JSON.parse(minify_options[i]);
options.keep_fnames = orig_options.keep_fnames;
options.keep_classnames = orig_options.keep_classnames;
if (orig_options.compress) {
options.compress.keep_classnames = orig_options.compress.keep_classnames;
options.compress.keep_fargs = orig_options.compress.keep_fargs;
options.compress.keep_fnames = orig_options.compress.keep_fnames;
}
if (orig_options.mangle) {
options.mangle.keep_classnames = orig_options.mangle.keep_classnames;
options.mangle.keep_fnames = orig_options.mangle.keep_fnames;
}
var options_formatted = JSON.stringify(options, null, 4);
var result = U.minify(input_code, options);
if (result.error) {
log("!!! failed input reminify\n---INPUT---\n{input}\n--ERROR---\n{error}\n\n", {
input: input_formatted,
error: result.error,
});
return false;
} else {
var stdout = sandbox.run_code(result.code);
if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
stdout = expect_stdout;
}
if (!sandbox.same_stdout(expect_stdout, stdout)) {
log("!!! failed running reminified input\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n---OUTPUT---\n{output}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
options: options_formatted,
output: result.code,
expected_type: typeof expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
return false;
}
}
}
return true;
}

View File

@@ -17,24 +17,30 @@ function safe_log(arg, level) {
return arg;
}
function strip_func_ids(text) {
return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>");
}
var FUNC_TOSTRING = [
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
" f.valueOf = Function.prototype.valueOf;",
"});",
"Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 100000;",
" return function() {",
' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";',
" var i = this.name;",
' if (typeof i != "number") {',
" i = ++id;",
" var n = this.name;",
' if (!/^F[0-9]{6}N$/.test(n)) {',
' n = "F" + ++id + "N";',
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return i;",
" return n;",
" }",
" });",
] : [], [
" }",
' return "[Function: " + i + "]";',
' return "[Function: " + n + "]";',
" }",
"}();",
'Object.defineProperty(Function.prototype, "valueOf", { enumerable: false });',
@@ -77,7 +83,7 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
}
return expected == actual;
return strip_func_ids(expected) == strip_func_ids(actual);
} : function(expected, actual) {
return typeof expected == typeof actual && expected.toString() == actual.toString();
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};

82
test/travis-ufuzz.js Normal file
View File

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

View File

@@ -6,11 +6,7 @@
// bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m
// cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
require("../tools/exit");
var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes;
@@ -127,6 +123,9 @@ for (var i = 2; i < process.argv.length; ++i) {
}
var VALUES = [
'"a"',
'"b"',
'"c"',
'""',
'true',
'false',
@@ -329,7 +328,8 @@ function createTopLevelCode() {
rng(2) == 0
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
: createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
'console.log(null, a, b, c);' // preceding `null` makes for a cleaner output (empty string still shows up etc)
// preceding `null` makes for a cleaner output (empty string still shows up etc)
'console.log(null, a, b, c, Infinity, NaN, undefined);'
].join('\n');
}
@@ -635,6 +635,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
return getVarName();
case p++:
return getVarName() + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
@@ -996,10 +998,11 @@ function log_suspects(minify_options, component) {
if (typeof options != "object") options = {};
var defs = default_options[component];
var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var flip = name == "keep_fargs";
if (flip ? name in options : (name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options));
o[name] = false;
o[name] = flip;
m[component] = o;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
@@ -1020,6 +1023,24 @@ function log_suspects(minify_options, component) {
}
}
function log_rename(options) {
if (!options.rename) return;
var m = JSON.parse(JSON.stringify(options));
m.rename = false;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
errorln("Error testing options.rename");
errorln(result.error.stack);
} else {
var r = sandbox.run_code(result.code);
if (sandbox.same_stdout(original_result, r)) {
errorln("Suspicious options:");
errorln(" rename");
errorln();
}
}
}
function log(options) {
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
errorln("//=============================================================");
@@ -1054,6 +1075,7 @@ function log(options) {
errorln();
if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options));
log_rename(options);
errorln("!!!!!! Failed... round " + round);
}
}

View File

@@ -5,7 +5,8 @@
"output": {
"beautify": true,
"bracketize": true
}
},
"rename": true
},
{
"compress": false
@@ -20,7 +21,13 @@
{
"compress": {
"keep_fargs": false,
"passes": 100
"passes": 1e6,
"sequences": 1e6,
"unsafe": true,
"unsafe_Function": true,
"unsafe_math": true,
"unsafe_proto": true,
"unsafe_regexp": true
}
}
]

15
tools/exit.js Normal file
View File

@@ -0,0 +1,15 @@
// workaround for tty output truncation upon process.exit()
var exit = process.exit;
process.exit = function() {
var args = [].slice.call(arguments);
process.once("uncaughtException", function() {
(function callback() {
if (process.stdout.bufferSize || process.stderr.bufferSize) {
setImmediate(callback);
} else {
exit.apply(process, args);
}
})();
});
throw exit;
};