Compare commits

...

101 Commits

Author SHA1 Message Date
Alex Lam S.L
370f2cc906 Merge pull request #2104 from alexlamsl/harmony-v3.0.17
Merging from master for 3.0.17
2017-06-15 23:07:22 +08:00
alexlamsl
78cf35f89c Merge branch 'master' into harmony-v3.0.17 2017-06-15 19:01:36 +08:00
Alex Lam S.L
57dc4fb32f v3.0.17 2017-06-15 18:59:37 +08:00
Alex Lam S.L
b85a358deb suppress inline of this (#2103)
fixes #2101
2017-06-15 12:14:16 +08:00
kzc
100e18305d first cut of async/await (#2098)
- async arrow functions not yet supported


fixes #1789
2017-06-15 06:15:48 +08:00
Alex Lam S.L
43697958f3 avoid intermittent test time-out failures (#2100) 2017-06-15 04:47:57 +08:00
Alex Lam S.L
3f961bbba0 compute uses_arguments correctly in figure_out_scope() (#2099)
fixes #2097
2017-06-15 03:28:26 +08:00
Alex Lam S.L
7cc03d4d40 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:26:49 +08:00
Alex Lam S.L
0a1e523cd5 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:00:03 +08:00
Alex Lam S.L
c28056d7ed Merge pull request #2094 from alexlamsl/harmony-v3.0.16
Merging from master for 3.0.16
2017-06-14 19:25:21 +08:00
alexlamsl
8af362ed57 Merge branch 'master' into harmony-v3.0.16 2017-06-14 17:09:30 +08:00
Alex Lam S.L
4231f7323e v3.0.16 2017-06-14 16:45:09 +08:00
Alex Lam S.L
68138f2281 fix reduce_vars on AST_Arrow (#2091)
fixes #2090
2017-06-14 16:40:37 +08:00
kzc
da2de350c3 add comment about quote_style and gzip (#2092) 2017-06-14 12:23:03 +08:00
Alex Lam S.L
41beae4dd7 cache web assets between CI runs (#2089)
- skip `test/jetstream.js` for `node@0.12`
2017-06-14 11:53:10 +08:00
Ziad El Khoury Hanna
82db9188ac fix CLI parsing of --source-map content (#2088)
fixes #2082
2017-06-13 16:30:46 +08:00
Alex Lam S.L
3dc9e140e4 add Node.js 8 to Travis CI (#2086)
- explicitly terminate `test/jetstream.js` upon completion
- log verbose output from `test/benchmark.js` & `test/jetstream.js`
- remove obsolete workaround for Travis CI
2017-06-13 06:21:16 +08:00
Alex Lam S.L
fed0096556 allow expect_stdout to specify Error (#2087) 2017-06-13 04:57:26 +08:00
Alex Lam S.L
2bdc8802dd fix variable accounting in inline (#2085)
fixes #2084
2017-06-13 01:40:14 +08:00
Alex Lam S.L
5ef7cb372a suppress false positives for-in loops (#2080)
fixes #2079
2017-06-10 13:55:17 +08:00
Alex Lam S.L
4ad7b1dae4 fix portability of sandbox.run_code() on Node.js 0.1x (#2078) 2017-06-10 01:08:58 +08:00
Alex Lam S.L
9186859cb7 fix non-string parameters (#2076)
`Stream.write()` is not as versatile as `console.log()`
2017-06-10 00:11:40 +08:00
Alex Lam S.L
47c0713747 report test/ufuzz.js failures in process.stderr (#2074) 2017-06-09 15:56:28 +08:00
Alex Lam S.L
293c566d6c marshal mangle[.properties].reserved from non-Array values (#2072) 2017-06-09 04:29:12 +08:00
Alex Lam S.L
9c306406f1 fix iteration over object with inherited properties (#2068)
fixes #2055
2017-06-08 03:27:03 +08:00
Alex Lam S.L
9db0695b10 fix cascade on multi-branch evaluations (#2067)
Partially reverts #2059 as this has better coverage and performance.

fixes #2062
2017-06-07 19:52:01 +08:00
Alex Lam S.L
a7971f4e34 fix unused crash with top-level AST_Var (#2066)
fixes #2063
2017-06-07 19:12:35 +08:00
Alex Lam S.L
f2af093402 fix CLI output corruption (#2061)
Using `console.error()` & `console.log()` result in inconsistent formatting across Node.js versions.

Avoid this issue by directly writing to `process.stderr` & `process.stdout` instead.

Miscellaneous
- prettify invalid option listing
2017-06-07 04:25:32 +08:00
Alex Lam S.L
b9ad53d1ab fix inline handling of AST_Call.args (#2059) 2017-06-06 22:55:25 +08:00
Alex Lam S.L
b0eab71470 implement test/jetstream.js --debug (#2058) 2017-06-06 19:28:12 +08:00
Alex Lam S.L
3493a182b2 implement function inlining (#2053)
- empty body
- single `AST_Return`
- single `AST_SimpleStatement`
- avoid `/*#__PURE__*/`

Miscellaneous
- enhance single-use function substitution

fixes #281
2017-06-06 05:49:53 +08:00
Alex Lam S.L
27c5284d3d workaround webkit parsing error (#2056)
apply `webkit` to jetstream tests
2017-06-06 04:06:42 +08:00
Alex Lam S.L
540220b91b fix AST_Function scope invariance (#2052)
improve function name hack in `run_code()`
2017-06-04 19:27:43 +08:00
kzc
82fefc5d29 fix class expression statements (#2051)
- class expression statements require parentheses
- allow unused class expression statements to be dropped

fixes #1782
closes #1784
2017-06-04 02:45:26 +08:00
kzc
753932b302 drop unused arrow functions (#2050) 2017-06-04 00:20:46 +08:00
Alex Lam S.L
84634da4b5 add tests for AST_SymbolAccessor (#2049) 2017-06-03 16:08:10 +08:00
Alex Lam S.L
1743621889 clean up lib/parse.js (#2047)
- remove unused definitions
- replace `array_to_hash()`
2017-06-03 14:00:59 +08:00
Alex Lam S.L
1edbd6556f fix beautify whitespace output within AST_Destructuring (#2046)
fixes #2044
2017-06-02 18:50:39 +08:00
kzc
f330ab743a better document behavior of unsafe_Func (#2043) 2017-06-02 12:07:17 +08:00
Alex Lam S.L
888a321417 Merge pull request #2042 from alexlamsl/harmony-v3.0.15
Merging from master for 3.0.15
2017-06-01 19:28:30 +08:00
alexlamsl
ee5c03f7f1 Merge branch 'master' into harmony-v3.0.15 2017-06-01 18:26:09 +08:00
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
0cb75089f0 document safari10 mangle option (#2035) 2017-05-31 23:16:20 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
Alex Lam S.L
a1647ee0c5 Merge pull request #2034 from alexlamsl/harmony-v3.0.14
Merging from master for 3.0.14
2017-05-31 12:44:58 +08:00
alexlamsl
c814060b4a Merge branch 'master' into harmony-v3.0.14 2017-05-31 11:42:54 +08:00
Alex Lam S.L
3e62faa64f v3.0.14 2017-05-31 11:34:51 +08:00
Alex Lam S.L
e9645e017f introduce unsafe_Func (#2033)
Separate flag for #203 functionality.
2017-05-31 03:38:00 +08:00
Alex Lam S.L
55b5f2a8aa widen CLI parse error code fragment displayed (#2032)
fixes #2030
2017-05-31 01:56:52 +08:00
Alex Lam S.L
303293e4aa fix side_effects on AST_Class (#2031)
fixes #2028
2017-05-31 01:44:29 +08:00
Alex Lam S.L
23265ac253 mangle destructuring function parameters (#2029)
fixes #2025
2017-05-30 23:41:55 +08:00
Alex Lam S.L
0cc6dedccc fix block elimination (#2023)
fixes #1664
fixes #1672
2017-05-30 14:59:54 +08:00
kzc
ec63588496 fix compress of IIFE with destructuring args (#2022) 2017-05-30 13:17:06 +08:00
Alex Lam S.L
c2e471e3ad fix if_return on block-scoped variables (#2021)
fixes #1317
2017-05-29 18:08:08 +08:00
Alex Lam S.L
ee23a84e14 Merge pull request #2020 from alexlamsl/harmony-v3.0.13
Merging from master for 3.0.13
2017-05-29 12:24:38 +08:00
alexlamsl
520da57fdc Merge branch 'master' into harmony-v3.0.13 2017-05-29 10:58:05 +08:00
Alex Lam S.L
4e0a22e5c8 v3.0.13 2017-05-29 10:52:13 +08:00
Alex Lam S.L
1aa38051fb better fix for #512 & #2010 (#2019)
- remove duplicated functionalities
- fix similar issue with `else`
2017-05-29 10:51:41 +08:00
Alex Lam S.L
e62b879b48 display default values in --help options (#2018) 2017-05-28 22:57:20 +08:00
Alex Lam S.L
c6c9f4f5a8 implement --help options (#2017) 2017-05-28 18:21:44 +08:00
Alex Lam S.L
fec14379f6 improve CLI usability (#2016)
Report supported options upon invalid option syntax.

fixes #1883
2017-05-28 04:09:40 +08:00
Alex Lam S.L
e5e0ce0b42 Merge pull request #2014 from alexlamsl/harmony-v3.0.12
Merging from master for 3.0.12
2017-05-28 00:08:08 +08:00
Alex Lam S.L
79131cd647 extend node_version range on applicable tests (#2015) 2017-05-27 22:18:28 +08:00
alexlamsl
94d2aeee89 fix block-scoped function for ES6
fixes #1903
2017-05-27 19:28:07 +08:00
alexlamsl
aa835eb0f6 Merge branch 'master' into harmony-v3.0.12 2017-05-27 18:12:10 +08:00
Alex Lam S.L
c3f14a1481 v3.0.12 2017-05-27 18:08:09 +08:00
Alex Lam S.L
7b13159cda fix hoist_funs on block-scoped function under "use strict" (#2013)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-27 17:44:59 +08:00
Alex Lam S.L
95094b9c22 fix if_return on AST_Defun (#2010)
Previous fiix for #1052 perturbs declaration order of functions which leads to incorrect behaviour under "use strict".
2017-05-27 13:41:49 +08:00
kzc
1ff8e9dd38 clarify what --mangle-props does (#2012) 2017-05-27 13:17:30 +08:00
kzc
78309a293d better document mangle properties options (#2009) 2017-05-27 02:28:43 +08:00
kzc
695e182d59 fix and expand --mangle-props documentation (#2008)
fixes #2007
2017-05-27 01:25:51 +08:00
Alex Lam S.L
dc33facfcb fix dead_code on block-scoped function under "use strict" (#2006)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-26 16:08:51 +08:00
Alex Lam S.L
39d4d7e20a fix export related issues (#2005)
- `mangle` non-exported names
- `unused` on `export` of `function`
- `hoist_funs` on `export`
- `export default`
  - prohibit definition statements
  - parse `AST_Defun` properly
  - drop only unused class and function names


fixes #2001
fixes #2004
2017-05-26 13:35:40 +08:00
Alex Lam S.L
c70fb60384 clean up lib/scope.js (#2003)
fixes #2004
2017-05-26 03:58:35 +08:00
Alex Lam S.L
02811ce35e fix issues related to export & function (#2002)
- `unused` function names
- confusion with function call syntax

fixes #2001
2017-05-26 03:12:52 +08:00
Alex Lam S.L
793d61499b report timing breakdown (#2000)
fix corner cases with `sourceMap`

fixes #1998
2017-05-25 07:15:55 +08:00
Alex Lam S.L
a277fe168d ensure new line after describe_ast() (#1999) 2017-05-25 02:32:36 +08:00
Alex Lam S.L
c988e5f4d6 remove AST_ArrowParametersOrSeq (#1997) 2017-05-24 17:45:18 +08:00
Alex Lam S.L
7d3b941e6e reinstate describe_ast() on CLI (#1996)
fixes #1995
2017-05-24 02:30:09 +08:00
Alex Lam S.L
075b648bb1 Merge pull request #1994 from alexlamsl/harmony-v3.0.11
Merging from master for 3.0.11
2017-05-24 00:04:47 +08:00
alexlamsl
37e549ff4f Merge branch 'master' into harmony-v3.0.11 2017-05-23 22:29:04 +08:00
Alex Lam S.L
e95052a423 v3.0.11 2017-05-23 22:26:59 +08:00
Alex Lam S.L
e667f0acb8 fix source map offset (#1993)
Account for whitespace insertions.

fixes #505
fixes #890
2017-05-23 20:25:48 +08:00
kzc
7bcb442e4c fix destructuring bugs in mangle and compress (#1992)
- destructuring mangle
- destructuring array default values

fixes #1335
2017-05-23 02:53:01 +08:00
kzc
a658cd84a5 fix destructuring of non string keys (#1989) 2017-05-22 16:38:03 +08:00
kzc
69ac794bc8 add another minify() options example (#1988) 2017-05-22 12:19:07 +08:00
Alex Lam S.L
efdb65913b improve usability of global_defs in minify() (#1987)
Use `@key` to `parse()` string value as `AST_Node`.

fixes #1986
2017-05-22 01:38:43 +08:00
kzc
a1dedeb3ce more refinement of minify() documentation (#1983) 2017-05-21 04:55:03 +08:00
Alex Lam S.L
5b22334f3b Merge pull request #1982 from alexlamsl/harmony-v3.0.10
Merging from master for 3.0.10
2017-05-21 03:23:59 +08:00
alexlamsl
a3053c537a Merge branch 'master' into harmony-v3.0.10 2017-05-21 01:36:38 +08:00
Alex Lam S.L
d3c4a8e9e7 v3.0.10 2017-05-21 01:30:17 +08:00
kzc
d6f77a6352 update keywords in package.json (#1981) 2017-05-20 22:10:51 +08:00
kzc
7e164aba8f add "es5" to package.json keywords (#1980) 2017-05-20 22:09:50 +08:00
kzc
22aedef849 document minify() option toplevel (#1979) 2017-05-20 22:09:21 +08:00
Alex Lam S.L
58fae7dc07 enhance if_return to handle return void... (#1977)
fixes #512
2017-05-20 15:58:46 +08:00
Alex Lam S.L
a2172e1a99 fix parsing of yield as object key (#1976)
fixes #1974
2017-05-20 13:11:37 +08:00
kzc
5bf8d7e949 document 3.x minify() does not throw errors (#1975) 2017-05-20 10:49:35 +08:00
kzc
1df9d06f4a document minify warnings and add an error example (#1973) 2017-05-19 17:20:21 +08:00
57 changed files with 3869 additions and 776 deletions

View File

@@ -4,8 +4,11 @@ node_js:
- "0.12" - "0.12"
- "4" - "4"
- "6" - "6"
- "8"
env: env:
- UGLIFYJS_TEST_ALL=1 - UGLIFYJS_TEST_ALL=1
matrix: matrix:
fast_finish: true fast_finish: true
sudo: false sudo: false
cache:
directories: tmp

257
README.md
View File

@@ -42,6 +42,7 @@ a double dash to prevent input files being used as option arguments:
``` ```
-h, --help Print usage information. -h, --help Print usage information.
`--help options` for details on available options.
-V, --version Print version number. -V, --version Print version number.
-p, --parse <options> Specify parser options: -p, --parse <options> Specify parser options:
`acorn` Use Acorn for parsing. `acorn` Use Acorn for parsing.
@@ -123,8 +124,8 @@ a double dash to prevent input files being used as option arguments:
the source map. the source map.
`url` If specified, path to the source map to append in `url` If specified, path to the source map to append in
`//# sourceMappingURL`. `//# sourceMappingURL`.
--stats Display operations run time on STDERR. --timings Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in toplevel scope. --toplevel Compress and/or mangle variables in top level scope.
--verbose Print diagnostic messages. --verbose Print diagnostic messages.
--warn Print warning messages. --warn Print warning messages.
--wrap <name> Embed everything in a big function, making the --wrap <name> Embed everything in a big function, making the
@@ -199,7 +200,7 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported: (comma-separated) options are supported:
- `toplevel` — mangle names declared in the toplevel scope (disabled by - `toplevel` — mangle names declared in the top level scope (disabled by
default). default).
- `eval` — mangle names visible in scopes where `eval` or `with` are used - `eval` — mangle names visible in scopes where `eval` or `with` are used
@@ -215,24 +216,54 @@ to prevent the `require`, `exports` and `$` names from being changed.
### CLI mangling property names (`--mangle-props`) ### CLI mangling property names (`--mangle-props`)
**Note:** this will probably break your code. Mangling property names is a **Note:** THIS WILL PROBABLY BREAK YOUR CODE. Mangling property names
separate step, different from variable name mangling. Pass is a separate step, different from variable name mangling. Pass
`--mangle-props`. It will mangle all properties that are seen in some `--mangle-props` to enable it. It will mangle all properties in the
object literal, or that are assigned to. For example: input code with the exception of built in DOM properties and properties
in core javascript classes. For example:
```javascript ```javascript
// example.js
var x = { var x = {
foo: 1 baz_: 0,
foo_: 1,
calc: function() {
return this.foo_ + this.baz_;
}
}; };
x.bar_ = 2;
x.bar = 2; x["baz_"] = 3;
x["baz"] = 3; console.log(x.calc());
x[condition ? "moo" : "boo"] = 4; ```
console.log(x.something()); Mangle all properties (except for javascript `builtins`):
```bash
$ uglifyjs example.js -c -m --mangle-props
```
```javascript
var x={o:0,_:1,l:function(){return this._+this.o}};x.t=2,x.o=3,console.log(x.l());
```
Mangle all properties except for `reserved` properties:
```bash
$ uglifyjs example.js -c -m --mangle-props reserved=[foo_,bar_]
```
```javascript
var x={o:0,foo_:1,_:function(){return this.foo_+this.o}};x.bar_=2,x.o=3,console.log(x._());
```
Mangle all properties matching a `regex`:
```bash
$ uglifyjs example.js -c -m --mangle-props regex=/_$/
```
```javascript
var x={o:0,_:1,calc:function(){return this._+this.o}};x.l=2,x.o=3,console.log(x.calc());
``` ```
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced Combining mangle properties options:
with single characters, while `something()` will be left as is. ```bash
$ uglifyjs example.js -c -m --mangle-props regex=/_$/,reserved=[bar_]
```
```javascript
var x={o:0,_:1,calc:function(){return this._+this.o}};x.bar_=2,x.o=3,console.log(x.calc());
```
In order for this to be of any use, we avoid mangling standard JS names by In order for this to be of any use, we avoid mangling standard JS names by
default (`--mangle-props builtins` to override). default (`--mangle-props builtins` to override).
@@ -241,7 +272,7 @@ A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass cover most standard JS and DOM properties defined in various browsers. Pass
`--mangle-props domprops` to disable this feature. `--mangle-props domprops` to disable this feature.
You can also use a regular expression to define which property names should be A regular expression can be used to define which property names should be
mangled. For example, `--mangle-props regex=/^_/` will only mangle property mangled. For example, `--mangle-props regex=/^_/` will only mangle property
names that start with an underscore. names that start with an underscore.
@@ -269,9 +300,20 @@ Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example: unquoted style (`o.foo`). Example:
```javascript
// stuff.js
var o = {
"foo": 1,
bar: 3
};
o.foo += o.bar;
console.log(o.foo);
```
```bash ```bash
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc $ uglifyjs stuff.js --mangle-props keep_quoted -c -m
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo); ```
```javascript
var o={foo:1,o:3};o.foo+=o.o,console.log(o.foo);
``` ```
### Debugging property name mangling ### Debugging property name mangling
@@ -282,6 +324,13 @@ would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify of a large codebase while still being able to debug the code and identify
where mangling is breaking things. where mangling is breaking things.
```bash
$ uglifyjs stuff.js --mangle-props debug -c -m
```
```javascript
var o={_$foo$_:1,_$bar$_:3};o._$foo$_+=o._$bar$_,console.log(o._$foo$_);
```
You can also pass a custom suffix using `--mangle-props debug=XYZ`. This would then You can also pass a custom suffix using `--mangle-props debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a script to identify how a property got mangled. One technique is to pass a
@@ -298,27 +347,94 @@ like this:
var UglifyJS = require("uglify-es"); var UglifyJS = require("uglify-es");
``` ```
There is a single high level minification function, `minify(files, options)`, which will There is a single high level function, **`minify(code, options)`**,
performs all the steps in a configurable manner. which will perform all minification [phases](#minify-options) in a configurable
Example: manner. By default `minify()` will enable the options [`compress`](#compress-options)
and [`mangle`](#mangle-options). Example:
```javascript ```javascript
var result = UglifyJS.minify("var b = function() {};"); var code = "function add(first, second) { return first + second; }";
console.log(result.code); // minified output var result = UglifyJS.minify(code);
console.log(result.error); // runtime error console.log(result.error); // runtime error, or `undefined` if no error
console.log(result.code); // minified output: function add(n,d){return n+d}
``` ```
You can also compress multiple files: You can `minify` more than one JavaScript file at a time by using an object
for the first argument where the keys are file names and the values are source
code:
```javascript ```javascript
var result = UglifyJS.minify({ var code = {
"file1.js": "var a = function() {};", "file1.js": "function add(first, second) { return first + second; }",
"file2.js": "var b = function() {};" "file2.js": "console.log(add(1 + 2, 3 + 4));"
}); };
var result = UglifyJS.minify(code);
console.log(result.code); console.log(result.code);
// function add(d,n){return d+n}console.log(add(3,7));
```
The `toplevel` option:
```javascript
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
console.log(result.code);
// console.log(function(n,o){return n+o}(3,7));
```
An example of a combination of `minify()` options:
```javascript
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = {
toplevel: true,
compress: {
global_defs: {
"@console.log": "alert"
},
passes: 2
},
output: {
beautify: false,
preamble: "/* uglified */"
}
};
var result = UglifyJS.minify(code, options);
console.log(result.code);
// /* uglified */
// alert(10);"
```
To produce warnings:
```javascript
var code = "function f(){ var u; return 2 + 3; }";
var options = { warnings: true };
var result = UglifyJS.minify(code, options);
console.log(result.error); // runtime error, `undefined` in this case
console.log(result.warnings); // [ 'Dropping unused variable u [0:1,18]' ]
console.log(result.code); // function f(){return 5}
```
An error example:
```javascript
var result = UglifyJS.minify({"foo.js" : "if (0) else console.log(1);"});
console.log(JSON.stringify(result.error));
// {"message":"Unexpected token: keyword (else)","filename":"foo.js","line":1,"col":7,"pos":7}
```
Note: unlike `uglify-js@2.x`, the `3.x` API does not throw errors. To
achieve a similar effect one could do the following:
```javascript
var result = UglifyJS.minify(code, options);
if (result.error) throw result.error;
``` ```
## Minify options ## Minify options
- `warnings` (default `false`) — pass `true` to display compressor warnings. - `warnings` (default `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `parse` (default `{}`) — pass an object if you wish to specify some - `parse` (default `{}`) — pass an object if you wish to specify some
additional [parse options](#parse-options). additional [parse options](#parse-options).
@@ -344,7 +460,7 @@ console.log(result.code);
- `ie8` (default `false`) - set to `true` to support IE8. - `ie8` (default `false`) - set to `true` to support IE8.
## Minify option structure ## Minify options structure
```javascript ```javascript
{ {
@@ -453,6 +569,9 @@ 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 comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true. `unsafe_comps` are both set to true.
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: false) -- optimize numerical expressions like - `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results. `2 * x * 3` into `6 * x`, which may give imprecise floating point results.
@@ -481,7 +600,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
assignments do not count as references unless set to `"keep_assign"`) assignments do not count as references unless set to `"keep_assign"`)
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`) - `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced in the top level scope (`false` by default, `true` to drop both unreferenced
functions and variables) functions and variables)
- `top_retain` -- prevent specific toplevel functions and variables from `unused` - `top_retain` -- prevent specific toplevel functions and variables from `unused`
@@ -494,13 +613,15 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `if_return` -- optimizations for if/return and if/continue - `if_return` -- optimizations for if/return and if/continue
- `inline` -- embed simple functions
- `join_vars` -- join consecutive `var` statements - `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x` - `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()` and `x = something(), x` into `x = something()`
- `collapse_vars` -- Collapse single-use `var` and `const` definitions - `collapse_vars` -- Collapse single-use non-constant variables - side
when possible. effects permitting.
- `reduce_vars` -- Improve optimization on variables assigned with and - `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values. used as constant values.
@@ -559,17 +680,22 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Mangle options ## Mangle options
- `reserved` - pass an array of identifiers that should be excluded from mangling - `reserved` (default `[]`). Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` mangle names declared in the toplevel scope (disabled by - `toplevel` (default `false`). Pass `true` to mangle names declared in the
default). top level scope.
- `eval` — mangle names visible in scopes where eval or with are used - `keep_fnames` (default `false`). Pass `true` to not mangle function names.
(disabled by default). Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options).
- `keep_fnames` -- default `false`. Pass `true` to not mangle - `eval` (default `false`). Pass `true` to mangle names visible in scopes
function names. Useful for code relying on `Function.prototype.name`. where `eval` or `with` are used.
See also: the `keep_fnames` [compress option](#compress-options).
- `safari10` (default `false`). Pass `true` to work around the Safari 10 loop
iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041)
"Cannot declare a let variable twice".
Examples: Examples:
@@ -595,10 +721,15 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options ### Mangle properties options
- `regex` — Pass a RegExp to only mangle certain names - `reserved` (default: `[]`) -- Do not mangle property names listed in the
- `keep_quoted` — Only mangle unquoted property names `reserved` array.
- `debug` — Mangle names with the original name still present. Defaults to `false`. - `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
Pass an empty string to enable, or a non-empty string to set the suffix. names matching the regular expression.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `debug` (default: `false`) -— Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
## Output options ## Output options
@@ -639,7 +770,7 @@ can pass additional arguments that control the code output:
- `quote_style` (default `0`) -- preferred quote style for strings (affects - `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well): quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are - `0` -- prefers double quotes, switches to single quotes when there are
more double quotes in the string itself. more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes - `1` -- always use single quotes
- `2` -- always use double quotes - `2` -- always use double quotes
- `3` -- always use the original quotes - `3` -- always use the original quotes
@@ -725,8 +856,8 @@ Another way of doing that is to declare your globals as constants in a
separate file and include it into the build. For example you can have a separate file and include it into the build. For example you can have a
`build/defines.js` file with the following: `build/defines.js` file with the following:
```javascript ```javascript
const DEBUG = false; var DEBUG = false;
const PRODUCTION = true; var PRODUCTION = true;
// etc. // etc.
``` ```
@@ -746,7 +877,7 @@ You can also use conditional compilation via the programmatic API. With the diff
property name is `global_defs` and is a compressor property: property name is `global_defs` and is a compressor property:
```javascript ```javascript
var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), { var result = UglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
compress: { compress: {
dead_code: true, dead_code: true,
global_defs: { global_defs: {
@@ -756,6 +887,32 @@ var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
}); });
``` ```
To replace an identifier with an arbitrary non-constant expression it is
necessary to prefix the `global_defs` key with `"@"` to instruct UglifyJS
to parse the value as an expression:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"@alert": "console.log"
}
}
}).code;
// returns: 'console.log("hello");'
```
Otherwise it would be replaced as string literal:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"alert": "console.log"
}
}
}).code;
// returns: '"console.log"("hello");'
```
### Using native Uglify AST with `minify()` ### Using native Uglify AST with `minify()`
```javascript ```javascript
// example: parse only, produce native Uglify AST // example: parse only, produce native Uglify AST

View File

@@ -21,9 +21,20 @@ var options = {
compress: false, compress: false,
mangle: false mangle: false
}; };
program.version(info.name + ' ' + info.version); program.version(info.name + " " + info.version);
program.parseArgv = program.parse; program.parseArgv = program.parse;
program.parse = undefined; program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
text.push(format_object(options[option]));
text.push("");
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true)); program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
@@ -38,7 +49,7 @@ program.option("--keep-fnames", "Do not mangle/drop function names. Useful for c
program.option("--name-cache <file>", "File to hold mangled name mappings."); program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"); program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map()); program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--stats", "Display operations run time on STDERR.") program.option("--timings", "Display operations run time on STDERR.")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--verbose", "Print diagnostic messages."); program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages."); program.option("--warn", "Print warning messages.");
@@ -114,10 +125,10 @@ if (program.output == "ast") {
}; };
} }
if (program.parse) { if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) { if (!program.parse.acorn && !program.parse.spidermonkey) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
} else {
options.parse = program.parse; options.parse = program.parse;
} else if (program.sourceMap && program.sourceMap.content == "inline") {
fatal("ERROR: inline source map only works with built-in parser");
} }
} }
var convert_path = function(name) { var convert_path = function(name) {
@@ -139,7 +150,7 @@ if (program.verbose) {
} }
if (program.self) { if (program.self) {
if (program.args.length) { if (program.args.length) {
console.error("WARN: Ignoring input files since --self was passed"); print_error("WARN: Ignoring input files since --self was passed");
} }
if (!options.wrap) options.wrap = "UglifyJS"; if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) { simple_glob(UglifyJS.FILES).forEach(function(name) {
@@ -169,9 +180,9 @@ function convert_ast(fn) {
function run() { function run() {
UglifyJS.AST_Node.warn_function = function(msg) { UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg); print_error("WARN: " + msg);
}; };
if (program.stats) program.stats = Date.now(); if (program.timings) options.timings = true;
try { try {
if (program.parse) { if (program.parse) {
if (program.parse.acorn) { if (program.parse.acorn) {
@@ -198,7 +209,7 @@ function run() {
if (result.error) { if (result.error) {
var ex = result.error; var ex = result.error;
if (ex.name == "SyntaxError") { if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col); print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col; var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/); var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1]; var line = lines[ex.line - 1];
@@ -207,21 +218,22 @@ function run() {
col = line.length; col = line.length;
} }
if (line) { if (line) {
if (col > 40) { var limit = 70;
line = line.slice(col - 40); if (col > limit) {
col = 40; line = line.slice(col - limit);
col = limit;
} }
console.error(line.slice(0, 80)); print_error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
} }
} }
if (ex.defs) { if (ex.defs) {
console.error("Supported options:"); print_error("Supported options:");
console.error(ex.defs); print_error(format_object(ex.defs));
} }
fatal(ex); fatal(ex);
} else if (program.output == "ast") { } else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) { print(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return; if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return; if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return; if (value instanceof UglifyJS.Dictionary) return;
@@ -237,7 +249,7 @@ function run() {
return value; return value;
}, 2)); }, 2));
} else if (program.output == "spidermonkey") { } else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, { print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false, compress: false,
mangle: false, mangle: false,
output: { output: {
@@ -251,19 +263,21 @@ function run() {
fs.writeFileSync(program.output + ".map", result.map); fs.writeFileSync(program.output + ".map", result.map);
} }
} else { } else {
console.log(result.code); print(result.code);
} }
if (program.nameCache) { if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) { fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value; return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
})); }));
} }
if (program.stats) console.error("Elapsed:", Date.now() - program.stats); if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
} }
function fatal(message) { function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:") if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message); print_error(message);
process.exit(1); process.exit(1);
} }
@@ -348,7 +362,7 @@ function parse_js(flag, constants) {
} }
})); }));
} catch(ex) { } catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value); options[value] = null;
} }
return options; return options;
} }
@@ -357,10 +371,10 @@ function parse_js(flag, constants) {
function parse_source_map() { function parse_source_map() {
var parse = parse_js("sourceMap", true); var parse = parse_js("sourceMap", true);
return function(value, options) { return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap; var hasContent = options && "content" in options;
var settings = parse(value, options); var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") { if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content); print_error("INFO: Using input source map: " + settings.content);
settings.content = read_file(settings.content, settings.content); settings.content = read_file(settings.content, settings.content);
} }
return settings; return settings;
@@ -382,3 +396,25 @@ function to_cache(key) {
function skip_key(key) { function skip_key(key) {
return skip_keys.indexOf(key) >= 0; return skip_keys.indexOf(key) >= 0;
} }
function format_object(obj) {
var lines = [];
var padding = "";
Object.keys(obj).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(obj[name]) ];
}).forEach(function(tokens) {
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
return lines.join("\n");
}
function print_error(msg) {
process.stderr.write(msg);
process.stderr.write("\n");
}
function print(txt) {
process.stdout.write(txt);
process.stdout.write("\n");
}

View File

@@ -355,86 +355,14 @@ var AST_Expansion = DEFNODE("Expansion", "expression", {
} }
}); });
var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", { var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator async", {
$documentation: "A set of arrow function parameters or a sequence expression. This is used because when the parser sees a \"(\" it could be the start of a seq, or the start of a parameter list of an arrow function.",
$propdoc: {
expressions: "[AST_Expression|AST_Destructuring|AST_Expansion*] array of expressions or argument names or destructurings."
},
as_params: function (croak) {
// We don't want anything which doesn't belong in a destructuring
var root = this;
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
var insert_default = function(ex, default_value) {
if (default_value) {
return new AST_DefaultAssign({
start: ex.start,
left: ex,
operator: "=",
right: default_value,
end: default_value.end
});
}
return ex;
}
if (ex instanceof AST_Object) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
names: ex.properties.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key]);
}
ex.value = to_fun_args(ex.value, 0, [ex.key]);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Hole) {
return ex;
} else if (ex instanceof AST_Destructuring) {
ex.names = ex.names.map(to_fun_args);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_SymbolRef) {
return insert_default(new AST_SymbolFunarg({
name: ex.name,
start: ex.start,
end: ex.end
}), default_seen_above);
} else if (ex instanceof AST_Expansion) {
ex.expression = to_fun_args(ex.expression);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Array) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
names: ex.elements.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_Assign) {
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
} else if (ex instanceof AST_DefaultAssign) {
ex.left = to_fun_args(ex.left, 0, [ex.left]);
return ex;
} else {
croak("Invalid function parameter", ex.start.line, ex.start.col);
}
});
},
as_expr: function() {
var exprs = this.expressions;
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
expressions: exprs
});
}
});
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?] the name of this function", name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments", argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
is_generator: "[boolean] is this a generator method",
async: "[boolean] is this method async",
}, },
args_as_names: function () { args_as_names: function () {
var out = []; var out = [];
@@ -966,11 +894,12 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
$documentation: "An object getter property", $documentation: "An object getter property",
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", { var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator async", {
$propdoc: { $propdoc: {
quote: "[string|undefined] the original quote character, if any", quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this method is static (classes only)", static: "[boolean] is this method static (classes only)",
is_generator: "[boolean] is generatorFn or not", is_generator: "[boolean] is this a generator method",
async: "[boolean] is this method async",
}, },
$documentation: "An ES6 concise method inside an object or class" $documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty); }, AST_ObjectProperty);
@@ -1174,7 +1103,17 @@ var AST_True = DEFNODE("True", null, {
value: true value: true
}, AST_Boolean); }, AST_Boolean);
/* -----[ Yield ]----- */ var AST_Await = DEFNODE("Await", "expression", {
$documentation: "An `await` statement",
$propdoc: {
expression: "[AST_Node] the mandatory expression being awaited",
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
});
}
});
var AST_Yield = DEFNODE("Yield", "expression is_star", { var AST_Yield = DEFNODE("Yield", "expression is_star", {
$documentation: "A `yield` statement", $documentation: "A `yield` statement",

View File

@@ -64,6 +64,7 @@ function Compressor(options, false_by_default) {
hoist_vars : false, hoist_vars : false,
ie8 : false, ie8 : false,
if_return : !false_by_default, if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default, join_vars : !false_by_default,
keep_fargs : true, keep_fargs : true,
keep_fnames : false, keep_fnames : false,
@@ -82,12 +83,24 @@ function Compressor(options, false_by_default) {
toplevel : !!(options && options["top_retain"]), toplevel : !!(options && options["top_retain"]),
unsafe : false, unsafe : false,
unsafe_comps : false, unsafe_comps : false,
unsafe_Func : false,
unsafe_math : false, unsafe_math : false,
unsafe_proto : false, unsafe_proto : false,
unsafe_regexp : false, unsafe_regexp : false,
unused : !false_by_default, unused : !false_by_default,
warnings : false, warnings : false,
}, true); }, true);
var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) {
if (/^@/.test(key) && HOP(global_defs, key)) {
var ast = parse(global_defs[key]);
if (ast.body.length == 1 && ast.body[0] instanceof AST_SimpleStatement) {
global_defs[key.slice(1)] = ast.body[0].body;
} else throw new Error(string_template("Can't handle expression: {value}", {
value: global_defs[key]
}));
}
}
var pure_funcs = this.options["pure_funcs"]; var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") { if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs; this.pure_funcs = pure_funcs;
@@ -240,7 +253,7 @@ merge(Compressor.prototype, {
}) })
}); });
} }
if (node instanceof AST_Lambda && node !== self) { if (node instanceof AST_Class || node instanceof AST_Lambda && node !== self) {
return node; return node;
} }
if (node instanceof AST_Block) { if (node instanceof AST_Block) {
@@ -351,7 +364,7 @@ merge(Compressor.prototype, {
safe_ids = save_ids; safe_ids = save_ids;
return true; return true;
} }
if (node instanceof AST_Function) { if (node instanceof AST_Function || node instanceof AST_Arrow) {
push(); push();
var iife; var iife;
if (!node.name if (!node.name
@@ -361,14 +374,16 @@ merge(Compressor.prototype, {
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them. // So existing transformation rules can work on them.
node.argnames.forEach(function(arg, i) { node.argnames.forEach(function(arg, i) {
var d = arg.definition(); if (arg.definition) {
if (!node.uses_arguments && d.fixed === undefined) { var d = arg.definition();
d.fixed = function() { if (!node.uses_arguments && d.fixed === undefined) {
return iife.args[i] || make_node(AST_Undefined, iife); d.fixed = function() {
}; return iife.args[i] || make_node(AST_Undefined, iife);
mark(d, true); };
} else { mark(d, true);
d.fixed = false; } else {
d.fixed = false;
}
} }
}); });
} }
@@ -655,6 +670,7 @@ merge(Compressor.prototype, {
function can_be_evicted_from_block(node) { function can_be_evicted_from_block(node) {
return !( return !(
node instanceof AST_DefClass || node instanceof AST_DefClass ||
node instanceof AST_Defun ||
node instanceof AST_Let || node instanceof AST_Let ||
node instanceof AST_Const node instanceof AST_Const
); );
@@ -735,6 +751,7 @@ merge(Compressor.prototype, {
// Stop immediately if these node types are encountered // Stop immediately if these node types are encountered
var parent = tt.parent(); var parent = tt.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left) if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|| node instanceof AST_Await
|| node instanceof AST_Debugger || node instanceof AST_Debugger
|| node instanceof AST_Destructuring || node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For) || node instanceof AST_IterationStatement && !(node instanceof AST_For)
@@ -939,13 +956,52 @@ merge(Compressor.prototype, {
// step. nevertheless, it's good to check. // step. nevertheless, it's good to check.
continue loop; continue loop;
case stat instanceof AST_If: case stat instanceof AST_If:
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
if (stat.body instanceof AST_Return) { if (stat.body instanceof AST_Return) {
var value = stat.body.value;
//--- //---
// pretty silly case, but: // pretty silly case, but:
// if (foo()) return; return; ==> foo(); return; // if (foo()) return; return; ==> foo(); return;
if (((in_lambda && ret.length == 0) if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value)
|| (ret[0] instanceof AST_Return && !ret[0].value)) && !value && !stat.alternative) {
&& !stat.body.value && !stat.alternative) {
CHANGED = true; CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, { var cond = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition body: stat.condition
@@ -955,7 +1011,7 @@ merge(Compressor.prototype, {
} }
//--- //---
// if (foo()) return x; return y; ==> return foo() ? x : y; // if (foo()) return x; return y; ==> return foo() ? x : y;
if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) { if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) {
CHANGED = true; CHANGED = true;
stat = stat.clone(); stat = stat.clone();
stat.alternative = ret[0]; stat.alternative = ret[0];
@@ -965,7 +1021,7 @@ merge(Compressor.prototype, {
//--- //---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined; // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return) if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
&& stat.body.value && !stat.alternative && in_lambda) { && value && !stat.alternative && in_lambda) {
CHANGED = true; CHANGED = true;
stat = stat.clone(); stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, { stat.alternative = ret[0] || make_node(AST_Return, stat, {
@@ -974,22 +1030,6 @@ merge(Compressor.prototype, {
ret[0] = stat.transform(compressor); ret[0] = stat.transform(compressor);
continue loop; continue loop;
} }
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
continue loop;
}
//--- //---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
// //
@@ -1009,48 +1049,6 @@ merge(Compressor.prototype, {
} }
} }
var ab = aborts(stat.body);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var body = as_statement_array(stat.body).slice(0, -1);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ];
continue loop;
}
var ab = aborts(stat.alternative);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: as_statement_array(stat.alternative).slice(0, -1)
});
ret = [ stat.transform(compressor) ];
continue loop;
}
ret.unshift(stat); ret.unshift(stat);
break; break;
default: default:
@@ -1070,6 +1068,30 @@ merge(Compressor.prototype, {
} }
return false; return false;
} }
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab || !all(ret, function(stat) {
return !(stat instanceof AST_Const || stat instanceof AST_Let);
})) return false;
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
}; };
function eliminate_dead_code(statements, compressor) { function eliminate_dead_code(statements, compressor) {
@@ -1236,8 +1258,15 @@ merge(Compressor.prototype, {
target.push(node); target.push(node);
return true; return true;
} }
if (node instanceof AST_Defun) { if (node instanceof AST_Defun && (node === stat || !compressor.has_directive("use strict"))) {
target.push(node); target.push(node === stat ? node : make_node(AST_Var, node, {
definitions: [
make_node(AST_VarDef, node, {
name: make_node(AST_SymbolVar, node.name, node.name),
value: null
})
]
}));
return true; return true;
} }
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
@@ -1432,7 +1461,7 @@ merge(Compressor.prototype, {
}); });
if (value && typeof value == "object") { if (value && typeof value == "object") {
var props = []; var props = [];
for (var key in value) { for (var key in value) if (HOP(value, key)) {
props.push(make_node(AST_ObjectKeyVal, orig, { props.push(make_node(AST_ObjectKeyVal, orig, {
key: key, key: key,
value: to_node(value[key], orig) value: to_node(value[key], orig)
@@ -1691,6 +1720,63 @@ merge(Compressor.prototype, {
} }
throw def; throw def;
}); });
var object_fns = [
'constructor',
'toString',
'valueOf',
];
var native_fns = {
Array: makePredicate([
'indexOf',
'join',
'lastIndexOf',
'slice',
].concat(object_fns)),
Boolean: makePredicate(object_fns),
Number: makePredicate([
'toExponential',
'toFixed',
'toPrecision',
].concat(object_fns)),
RegExp: makePredicate([
'test',
].concat(object_fns)),
String: makePredicate([
'charAt',
'charCodeAt',
'concat',
'indexOf',
'italics',
'lastIndexOf',
'match',
'replace',
'search',
'slice',
'split',
'substr',
'substring',
'trim',
].concat(object_fns)),
};
def(AST_Call, function(compressor){
var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property;
if (key instanceof AST_Node) {
key = ev(key, compressor);
}
var val = ev(exp.expression, compressor);
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
return val[key].apply(val, this.args.map(function(arg) {
return ev(arg, compressor);
}));
}
}
throw def;
});
def(AST_New, function(compressor){
throw def;
});
})(function(node, func){ })(function(node, func){
node.DEFMETHOD("_eval", func); node.DEFMETHOD("_eval", func);
}); });
@@ -1839,6 +1925,7 @@ merge(Compressor.prototype, {
}); });
def(AST_Defun, return_true); def(AST_Defun, return_true);
def(AST_Function, return_false); def(AST_Function, return_false);
def(AST_Arrow, return_false);
def(AST_Class, return_false); def(AST_Class, return_false);
def(AST_DefClass, return_true); def(AST_DefClass, return_true);
def(AST_Binary, function(compressor){ def(AST_Binary, function(compressor){
@@ -1972,7 +2059,8 @@ merge(Compressor.prototype, {
self.body = tighten_body(self.body, compressor); self.body = tighten_body(self.body, compressor);
switch (self.body.length) { switch (self.body.length) {
case 1: case 1:
if (can_be_evicted_from_block(self.body[0])) { if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If
|| can_be_evicted_from_block(self.body[0])) {
return self.body[0]; return self.body[0];
} }
break; break;
@@ -2096,15 +2184,21 @@ merge(Compressor.prototype, {
})); }));
} }
} }
else if (node.names[i] instanceof AST_Hole) { else if (node.names[i] instanceof AST_ObjectKeyVal) {
continue; if (typeof node.names[i].key === "string") {
} initializations.add(node.names[i].key, destructuring_value);
else if (node.names[i] instanceof AST_ObjectKeyVal && typeof node.names[i].key === "string") { }
initializations.add(node.names[i].key, destructuring_value);
} }
else if (node.names[i] instanceof AST_Symbol) { else if (node.names[i] instanceof AST_Symbol) {
initializations.add(node.names[i].name, destructuring_value); initializations.add(node.names[i].name, destructuring_value);
} else { }
else if (node.names[i] instanceof AST_DefaultAssign) {
continue;
}
else if (node.names[i] instanceof AST_Hole) {
continue;
}
else {
throw new Error(string_template("Unknown destructuring element of type: {type}", { throw new Error(string_template("Unknown destructuring element of type: {type}", {
type: Object.getPrototypeOf(node.names[i]).TYPE type: Object.getPrototypeOf(node.names[i]).TYPE
})); }));
@@ -2139,9 +2233,11 @@ merge(Compressor.prototype, {
// pass 3: we should drop declarations not in_use // pass 3: we should drop declarations not in_use
var tt = new TreeTransformer( var tt = new TreeTransformer(
function before(node, descend, in_list) { function before(node, descend, in_list) {
if (node instanceof AST_Function var parent = tt.parent();
&& node.name if (!compressor.option("keep_fnames")
&& !compressor.option("keep_fnames")) { && ((node instanceof AST_Function || node instanceof AST_ClassExpression) && node.name
|| (node instanceof AST_Defun || node instanceof AST_DefClass)
&& parent instanceof AST_Export && parent.is_default)) {
var def = node.name.definition(); var def = node.name.definition();
// any declarations with same name will overshadow // any declarations with same name will overshadow
// name of this anonymous function and can therefore // name of this anonymous function and can therefore
@@ -2176,7 +2272,7 @@ merge(Compressor.prototype, {
} }
} }
} }
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) { if ((node instanceof AST_Defun || node instanceof AST_DefClass) && !(parent instanceof AST_Export) && node !== self) {
var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global; var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global;
if (!keep) { if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
@@ -2184,7 +2280,9 @@ merge(Compressor.prototype, {
} }
return node; return node;
} }
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) { if (node instanceof AST_Definitions
&& !(parent instanceof AST_ForIn && parent.init === node)
&& (drop_vars || !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var))) {
// place uninitialized names at the start // place uninitialized names at the start
var body = [], head = [], tail = []; var body = [], head = [], tail = [];
// for unused names whose initialization has // for unused names whose initialization has
@@ -2280,7 +2378,7 @@ merge(Compressor.prototype, {
if (!(def.id in in_use_ids) if (!(def.id in in_use_ids)
&& (drop_vars || !def.global) && (drop_vars || !def.global)
&& self.variables.get(def.name) === def) { && self.variables.get(def.name) === def) {
return maintain_this_binding(tt.parent(), node, node.right.transform(tt)); return maintain_this_binding(parent, node, node.right.transform(tt));
} }
} }
// certain combination of unused name + side effect leads to: // certain combination of unused name + side effect leads to:
@@ -2366,11 +2464,13 @@ merge(Compressor.prototype, {
dirs.push(node); dirs.push(node);
return make_node(AST_EmptyStatement, node); return make_node(AST_EmptyStatement, node);
} }
if (node instanceof AST_Defun && hoist_funs) { if (hoist_funs && node instanceof AST_Defun
&& !(tt.parent() instanceof AST_Export)
&& tt.parent() === self) {
hoisted.push(node); hoisted.push(node);
return make_node(AST_EmptyStatement, node); return make_node(AST_EmptyStatement, node);
} }
if (node instanceof AST_Var && hoist_vars) { if (hoist_vars && node instanceof AST_Var) {
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
if (def.name instanceof AST_Destructuring) return; if (def.name instanceof AST_Destructuring) return;
vars.set(def.name.name, def); vars.set(def.name.name, def);
@@ -2513,6 +2613,8 @@ merge(Compressor.prototype, {
}); });
def(AST_Accessor, return_null); def(AST_Accessor, return_null);
def(AST_Function, return_null); def(AST_Function, return_null);
def(AST_Arrow, return_null);
def(AST_ClassExpression, return_null);
def(AST_Binary, function(compressor, first_in_statement){ def(AST_Binary, function(compressor, first_in_statement){
var right = this.right.drop_side_effect_free(compressor); var right = this.right.drop_side_effect_free(compressor);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement); if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
@@ -3012,24 +3114,9 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){ OPT(AST_Call, function(self, compressor){
var exp = self.expression; var exp = self.expression;
if (compressor.option("reduce_vars") if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
&& exp instanceof AST_SymbolRef) {
var def = exp.definition();
var fixed = exp.fixed_value(); var fixed = exp.fixed_value();
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Function) exp = fixed;
def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
}
if (fixed instanceof AST_Function) {
exp = fixed;
if (compressor.option("unused")
&& def.references.length == 1
&& !(def.scope.uses_arguments
&& def.orig[0] instanceof AST_SymbolFunarg)
&& !def.scope.uses_eval
&& compressor.find_parent(AST_Scope) === def.scope) {
self.expression = exp;
}
}
} }
if (compressor.option("unused") if (compressor.option("unused")
&& exp instanceof AST_Function && exp instanceof AST_Function
@@ -3100,63 +3187,6 @@ merge(Compressor.prototype, {
operator: "!" operator: "!"
}).optimize(compressor); }).optimize(compressor);
break; break;
case "Function":
// new Function() => function(){}
if (self.args.length == 0) return make_node(AST_Function, self, {
argnames: [],
body: []
});
if (all(self.args, function(x){ return x instanceof AST_String })) {
// quite a corner-case, but we can handle it:
// https://github.com/mishoo/UglifyJS2/issues/203
// if the code argument is a constant, then we can minify it.
try {
var code = "(function(" + self.args.slice(0, -1).map(function(arg){
return arg.value;
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
var ast = parse(code);
var mangle = { ie8: compressor.option("ie8") };
ast.figure_out_scope(mangle);
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
ast.mangle_names();
var fun;
try {
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_Lambda) {
fun = node;
throw ast;
}
}));
} catch(ex) {
if (ex !== ast) throw ex;
};
if (!fun) return self;
var args = fun.argnames.map(function(arg, i){
return make_node(AST_String, self.args[i], {
value: arg.print_to_string({ecma: compressor.option("ecma")})
});
});
var code = OutputStream();
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
code = code.toString().replace(/^\{|\}$/g, "");
args.push(make_node(AST_String, self.args[self.args.length - 1], {
value: code
}));
self.args = args;
return self;
} catch(ex) {
if (ex instanceof JS_Parse_Error) {
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
compressor.warn(ex.toString());
} else {
console.log(ex);
throw ex;
}
}
}
break;
case "Symbol": case "Symbol":
// Symbol's argument is only used for debugging. // Symbol's argument is only used for debugging.
self.args = []; self.args = [];
@@ -3243,14 +3273,133 @@ merge(Compressor.prototype, {
} }
} }
} }
if (exp instanceof AST_Function && !self.expression.is_generator) { if (compressor.option("unsafe_Func")
if (exp.body[0] instanceof AST_Return) { && exp instanceof AST_SymbolRef
var value = exp.body[0].value; && exp.undeclared()
&& exp.name == "Function") {
// new Function() => function(){}
if (self.args.length == 0) return make_node(AST_Function, self, {
argnames: [],
body: []
});
if (all(self.args, function(x) {
return x instanceof AST_String;
})) {
// quite a corner-case, but we can handle it:
// https://github.com/mishoo/UglifyJS2/issues/203
// if the code argument is a constant, then we can minify it.
try {
var code = "NaN(function(" + self.args.slice(0, -1).map(function(arg) {
return arg.value;
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
var ast = parse(code);
var mangle = { ie8: compressor.option("ie8") };
ast.figure_out_scope(mangle);
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
ast.mangle_names();
var fun;
ast.walk(new TreeWalker(function(node) {
if (fun) return true;
if (node instanceof AST_Lambda) {
fun = node;
return true;
}
}));
var args = fun.argnames.map(function(arg, i) {
return make_node(AST_String, self.args[i], {
value: arg.print_to_string()
});
});
var code = OutputStream();
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
code = code.toString().replace(/^\{|\}$/g, "");
args.push(make_node(AST_String, self.args[self.args.length - 1], {
value: code
}));
self.args = args;
return self;
} catch (ex) {
if (ex instanceof JS_Parse_Error) {
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
compressor.warn(ex.toString());
} else {
throw ex;
}
}
}
}
if (exp instanceof AST_Function && !self.expression.is_generator && !self.expression.async) {
var stat = exp.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat && stat.value;
if (!value || value.is_constant_expression()) { if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self)); var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor); return make_sequence(self, args).transform(compressor);
} }
} }
if (compressor.option("inline")
&& !exp.name
&& exp.body.length == 1
&& !exp.uses_arguments
&& !exp.uses_eval
&& !self.has_pure_annotation(compressor)) {
var value;
if (stat instanceof AST_Return) {
value = stat.value.clone(true);
} else if (stat instanceof AST_SimpleStatement) {
value = make_node(AST_UnaryPrefix, stat, {
operator: "void",
expression: stat.body.clone(true)
});
}
if (value) {
var fn = exp.clone();
fn.argnames = [];
fn.body = [];
if (exp.argnames.length > 0) {
fn.body.push(make_node(AST_Var, self, {
definitions: exp.argnames.map(function(sym, i) {
return make_node(AST_VarDef, sym, {
name: sym,
value: self.args[i] || make_node(AST_Undefined, self)
});
})
}));
}
if (self.args.length > exp.argnames.length) {
fn.body.push(make_node(AST_SimpleStatement, self, {
body: make_sequence(self, self.args.slice(exp.argnames.length))
}));
}
fn.body.push(make_node(AST_Return, self, {
value: value
}));
var body = fn.transform(compressor).body;
if (body.length == 0) return make_node(AST_Undefined, self);
if (body.length == 1 && body[0] instanceof AST_Return) {
value = body[0].value;
if (!value) return make_node(AST_Undefined, self);
value.walk(new TreeWalker(function(node) {
if (value === self) return true;
if (node instanceof AST_SymbolRef && matches(node.scope.find_variable(node))
|| node instanceof AST_This && matches(node)) {
value = self;
return true;
}
function matches(ref) {
return ref && ref.scope.parent_scope === fn.parent_scope;
}
}));
if (value !== self) value = best_of(compressor, value, self);
} else {
value = self;
}
if (value !== self) return value;
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) { if (compressor.option("side_effects") && all(exp.body, is_empty)) {
var args = self.args.concat(make_node(AST_Undefined, self)); var args = self.args.concat(make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor); return make_sequence(self, args).transform(compressor);
@@ -3274,6 +3423,11 @@ merge(Compressor.prototype, {
&& is_iife_call(self)) { && is_iife_call(self)) {
return self.negate(compressor, true); return self.negate(compressor, true);
} }
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self; return self;
}); });
@@ -3348,6 +3502,7 @@ merge(Compressor.prototype, {
continue; continue;
} }
var parent = null, field; var parent = null, field;
expressions[j] = cdr = cdr.clone();
while (true) { while (true) {
if (cdr.equivalent_to(left)) { if (cdr.equivalent_to(left)) {
var car = expressions[i]; var car = expressions[i];
@@ -3384,7 +3539,7 @@ merge(Compressor.prototype, {
break; break;
} }
parent = cdr; parent = cdr;
cdr = cdr[field]; cdr = cdr[field] = cdr[field].clone();
} }
} }
end = i; end = i;
@@ -3904,12 +4059,22 @@ merge(Compressor.prototype, {
return make_node(AST_Infinity, self).optimize(compressor); return make_node(AST_Infinity, self).optimize(compressor);
} }
} }
if (compressor.option("evaluate") if (compressor.option("reduce_vars")
&& compressor.option("reduce_vars")
&& is_lhs(self, compressor.parent()) !== self) { && is_lhs(self, compressor.parent()) !== self) {
var d = self.definition(); var d = self.definition();
var fixed = self.fixed_value(); var fixed = self.fixed_value();
if (fixed) { if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
}
if (compressor.option("unused")
&& fixed instanceof AST_Function
&& d.references.length == 1
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope) {
return fixed;
}
if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) { if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor); var init = fixed.evaluate(compressor);
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {

View File

@@ -30,9 +30,6 @@ function set_shorthand(name, options, keys) {
function minify(files, options) { function minify(files, options) {
var warn_function = AST_Node.warn_function; var warn_function = AST_Node.warn_function;
try { try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, { options = defaults(options, {
compress: {}, compress: {},
ie8: false, ie8: false,
@@ -41,10 +38,14 @@ function minify(files, options) {
output: {}, output: {},
parse: {}, parse: {},
sourceMap: false, sourceMap: false,
timings: false,
toplevel: false, toplevel: false,
warnings: false, warnings: false,
wrap: false, wrap: false,
}, true); }, true);
var timings = options.timings && {
start: Date.now()
};
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
@@ -77,13 +78,17 @@ function minify(files, options) {
warnings.push(warning); warnings.push(warning);
}; };
} }
if (timings) timings.parse = Date.now();
var toplevel; var toplevel;
if (files instanceof AST_Toplevel) { if (files instanceof AST_Toplevel) {
toplevel = files; toplevel = files;
} else { } else {
if (typeof files == "string") {
files = [ files ];
}
options.parse = options.parse || {}; options.parse = options.parse || {};
options.parse.toplevel = null; options.parse.toplevel = null;
for (var name in files) { for (var name in files) if (HOP(files, name)) {
options.parse.filename = name; options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse); options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") { if (options.sourceMap && options.sourceMap.content == "inline") {
@@ -97,19 +102,23 @@ function minify(files, options) {
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }
if (options.compress) { if (timings) timings.scope1 = Date.now();
toplevel.figure_out_scope(options.mangle); if (options.compress) toplevel.figure_out_scope(options.mangle);
toplevel = new Compressor(options.compress).compress(toplevel); if (timings) timings.compress = Date.now();
} if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) { if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
base54.reset(); base54.reset();
toplevel.compute_char_frequency(options.mangle); toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle); toplevel.mangle_names(options.mangle);
if (options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
} }
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now();
var result = {}; var result = {};
if (options.output.ast) { if (options.output.ast) {
result.ast = toplevel; result.ast = toplevel;
@@ -125,7 +134,9 @@ function minify(files, options) {
root: options.sourceMap.root root: options.sourceMap.root
}); });
if (options.sourceMap.includeSources) { if (options.sourceMap.includeSources) {
for (var name in files) { if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) if (HOP(files, name)) {
options.output.source_map.get().setSourceContent(name, files[name]); options.output.source_map.get().setSourceContent(name, files[name]);
} }
} }
@@ -144,6 +155,18 @@ function minify(files, options) {
} }
} }
} }
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),
total: 1e-3 * (timings.end - timings.start)
}
}
if (warnings.length) { if (warnings.length) {
result.warnings = warnings; result.warnings = warnings;
} }

View File

@@ -73,6 +73,7 @@ function OutputStream(options) {
shebang : true, shebang : true,
shorthand : undefined, shorthand : undefined,
source_map : null, source_map : null,
webkit : false,
width : 80, width : 80,
wrap_iife : false, wrap_iife : false,
}, true); }, true);
@@ -212,12 +213,43 @@ function OutputStream(options) {
var might_need_semicolon = false; var might_need_semicolon = false;
var might_add_newline = 0; var might_add_newline = 0;
var last = ""; var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var do_add_mapping = mappings ? function() {
mappings.forEach(function(mapping) {
try {
options.source_map.add(
mapping.token.file,
mapping.line, mapping.col,
mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
});
mappings = [];
} : noop;
var ensure_line_len = options.max_line_len ? function() { var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) { if (current_col > options.max_line_len) {
if (might_add_newline) { if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline); var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline); var right = OUTPUT.slice(might_add_newline);
if (mappings) {
var delta = right.length - current_col;
mappings.forEach(function(mapping) {
mapping.line++;
mapping.col += delta;
});
}
OUTPUT = left + "\n" + right; OUTPUT = left + "\n" + right;
current_line++; current_line++;
current_pos++; current_pos++;
@@ -227,7 +259,10 @@ function OutputStream(options) {
AST_Node.warn("Output exceeds {max_line_len} characters", options); AST_Node.warn("Output exceeds {max_line_len} characters", options);
} }
} }
might_add_newline = 0; if (might_add_newline) {
might_add_newline = 0;
do_add_mapping();
}
} : noop; } : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , ."); var requireSemicolonChars = makePredicate("( [ + * / - , .");
@@ -287,6 +322,18 @@ function OutputStream(options) {
} }
might_need_space = false; might_need_space = false;
} }
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str; OUTPUT += str;
current_pos += str.length; current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1; var a = str.split(/\r?\n/), n = a.length - 1;
@@ -385,24 +432,9 @@ function OutputStream(options) {
space(); space();
}; };
var add_mapping = options.source_map ? function(token, name) { var add_mapping = mappings ? function(token, name) {
try { mapping_token = token;
if (token) options.source_map.add( mapping_name = name;
token.file || "?",
current_line, current_col,
token.line, token.col,
(!name && token.type == "name") ? token.value : name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: token.file,
line: token.line,
col: token.col,
cline: current_line,
ccol: current_col,
name: name || ""
})
}
} : noop; } : noop;
function get() { function get() {
@@ -598,6 +630,13 @@ function OutputStream(options) {
return true; return true;
} }
if (output.option('webkit')) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
return true;
}
}
if (output.option('wrap_iife')) { if (output.option('wrap_iife')) {
var p = output.parent(); var p = output.parent();
return p instanceof AST_Call && p.expression === this; return p instanceof AST_Call && p.expression === this;
@@ -611,6 +650,10 @@ function OutputStream(options) {
return p instanceof AST_PropAccess && p.expression === this; 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 // same goes for an object literal, because otherwise it would be
// interpreted as a block of code. // interpreted as a block of code.
PARENS(AST_Object, function(output){ PARENS(AST_Object, function(output){
@@ -774,17 +817,15 @@ function OutputStream(options) {
DEFPRINT(AST_Destructuring, function (self, output) { DEFPRINT(AST_Destructuring, function (self, output) {
output.print(self.is_array ? "[" : "{"); output.print(self.is_array ? "[" : "{");
var first = true;
var len = self.names.length; var len = self.names.length;
self.names.forEach(function (name, i) { self.names.forEach(function (name, i) {
if (first) first = false; else { output.comma(); output.space(); } if (i > 0) output.comma();
name.print(output); name.print(output);
// If the final element is a hole, we need to make sure it // If the final element is a hole, we need to make sure it
// doesn't look like a trailing comma, by inserting an actual // doesn't look like a trailing comma, by inserting an actual
// trailing comma. // trailing comma.
if (i === len - 1 && name instanceof AST_Hole) if (i == len - 1 && name instanceof AST_Hole) output.comma();
output.comma(); });
})
output.print(self.is_array ? "]" : "}"); output.print(self.is_array ? "]" : "}");
}); });
@@ -937,6 +978,10 @@ function OutputStream(options) {
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
var self = this; var self = this;
if (!nokeyword) { if (!nokeyword) {
if (this.async) {
output.print("async");
output.space();
}
output.print("function"); output.print("function");
if (this.is_generator) { if (this.is_generator) {
output.star(); output.star();
@@ -1042,6 +1087,22 @@ function OutputStream(options) {
} }
}); });
DEFPRINT(AST_Await, function(self, output){
output.print("await");
output.space();
var e = self.expression;
var parens = !(
e instanceof AST_Call
|| e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
|| e instanceof AST_Unary
|| e instanceof AST_Constant
);
if (parens) output.print("(");
self.expression.print(output);
if (parens) output.print(")");
});
/* -----[ loop control ]----- */ /* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind); output.print(kind);
@@ -1505,7 +1566,8 @@ function OutputStream(options) {
DEFPRINT(AST_NewTarget, function(self, output) { DEFPRINT(AST_NewTarget, function(self, output) {
output.print("new.target"); output.print("new.target");
}); });
AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) {
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) { if (output.option("quote_keys")) {
output.print_string(key + ""); output.print_string(key + "");
} else if ((typeof key == "number" } else if ((typeof key == "number"
@@ -1522,7 +1584,8 @@ function OutputStream(options) {
} else { } else {
output.print_string(key, quote); output.print_string(key, quote);
} }
}); }
DEFPRINT(AST_ObjectKeyVal, function(self, output){ DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) { function get_name(self) {
var def = self.definition(); var def = self.definition();
@@ -1535,7 +1598,7 @@ function OutputStream(options) {
is_identifier_string(self.key) && is_identifier_string(self.key) &&
get_name(self.value) === self.key get_name(self.value) === self.key
) { ) {
self.print_property_name(self.key, self.quote, output); print_property_name(self.key, self.quote, output);
} else if (allowShortHand && } else if (allowShortHand &&
self.value instanceof AST_DefaultAssign && self.value instanceof AST_DefaultAssign &&
@@ -1543,12 +1606,14 @@ function OutputStream(options) {
is_identifier_string(self.key) && is_identifier_string(self.key) &&
get_name(self.value.left) === self.key get_name(self.value.left) === self.key
) { ) {
self.print_property_name(self.key, self.quote, output); print_property_name(self.key, self.quote, output);
output.space();
output.print("="); output.print("=");
output.space();
self.value.right.print(output); self.value.right.print(output);
} else { } else {
if (!(self.key instanceof AST_Node)) { if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output); print_property_name(self.key, self.quote, output);
} else { } else {
output.with_square(function() { output.with_square(function() {
self.key.print(output); self.key.print(output);
@@ -1558,15 +1623,18 @@ function OutputStream(options) {
self.value.print(output); self.value.print(output);
} }
}); });
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) { AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
var self = this;
if (self.static) { if (self.static) {
output.print("static"); output.print("static");
output.space(); output.space();
} }
output.print(type); if (type) {
output.space(); output.print(type);
output.space();
}
if (self.key instanceof AST_SymbolMethod) { if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output); print_property_name(self.key.name, self.quote, output);
} else { } else {
output.with_square(function() { output.with_square(function() {
self.key.print(output); self.key.print(output);
@@ -1575,27 +1643,13 @@ function OutputStream(options) {
self.value._do_print(output, true); self.value._do_print(output, true);
}); });
DEFPRINT(AST_ObjectSetter, function(self, output){ DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", self, output); self._print_getter_setter("set", output);
}); });
DEFPRINT(AST_ObjectGetter, function(self, output){ DEFPRINT(AST_ObjectGetter, function(self, output){
self._print_getter_setter("get", self, output); self._print_getter_setter("get", output);
}); });
DEFPRINT(AST_ConciseMethod, function(self, output){ DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) { self._print_getter_setter(self.is_generator && "*" || self.async && "async", output);
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
}); });
AST_Symbol.DEFMETHOD("_do_print", function(output){ AST_Symbol.DEFMETHOD("_do_print", function(output){
var def = this.definition(); var def = this.definition();

View File

@@ -47,7 +47,7 @@
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import'; var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import';
var KEYWORDS_ATOM = 'false null true'; var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS; var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield'; var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield await';
KEYWORDS = makePredicate(KEYWORDS); KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS); RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -122,8 +122,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
/* -----[ Tokenizer ]----- */ /* -----[ Tokenizer ]----- */
// surrogate safe regexps adapted from https://github.com/mathiasbynens/unicode-8.0.0/tree/89b412d8a71ecca9ed593d9e9fa073ab64acfebe/Binary_Property // surrogate safe regexps adapted from https://github.com/mathiasbynens/unicode-8.0.0/tree/89b412d8a71ecca9ed593d9e9fa073ab64acfebe/Binary_Property
@@ -850,9 +848,7 @@ var PRECEDENCE = (function(a, ret){
{} {}
); );
var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
/* -----[ Parser ]----- */ /* -----[ Parser ]----- */
@@ -866,7 +862,7 @@ function parse($TEXT, options) {
shebang : true, shebang : true,
strict : false, strict : false,
toplevel : null, toplevel : null,
}); }, true);
var S = { var S = {
input : (typeof $TEXT == "string" input : (typeof $TEXT == "string"
@@ -877,6 +873,7 @@ function parse($TEXT, options) {
prev : null, prev : null,
peeked : null, peeked : null,
in_function : 0, in_function : 0,
in_async : -1,
in_generator : -1, in_generator : -1,
in_directives : true, in_directives : true,
in_loop : 0, in_loop : 0,
@@ -947,6 +944,10 @@ function parse($TEXT, options) {
return S.in_generator === S.in_function; return S.in_generator === S.in_function;
} }
function is_in_async() {
return S.in_async === S.in_function;
}
function semicolon(optional) { function semicolon(optional) {
if (is("punc", ";")) next(); if (is("punc", ";")) next();
else if (!optional && !can_insert_semicolon()) unexpected(); else if (!optional && !can_insert_semicolon()) unexpected();
@@ -1003,6 +1004,11 @@ function parse($TEXT, options) {
return simple_statement(); return simple_statement();
case "name": case "name":
if (S.token.value == "async" && is_token(peek(), "keyword", "function")) {
next();
next();
return function_(AST_Defun, false, true);
}
return is_token(peek(), "punc", ":") return is_token(peek(), "punc", ":")
? labeled_statement() ? labeled_statement()
: simple_statement(); : simple_statement();
@@ -1159,6 +1165,9 @@ function parse($TEXT, options) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors // Ecma-262, 12.1.1 Static Semantics: Early Errors
token_error(S.prev, "Yield cannot be used as label inside generators"); 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");
}
if (find_if(function(l){ return l.name == label.name }, S.labels)) { if (find_if(function(l){ return l.name == label.name }, S.labels)) {
// ECMA-262, 12.12: An ECMAScript program is considered // ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a // syntactically incorrect if it contains a
@@ -1272,39 +1281,31 @@ function parse($TEXT, options) {
}); });
}; };
var arrow_function = function(args) { var arrow_function = function(start, argnames) {
if (S.token.nlb) { if (S.token.nlb) {
croak("Unexpected newline before arrow (=>)"); croak("Unexpected newline before arrow (=>)");
} }
expect_token("arrow", "=>"); expect_token("arrow", "=>");
var argnames; var body = _function_body(is("punc", "{"));
if (typeof args.length === 'number') {
argnames = args;
} else {
argnames = args.as_params(croak);
}
var body = is("punc", "{") ?
_function_body(true) :
_function_body(false);
return new AST_Arrow({ return new AST_Arrow({
start : args.start, start : start,
end : body.end, end : body.end,
argnames : argnames, argnames : argnames,
body : body body : body
}); });
}; };
var function_ = function(ctor, is_generator_property) { var function_ = function(ctor, is_generator_property, is_async) {
if (is_generator_property && is_async) croak("generators cannot be async");
var start = S.token var start = S.token
var in_statement = ctor === AST_Defun; var in_statement = ctor === AST_Defun;
var is_generator = is("operator", "*"); var is_generator = is("operator", "*");
if (is_generator) { if (is_generator) {
next(); next();
} }
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
@@ -1312,11 +1313,12 @@ function parse($TEXT, options) {
unexpected(); unexpected();
var args = parameters(); var args = parameters();
var body = _function_body(true, is_generator || is_generator_property, name, args); var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
return new ctor({ return new ctor({
start : args.start, start : args.start,
end : body.end, end : body.end,
is_generator: is_generator, is_generator: is_generator,
async : is_async,
name : name, name : name,
argnames: args, argnames: args,
body : body body : body
@@ -1615,8 +1617,6 @@ function parse($TEXT, options) {
} }
function params_or_seq_() { function params_or_seq_() {
var start = S.token
expect("(");
var first = true; var first = true;
var a = []; var a = [];
while (!is("punc", ")")) { while (!is("punc", ")")) {
@@ -1626,32 +1626,29 @@ function parse($TEXT, options) {
next(); next();
a.push(new AST_Expansion({ a.push(new AST_Expansion({
start: prev(), start: prev(),
expression: expression(false), expression: expression(),
end: S.token, end: S.token,
})); }));
if (!is("punc", ")")) { if (!is("punc", ")")) {
unexpected(spread_token); unexpected(spread_token);
} }
} else { } else {
a.push(expression(false)); a.push(expression());
} }
} }
var end = S.token return a;
next();
return new AST_ArrowParametersOrSeq({
start: start,
end: end,
expressions: a
});
} }
function _function_body(block, generator, name, args) { function _function_body(block, generator, is_async, name, args) {
var loop = S.in_loop; var loop = S.in_loop;
var labels = S.labels; var labels = S.labels;
var current_generator = S.in_generator; var current_generator = S.in_generator;
var current_async = S.in_async;
++S.in_function; ++S.in_function;
if (generator) if (generator)
S.in_generator = S.in_function; S.in_generator = S.in_function;
if (is_async)
S.in_async = S.in_function;
if (block) if (block)
S.in_directives = true; S.in_directives = true;
S.in_loop = 0; S.in_loop = 0;
@@ -1671,9 +1668,22 @@ function parse($TEXT, options) {
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
S.in_generator = current_generator; S.in_generator = current_generator;
S.in_async = current_async;
return a; return a;
} }
function _await_expression() {
// Previous token must be "await" and not be interpreted as an identifier
if (!is_in_async()) {
croak("Unexpected await expression outside async function",
S.prev.line, S.prev.col, S.prev.pos);
}
// the await expression is parsed as a unary expression in Babel
return new AST_Await({
expression : maybe_unary(true),
});
}
function _yield_expression() { function _yield_expression() {
// Previous token must be keyword yield and not be interpret as an identifier // Previous token must be keyword yield and not be interpret as an identifier
if (!is_in_generator()) { if (!is_in_generator()) {
@@ -1682,7 +1692,6 @@ function parse($TEXT, options) {
} }
var star = false; var star = false;
var has_expression = true; var has_expression = true;
var tmp;
// Attempt to get expression or star (and then the mandatory expression) // Attempt to get expression or star (and then the mandatory expression)
// behind yield on the same line. // behind yield on the same line.
@@ -1889,7 +1898,6 @@ function parse($TEXT, options) {
var tok = S.token, ret; var tok = S.token, ret;
switch (tok.type) { switch (tok.type) {
case "name": case "name":
case "keyword":
ret = _make_symbol(AST_SymbolRef); ret = _make_symbol(AST_SymbolRef);
break; break;
case "num": case "num":
@@ -1919,18 +1927,68 @@ function parse($TEXT, options) {
break; break;
} }
break; break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
} }
next(); next();
return ret; return ret;
}; };
function to_fun_args(ex, _, __, default_seen_above) {
var insert_default = function(ex, default_value) {
if (default_value) {
return new AST_DefaultAssign({
start: ex.start,
left: ex,
operator: "=",
right: default_value,
end: default_value.end
});
}
return ex;
}
if (ex instanceof AST_Object) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
names: ex.properties.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key]);
}
ex.value = to_fun_args(ex.value, 0, [ex.key]);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Hole) {
return ex;
} else if (ex instanceof AST_Destructuring) {
ex.names = ex.names.map(to_fun_args);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_SymbolRef) {
return insert_default(new AST_SymbolFunarg({
name: ex.name,
start: ex.start,
end: ex.end
}), default_seen_above);
} else if (ex instanceof AST_Expansion) {
ex.expression = to_fun_args(ex.expression);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Array) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
names: ex.elements.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_Assign) {
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
} else if (ex instanceof AST_DefaultAssign) {
ex.left = to_fun_args(ex.left, 0, [ex.left]);
return ex;
} else {
croak("Invalid function parameter", ex.start.line, ex.start.col);
}
}
var expr_atom = function(allow_calls) { var expr_atom = function(allow_calls) {
if (is("operator", "new")) { if (is("operator", "new")) {
return new_(allow_calls); return new_(allow_calls);
@@ -1939,13 +1997,15 @@ function parse($TEXT, options) {
if (is("punc")) { if (is("punc")) {
switch (start.value) { switch (start.value) {
case "(": case "(":
var ex = params_or_seq_(); next();
var exprs = params_or_seq_();
expect(")");
if (is("arrow", "=>")) { if (is("arrow", "=>")) {
ex.start = start; return arrow_function(start, exprs.map(to_fun_args));
ex.end = S.token;
return arrow_function(ex);
} }
ex = ex.as_expr(croak); var ex = exprs.length == 1 ? exprs[0] : new AST_Sequence({
expressions: exprs
});
ex.start = start; ex.start = start;
ex.end = S.token; ex.end = S.token;
return subscripts(ex, allow_calls); return subscripts(ex, allow_calls);
@@ -1956,6 +2016,14 @@ function parse($TEXT, options) {
} }
unexpected(); unexpected();
} }
if (is("name", "async") && is_token(peek(), "keyword", "function")) {
next();
next();
var func = function_(AST_Function, false, true);
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
}
if (is("keyword", "function")) { if (is("keyword", "function")) {
next(); next();
var func = function_(AST_Function); var func = function_(AST_Function);
@@ -1973,7 +2041,7 @@ function parse($TEXT, options) {
if (is("template_head")) { if (is("template_head")) {
return subscripts(template_string(), allow_calls); return subscripts(template_string(), allow_calls);
} }
if (ATOMIC_START_TOKEN[S.token.type]) { if (ATOMIC_START_TOKEN(S.token.type)) {
return subscripts(as_atom_node(), allow_calls); return subscripts(as_atom_node(), allow_calls);
} }
unexpected(); unexpected();
@@ -2037,8 +2105,8 @@ function parse($TEXT, options) {
}); });
}); });
var create_accessor = embed_tokens(function(is_generator) { var create_accessor = embed_tokens(function(is_generator, is_async) {
return function_(AST_Accessor, is_generator); return function_(AST_Accessor, is_generator, is_async);
}); });
var object_or_object_destructuring_ = embed_tokens(function() { var object_or_object_destructuring_ = embed_tokens(function() {
@@ -2154,6 +2222,7 @@ function parse($TEXT, options) {
} }
return name; return name;
} }
var is_async = false;
var is_static = false; var is_static = false;
var is_generator = false; var is_generator = false;
var property_token = start; var property_token = start;
@@ -2162,6 +2231,11 @@ function parse($TEXT, options) {
property_token = S.token; property_token = S.token;
name = as_property_name(); name = as_property_name();
} }
if (name === "async" && !is("punc", "(")) {
is_async = true;
property_token = S.token;
name = as_property_name();
}
if (name === null) { if (name === null) {
is_generator = true; is_generator = true;
property_token = S.token; property_token = S.token;
@@ -2176,10 +2250,11 @@ function parse($TEXT, options) {
start : start, start : start,
static : is_static, static : is_static,
is_generator: is_generator, is_generator: is_generator,
async : is_async,
key : name, key : name,
quote : name instanceof AST_SymbolMethod ? quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined, property_token.quote : undefined,
value : create_accessor(is_generator), value : create_accessor(is_generator, is_async),
end : prev() end : prev()
}); });
return node; return node;
@@ -2377,9 +2452,26 @@ function parse($TEXT, options) {
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const"); var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
if (is_definition) { if (is_definition) {
if (is_default) unexpected();
exported_definition = statement(); exported_definition = statement();
} else if (is("keyword", "class")) {
var cls = expr_atom(false);
if (cls.name) {
cls.name = new AST_SymbolDefClass(cls.name);
exported_definition = new AST_DefClass(cls);
} else {
exported_value = cls;
}
} else if (is("keyword", "function")) {
var func = expr_atom(false);
if (func.name) {
func.name = new AST_SymbolDefun(func.name);
exported_definition = new AST_Defun(func);
} else {
exported_value = func;
}
} else { } else {
exported_value = expression(); exported_value = expression(false);
semicolon(); semicolon();
} }
@@ -2411,7 +2503,8 @@ function parse($TEXT, options) {
unexpected(tmp); unexpected(tmp);
} }
case "name": case "name":
if (tmp.value === "yield" && S.input.has_directive("use strict") && !is_in_generator()) { if (tmp.value == "yield" && !is_token(peek(), "punc", ":")
&& S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "Unexpected yield identifier inside strict mode"); token_error(tmp, "Unexpected yield identifier inside strict mode");
} }
case "string": case "string":
@@ -2526,6 +2619,14 @@ function parse($TEXT, options) {
var maybe_unary = function(allow_calls) { var maybe_unary = function(allow_calls) {
var start = S.token; var start = S.token;
if (start.type == "name" && start.value == "await") {
if (is_in_async()) {
next();
return _await_expression();
} else if (S.input.has_directive("use strict")) {
token_error(S.token, "Unexpected await identifier inside strict mode")
}
}
if (is("operator") && UNARY_PREFIX(start.value)) { if (is("operator") && UNARY_PREFIX(start.value)) {
next(); next();
handle_regexp(); handle_regexp();
@@ -2667,7 +2768,7 @@ function parse($TEXT, options) {
if (start.type == "punc" && start.value == "(" && peek().value == ")") { if (start.type == "punc" && start.value == "(" && peek().value == ")") {
next(); next();
next(); next();
return arrow_function([]); return arrow_function(start, []);
} }
if (is("name") && is_token(peek(), "arrow")) { if (is("name") && is_token(peek(), "arrow")) {
@@ -2677,7 +2778,7 @@ function parse($TEXT, options) {
end: start, end: start,
}); });
next(); next();
return arrow_function([param]) return arrow_function(start, [param]);
} }
var left = maybe_conditional(no_in); var left = maybe_conditional(no_in);
@@ -2708,16 +2809,7 @@ function parse($TEXT, options) {
next(); next();
commas = true; commas = true;
} }
if (exprs.length == 1) { return exprs.length == 1 ? exprs[0] : new AST_Sequence({
var expr = exprs[0];
if (!(expr instanceof AST_SymbolRef) || !is("arrow", "=>")) return expr;
return arrow_function(new AST_ArrowParametersOrSeq({
start: expr.start,
end: expr.end,
expressions: [expr]
}));
}
return new AST_Sequence({
start : start, start : start,
expressions : exprs, expressions : exprs,
end : peek() end : peek()

View File

@@ -96,7 +96,8 @@ function mangle_properties(ast, options) {
reserved: null, reserved: null,
}); });
var reserved = options.reserved || []; var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved); if (!options.builtins) find_builtins(reserved);
var cache = options.cache; var cache = options.cache;

View File

@@ -51,7 +51,6 @@ function SymbolDef(scope, index, orig) {
this.global = false; this.global = false;
this.export = false; this.export = false;
this.mangled_name = null; this.mangled_name = null;
this.object_destructuring_arg = false;
this.undeclared = false; this.undeclared = false;
this.index = index; this.index = index;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
@@ -65,7 +64,6 @@ SymbolDef.prototype = {
return (this.global && !options.toplevel) return (this.global && !options.toplevel)
|| this.export || this.export
|| this.object_destructuring_arg
|| this.undeclared || this.undeclared
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) || (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|| (options.keep_fnames || (options.keep_fnames
@@ -111,8 +109,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var labels = new Dictionary(); var labels = new Dictionary();
var defun = null; var defun = null;
var in_destructuring = null; var in_destructuring = null;
var in_export = false;
var in_block = 0;
var for_scopes = []; var for_scopes = [];
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) { if (node.is_block_scope()) {
@@ -152,22 +148,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
labels = save_labels; labels = save_labels;
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Export) {
in_export = true;
descend();
in_export = false;
return true;
}
if (node instanceof AST_BlockStatement
|| node instanceof AST_Switch
|| node instanceof AST_Try
|| node instanceof AST_Catch
|| node instanceof AST_Finally) {
in_block++;
descend();
in_block--;
return true;
}
if (node instanceof AST_LabeledStatement) { if (node instanceof AST_LabeledStatement) {
var l = node.label; var l = node.label;
if (labels.has(l.name)) { if (labels.has(l.name)) {
@@ -186,15 +166,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) { if (node instanceof AST_Symbol) {
node.scope = scope; node.scope = scope;
} }
if (node instanceof AST_SymbolFunarg) {
node.object_destructuring_arg = !!in_destructuring;
}
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
node.thedef = node; node.thedef = node;
node.references = []; node.references = [];
} }
if (node instanceof AST_SymbolLambda) { if (node instanceof AST_SymbolLambda) {
defun.def_function(node, in_export, in_block); defun.def_function(node);
} }
else if (node instanceof AST_SymbolDefun) { else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is // Careful here, the scope where this should be defined is
@@ -206,23 +183,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
while (parent_lambda.is_block_scope()) { while (parent_lambda.is_block_scope()) {
parent_lambda = parent_lambda.parent_scope; parent_lambda = parent_lambda.parent_scope;
} }
(node.scope = parent_lambda).def_function(node, in_export, in_block); mark_export((node.scope = parent_lambda).def_function(node), 1);
} }
else if (node instanceof AST_SymbolClass) { else if (node instanceof AST_SymbolClass) {
defun.def_variable(node, in_export, in_block); mark_export(defun.def_variable(node), 1);
} }
else if (node instanceof AST_SymbolImport) { else if (node instanceof AST_SymbolImport) {
scope.def_variable(node, in_export, in_block); scope.def_variable(node);
} }
else if (node instanceof AST_SymbolDefClass) { else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available // This deals with the name of the class being available
// inside the class. // inside the class.
(node.scope = defun.parent_scope).def_function(node, in_export, in_block); mark_export((node.scope = defun.parent_scope).def_function(node), 1);
} }
else if (node instanceof AST_SymbolVar else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet || node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) { || node instanceof AST_SymbolConst) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export, in_block); var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node);
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring; def.destructuring = in_destructuring;
if (defun !== scope) { if (defun !== scope) {
node.mark_enclosed(options); node.mark_enclosed(options);
@@ -234,7 +212,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
} }
else if (node instanceof AST_SymbolCatch) { else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node, in_export, in_block).defun = defun; scope.def_variable(node).defun = defun;
} }
else if (node instanceof AST_LabelRef) { else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name); var sym = labels.get(node.name);
@@ -245,28 +223,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
})); }));
node.thedef = sym; node.thedef = sym;
} }
function mark_export(def, level) {
var node = tw.parent(level);
def.export = node instanceof AST_Export && !node.is_default;
}
}); });
self.walk(tw); self.walk(tw);
// pass 2: find back references and eval // pass 2: find back references and eval
var func = null; self.globals = new Dictionary();
var cls = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
if (node instanceof AST_Class) {
var prev_cls = cls;
cls = node;
descend();
cls = prev_cls;
return true;
}
if (node instanceof AST_LoopControl && node.label) { if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node); node.label.thedef.references.push(node);
return true; return true;
@@ -279,11 +246,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
} }
var sym = node.scope.find_variable(name); var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) { if (!sym) {
sym = self.def_global(node); sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
} }
node.thedef = sym; node.thedef = sym;
node.reference(options); node.reference(options);
@@ -351,22 +317,13 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
this.cname = -1; // the current index for mangling functions/variables this.cname = -1; // the current index for mangling functions/variables
}); });
AST_Node.DEFMETHOD("is_block_scope", function(){ AST_Node.DEFMETHOD("is_block_scope", return_false);
return false; // Behaviour will be overridden by AST_Block AST_Class.DEFMETHOD("is_block_scope", return_false);
}); AST_Lambda.DEFMETHOD("is_block_scope", return_false);
AST_Toplevel.DEFMETHOD("is_block_scope", return_false);
AST_Block.DEFMETHOD("is_block_scope", function(){ AST_SwitchBranch.DEFMETHOD("is_block_scope", return_false);
return ( AST_Block.DEFMETHOD("is_block_scope", return_true);
!(this instanceof AST_Lambda) && AST_IterationStatement.DEFMETHOD("is_block_scope", return_true);
!(this instanceof AST_Toplevel) &&
!(this instanceof AST_Class) &&
!(this instanceof AST_SwitchBranch)
);
});
AST_IterationStatement.DEFMETHOD("is_block_scope", function(){
return true;
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
@@ -404,24 +361,18 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name)); || (this.parent_scope && this.parent_scope.find_variable(name));
}); });
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export, in_block){ AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol, in_export, in_block)); var def = this.def_variable(symbol);
this.functions.set(symbol.name, def);
return def;
}); });
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export, in_block){ AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def; var def;
if (!this.variables.has(symbol.name)) { if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol); def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
def.object_destructuring_arg = symbol.object_destructuring_arg; def.global = !this.parent_scope;
if (in_export) {
def.export = true;
}
if (in_block && symbol instanceof AST_SymbolBlockDeclaration) {
def.global = false;
} else {
def.global = !this.parent_scope;
}
} else { } else {
def = this.variables.get(symbol.name); def = this.variables.get(symbol.name);
def.orig.push(symbol); def.orig.push(symbol);
@@ -474,9 +425,7 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
}); });
// labels are always mangleable // labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){ AST_Label.DEFMETHOD("unmangleable", return_false);
return false;
});
AST_Symbol.DEFMETHOD("unreferenced", function(){ AST_Symbol.DEFMETHOD("unreferenced", function(){
return this.definition().references.length == 0 return this.definition().references.length == 0
@@ -487,13 +436,9 @@ AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared; return this.definition().undeclared;
}); });
AST_LabelRef.DEFMETHOD("undeclared", function(){ AST_LabelRef.DEFMETHOD("undeclared", return_false);
return false;
});
AST_Label.DEFMETHOD("undeclared", function(){ AST_Label.DEFMETHOD("undeclared", return_false);
return false;
});
AST_Symbol.DEFMETHOD("definition", function(){ AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef; return this.thedef;
@@ -504,7 +449,7 @@ AST_Symbol.DEFMETHOD("global", function(){
}); });
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, { options = defaults(options, {
eval : false, eval : false,
ie8 : false, ie8 : false,
keep_classnames: false, keep_classnames: false,
@@ -512,6 +457,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
reserved : [], reserved : [],
toplevel : false, toplevel : false,
}); });
if (!Array.isArray(options.reserved)) options.reserved = [];
return options;
}); });
AST_Toplevel.DEFMETHOD("mangle_names", function(options){ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
@@ -569,7 +516,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
}); });
this.walk(tw); this.walk(tw);
to_mangle.forEach(function(def){ to_mangle.forEach(function(def){
if (def.destructuring && !def.destructuring.is_array) return;
def.mangle(options); def.mangle(options);
}); });
@@ -643,6 +589,8 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
base54.consider("finally"); base54.consider("finally");
else if (node instanceof AST_Yield) else if (node instanceof AST_Yield)
base54.consider("yield"); base54.consider("yield");
else if (node instanceof AST_Await)
base54.consider("await");
else if (node instanceof AST_Symbol && node.unmangleable(options)) else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name); base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary) else if (node instanceof AST_Unary || node instanceof AST_Binary)

View File

@@ -199,6 +199,10 @@ TreeTransformer.prototype = new TreeWalker;
if (self.expression) self.expression = self.expression.transform(tw); if (self.expression) self.expression = self.expression.transform(tw);
}); });
_(AST_Await, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_Unary, function(self, tw){ _(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw); self.expression = self.expression.transform(tw);
}); });
@@ -240,6 +244,7 @@ TreeTransformer.prototype = new TreeWalker;
}); });
_(AST_Export, function(self, tw){ _(AST_Export, function(self, tw){
if (self.exported_definition) self.exported_definition = self.exported_definition.transform(tw);
if (self.exported_value) self.exported_value = self.exported_value.transform(tw); if (self.exported_value) self.exported_value = self.exported_value.transform(tw);
}); });

View File

@@ -43,13 +43,6 @@
"use strict"; "use strict";
function array_to_hash(a) {
var ret = Object.create(null);
for (var i = 0; i < a.length; ++i)
ret[a[i]] = true;
return ret;
};
function slice(a, start) { function slice(a, start) {
return Array.prototype.slice.call(a, start || 0); return Array.prototype.slice.call(a, start || 0);
}; };

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.9", "version": "3.0.17",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -40,5 +40,5 @@
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
}, },
"keywords": ["uglify", "uglify-js", "minify", "minifier"] "keywords": ["uglify", "uglify-js", "uglify-es", "minify", "minifier", "es5", "es6", "es2015"]
} }

View File

@@ -4,12 +4,13 @@
"use strict"; "use strict";
var createHash = require("crypto").createHash; var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork; var fork = require("child_process").fork;
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc");
} }
args.push("--stats"); args.push("--timings");
var urls = [ var urls = [
"https://code.jquery.com/jquery-3.2.1.js", "https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js", "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
@@ -29,12 +30,7 @@ function done() {
var info = results[url]; var info = results[url];
console.log(); console.log();
console.log(url); console.log(url);
var elapsed = 0; console.log(info.log);
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) {
elapsed += 1e-3 * parseInt(time);
return "";
}));
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log("Original:", info.input, "bytes"); console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes"); console.log("Uglified:", info.output, "bytes");
console.log("SHA1 sum:", info.sha1); console.log("SHA1 sum:", info.sha1);
@@ -57,7 +53,8 @@ urls.forEach(function(url) {
output: 0, output: 0,
log: "" log: ""
}; };
require(url.slice(0, url.indexOf(":"))).get(url, function(res) { fetch(url, function(err, res) {
if (err) throw err;
var uglifyjs = fork("bin/uglifyjs", args, { silent: true }); var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.on("data", function(data) { res.on("data", function(data) {
results[url].input += data.length; results[url].input += data.length;

View File

@@ -135,3 +135,70 @@ arrow_with_regexp: {
num => /\d{11,14}/.test( num ) num => /\d{11,14}/.test( num )
} }
} }
arrow_unused: {
options = {
toplevel: false,
side_effects: true,
unused: true,
}
input: {
top => dog;
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let unused = x => { console.log(x); };
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect: {
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6"
}
arrow_unused_toplevel: {
options = {
toplevel: true,
side_effects: true,
unused: true,
}
input: {
top => dog;
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let unused = x => { console.log(x); };
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect: {
let fn = a => { console.log(a * a); };
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6"
}

200
test/compress/async.js Normal file
View File

@@ -0,0 +1,200 @@
await_precedence: {
input: {
async function f1() { await x + y; }
async function f2() { await (x + y); }
}
expect_exact: "async function f1(){await x+y}async function f2(){await(x+y)}"
}
async_function_declaration: {
options = {
side_effects: true,
unused: true,
}
input: {
async function f0() {}
async function f1() { await x + y; }
async function f2() { await (x + y); }
async function f3() { await x + await y; }
async function f4() { await (x + await y); }
async function f5() { await x; await y; }
async function f6() { await x, await y; }
}
expect: {
async function f0() {}
async function f1() { await x, y; }
async function f2() { await (x + y); }
async function f3() { await x, await y; }
async function f4() { await (x + await y); }
async function f5() { await x; await y; }
async function f6() { await x, await y; }
}
}
async_function_expression: {
options = {
evaluate: true,
side_effects: true,
unused: true,
}
input: {
var named = async function foo() {
await bar(1 + 0) + (2 + 0);
}
var anon = async function() {
await (1 + 0) + bar(2 + 0);
}
}
expect: {
var named = async function() {
await bar(1);
};
var anon = async function() {
await 1, bar(2);
};
}
}
async_class: {
options = {
evaluate: true,
}
input: {
class Foo {
async m1() {
return await foo(1 + 2);
}
static async m2() {
return await foo(3 + 4);
}
}
}
expect: {
class Foo {
async m1() {
return await foo(3);
}
static async m2() {
return await foo(7);
}
}
}
}
async_object_literal: {
options = {
evaluate: true,
}
input: {
var obj = {
async a() {
await foo(1 + 0);
},
anon: async function(){
await foo(2 + 0);
}
};
}
expect: {
var obj = {
async a() {
await foo(1);
},
anon: async function() {
await foo(2);
}
};
}
}
async_export: {
input: {
export async function run() {};
export default async function def() {};
}
expect: {
export async function run() {};
export default async function def() {};
}
}
async_inline: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(async function(){ return await 3; })();
(async function(x){ await console.log(x); })(4);
function echo(x) { return x; }
echo( async function(){ return await 1; }() );
echo( async function(x){ await console.log(x); }(2) );
function top() { console.log("top"); }
top();
async function async_top() { console.log("async_top"); }
async_top();
}
expect: {
!async function(){await 3}();
!async function(x){await console.log(4)}();
function echo(x){return x}
echo(async function(){return await 1}());
echo(async function(x){await console.log(2)}());
console.log("top");
!async function(){console.log("async_top")}();
}
expect_stdout: [
"4",
"2",
"top",
"async_top",
]
node_version: ">=8"
}
async_identifiers: {
input: {
let async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect: {
let async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect_stdout: [
"async 1",
"await 2",
]
node_version: ">=8"
}
/* FIXME: add test when supported by parser
async_arrow: {
input: {
let a1 = async x => await foo(x);
let a2 = async () => await bar();
let a3 = async (x) => await baz(x);
let a4 = async (x, y) => { await far(x, y); }
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); }
}
expect: {
}
}
*/

View File

@@ -47,3 +47,142 @@ keep_some_blocks: {
} else stuff(); } else stuff();
} }
} }
issue_1664: {
input: {
var a = 1;
function f() {
if (undefined) a = 2;
{
function undefined() {}
undefined();
}
}
f();
console.log(a);
}
expect: {
var a = 1;
function f() {
if (undefined) a = 2;
{
function undefined() {}
undefined();
}
}
f();
console.log(a);
}
expect_stdout: "1"
node_version: ">=6"
}
issue_1672_for: {
input: {
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect: {
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_for_strict: {
input: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_if: {
input: {
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect: {
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) function xxx() {}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_if_strict: {
input: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}

View File

@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
function f3(x) { function f3(x) {
var b = x.prop; var b = x.prop;
sideeffect1(); sideeffect1();
return b + -9; return b + (function() { return -9; })();
} }
} }
} }

View File

@@ -31,7 +31,7 @@ dead_code_2_should_warn: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw "foo"; throw new Error("foo");
// completely discarding the `if` would introduce some // completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2 // bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope. // we copy any declarations to the upper scope.
@@ -46,16 +46,60 @@ dead_code_2_should_warn: {
})(); })();
} }
} }
f();
} }
expect: { expect: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw "foo"; throw new Error("foo");
var x; var x;
function g(){}; var g;
} }
f();
} }
expect_stdout: true
node_version: ">=6"
}
dead_code_2_should_warn_strict: {
options = {
dead_code: true
};
input: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
f();
}
expect: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
}
expect_stdout: true
node_version: ">=4"
} }
dead_code_constant_boolean_should_warn_more: { dead_code_constant_boolean_should_warn_more: {
@@ -78,16 +122,55 @@ dead_code_constant_boolean_should_warn_more: {
foo(); foo();
var moo; var moo;
} }
bar();
} }
expect: { expect: {
var foo; var foo;
function bar() {} var bar;
// nothing for the while // nothing for the while
// as for the for, it should keep: // as for the for, it should keep:
var x = 10, y; var x = 10, y;
var moo; var moo;
bar();
} }
expect_stdout: true expect_stdout: true
node_version: ">=6"
}
dead_code_constant_boolean_should_warn_more_strict: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
side_effects : true,
};
input: {
"use strict";
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var foo;
function bar() {}
}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
bar();
}
expect: {
"use strict";
var foo;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
bar();
}
expect_stdout: true
node_version: ">=4"
} }
dead_code_block_decls_die: { dead_code_block_decls_die: {
@@ -134,7 +217,7 @@ dead_code_const_declaration: {
var unused; var unused;
const CONST_FOO = !1; const CONST_FOO = !1;
var moo; var moo;
function bar() {} var bar;
} }
expect_stdout: true expect_stdout: true
} }
@@ -162,7 +245,7 @@ dead_code_const_annotation: {
var unused; var unused;
var CONST_FOO_ANN = !1; var CONST_FOO_ANN = !1;
var moo; var moo;
function bar() {} var bar;
} }
expect_stdout: true expect_stdout: true
} }
@@ -229,7 +312,7 @@ dead_code_const_annotation_complex_scope: {
var CONST_FOO_ANN = !1; var CONST_FOO_ANN = !1;
var unused_var_2; var unused_var_2;
var moo; var moo;
function bar() {} var bar;
var beef = 'good'; var beef = 'good';
var meat = 'beef'; var meat = 'beef';
var pork = 'bad'; var pork = 'bad';

View File

@@ -328,5 +328,314 @@ issue_1886: {
let [a] = [1]; let [a] = [1];
console.log(a); console.log(a);
} }
expect_exact: "1" }
destructuring_decl_of_numeric_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let { 3: x } = { [1 + 2]: 42 };
console.log(x);
}
expect: {
let { 3: x } = { [3]: 42 };
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
destructuring_decl_of_computed_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let four = 4;
let { [7 - four]: x } = { [1 + 2]: 42 };
console.log(x);
}
expect: {
let four = 4;
let { [7 - four]: x } = { [3]: 42 };
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
destructuring_assign_of_numeric_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let x;
({ 3: x } = { [1 + 2]: 42 });
console.log(x);
}
expect: {
let x;
({ 3: x } = { [3]: 42 });
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
destructuring_assign_of_computed_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let x;
let four = 4;
({ [(5 + 2) - four]: x } = { [1 + 2]: 42 });
console.log(x);
}
expect: {
let x;
let four = 4;
({ [7 - four]: x } = { [3]: 42 });
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
mangle_destructuring_decl: {
options = {
evaluate: true,
unused: true,
}
mangle = {
}
input: {
function test(opts) {
let a = opts.a || { e: 7, n: 8 };
let { t, e, n, s = 5 + 4, o, r } = a;
console.log(t, e, n, s, o, r);
}
test({a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 }});
test({});
}
expect: {
function test(t) {
let e = t.a || { e: 7, n: 8 };
let {t: n, e: o, n: s, s: a = 9, o: c, r: l} = e;
console.log(n, o, s, a, c, l);
}
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
test({});
}
expect_stdout: [
"1 2 3 4 5 6",
"undefined 7 8 9 undefined undefined",
]
node_version: ">=6"
}
mangle_destructuring_assign_toplevel_true: {
options = {
toplevel: true,
evaluate: true,
unused: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 6
}
input: {
function test(opts) {
let s, o, r;
let a = opts.a || { e: 7, n: 8 };
({ t, e, n, s = 5 + 4, o, r } = a);
console.log(t, e, n, s, o, r);
}
let t, e, n;
test({a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 }});
test({});
}
expect: {
function n(n) {
let t, a, c;
let l = n.a || { e: 7, n: 8 };
({t: o, e, n: s, s: t = 9, o: a, r: c} = l);
console.log(o, e, s, t, a, c);
}
let o, e, s;
n({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
n({});
}
expect_stdout: [
"1 2 3 4 5 6",
"undefined 7 8 9 undefined undefined",
]
node_version: ">=6"
}
mangle_destructuring_assign_toplevel_false: {
options = {
toplevel: false,
evaluate: true,
unused: true,
}
mangle = {
toplevel: false,
}
beautify = {
ecma: 6
}
input: {
function test(opts) {
let s, o, r;
let a = opts.a || { e: 7, n: 8 };
({ t, e, n, s = 9, o, r } = a);
console.log(t, e, n, s, o, r);
}
let t, e, n;
test({a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 }});
test({});
}
expect: {
function test(o) {
let s, a, c;
let l = o.a || { e: 7, n: 8 };
({t, e, n, s = 9, o: a, r: c} = l);
console.log(t, e, n, s, a, c);
}
let t, e, n;
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
test({});
}
expect_stdout: [
"1 2 3 4 5 6",
"undefined 7 8 9 undefined undefined",
]
node_version: ">=6"
}
mangle_destructuring_decl_array: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 6
}
input: {
var [, t, e, n, s, o = 2, r = [ 1 + 2 ]] = [ 9, 8, 7, 6 ];
console.log(t, e, n, s, o, r);
}
expect: {
var [, o, l, a, c, e = 2, g = [ 3 ]] = [ 9, 8, 7, 6 ];
console.log(o, l, a, c, e, g);
}
expect_stdout: "8 7 6 undefined 2 [ 3 ]"
node_version: ">=6"
}
anon_func_with_destructuring_args: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 5,
}
input: {
(function({foo = 1 + 0, bar = 2}, [car = 3, far = 4]) {
console.log(foo, bar, car, far);
})({bar: 5 - 0}, [, 6]);
}
expect: {
(function({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) {
console.log(o, n, a, b);
})({bar: 5}, [, 6]);
}
expect_stdout: "1 5 3 6"
node_version: ">=6"
}
arrow_func_with_destructuring_args: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 5,
}
input: {
(({foo = 1 + 0, bar = 2}, [car = 3, far = 4]) => {
console.log(foo, bar, car, far);
})({bar: 5 - 0}, [, 6]);
}
expect: {
(({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) => {
console.log(o, n, a, b);
})({bar: 5}, [, 6]);
}
expect_stdout: "1 5 3 6"
node_version: ">=6"
}
issue_2044_ecma_5: {
beautify = {
beautify: false,
ecma: 5,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x:a=1,y:y=2+b,z:z=3-c}=obj);"
}
issue_2044_ecma_6: {
beautify = {
beautify: false,
ecma: 6,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x:a=1,y=2+b,z=3-c}=obj);"
}
issue_2044_ecma_5_beautify: {
beautify = {
beautify: true,
ecma: 5,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x: a = 1, y: y = 2 + b, z: z = 3 - c} = obj);"
}
issue_2044_ecma_6_beautify: {
beautify = {
beautify: true,
ecma: 6,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x: a = 1, y = 2 + b, z = 3 - c} = obj);"
} }

View File

@@ -863,12 +863,12 @@ issue_1583: {
expect: { expect: {
function m(t) { function m(t) {
(function(e) { (function(e) {
t = (function() { t = e();
return (function(a) { })(function() {
return a; return (function(a) {
})(function(a) {}); return a;
})(); })(function(a) {});
})(); });
} }
} }
} }
@@ -1280,3 +1280,17 @@ issue_1968: {
expect_stdout: "5" expect_stdout: "5"
node_version: ">=6" node_version: ">=6"
} }
issue_2063: {
options = {
unused: true,
}
input: {
var a;
var a;
}
expect: {
var a;
var a;
}
}

View File

@@ -738,6 +738,7 @@ unsafe_prototype_function: {
call_args: { call_args: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
reduce_vars: true, reduce_vars: true,
} }
input: { input: {
@@ -758,6 +759,7 @@ call_args: {
call_args_drop_param: { call_args_drop_param: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
@@ -873,13 +875,15 @@ unsafe_charAt_noop: {
input: { input: {
console.log( console.log(
s.charAt(0), s.charAt(0),
"string".charAt(x) "string".charAt(x),
(typeof x).charAt()
); );
} }
expect: { expect: {
console.log( console.log(
s.charAt(0), s.charAt(0),
"string".charAt(x) "string".charAt(x),
(typeof x)[0]
); );
} }
} }
@@ -1130,3 +1134,31 @@ issue_1964_2: {
} }
expect_stdout: "b" expect_stdout: "b"
} }
array_slice_index: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([1,2,3].slice(1)[1]);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}
string_charCodeAt: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("foo".charCodeAt("bar".length));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}

View File

@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
join_vars : true, join_vars : true,
reduce_vars : true, reduce_vars : true,
cascade : true, cascade : true,
inline : true,
} }
input: { input: {
(function(){ return -1.23; }()); (function(){ return -1.23; }());
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
join_vars : true, join_vars : true,
reduce_vars : true, reduce_vars : true,
cascade : true, cascade : true,
inline : true,
} }
input: { input: {
(function(){ return -1.23; }()); (function(){ return -1.23; }());
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
inline: true,
} }
input: { input: {
(function(a) { (function(a) {
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
evaluate: true, evaluate: true,
cascade: true, cascade: true,
unused: true, unused: true,
inline: true,
} }
input: { input: {
function greeter() { function greeter() {
@@ -167,3 +171,246 @@ function_returning_constant_literal: {
} }
expect_stdout: "Hello there" expect_stdout: "Hello there"
} }
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'function' 'function'",
]
node_version: ">=6"
}
hoist_funs_strict: {
options = {
hoist_funs: true,
}
input: {
"use strict";
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
"use strict";
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'undefined' 'function'",
]
node_version: ">=4"
}
issue_203: {
options = {
keep_fargs: false,
side_effects: true,
unsafe_Func: true,
unused: true,
}
input: {
var m = {};
var fn = Function("require", "module", "exports", "module.exports = 42;");
fn(null, m, m.exports);
console.log(m.exports);
}
expect: {
var m = {};
var fn = Function("a", "b", "b.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
expect_stdout: "42"
}
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
!function(c) {
c = 1 + c;
var c = 0;
function f14(a_1) {
if (c = 1 + c, 0 !== 23..toString())
c = 1 + c, a_1 && (a_1[0] = 0);
}
f14();
}(-1);
}();
console.log(c);
}
expect: {
var c = 0;
!function(c) {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(-1),
console.log(c);
}
expect_stdout: "0"
}
issue_2097: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}
f(1);
}
expect: {
!function() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}(1);
}
expect_stdout: "1"
}
issue_2101: {
options = {
inline: true,
}
input: {
a = {};
console.log(function() {
return function() {
return this.a;
}();
}() === function() {
return a;
}());
}
expect: {
a = {};
console.log(function() {
return this.a;
}() === a);
}
expect_stdout: "true"
}
inner_ref: {
options = {
inline: true,
unused: true,
}
input: {
console.log(function(a) {
return function() {
return a;
}();
}(1), function(a) {
return function(a) {
return a;
}();
}(2));
}
expect: {
console.log(function(a) {
return a;
}(1), function(a) {
return a;
}());
}
expect_stdout: "1 undefined"
}

View File

@@ -160,3 +160,17 @@ issue_1801: {
console.log(!0); console.log(!0);
} }
} }
issue_1986: {
options = {
global_defs: {
"@alert": "console.log",
},
}
input: {
alert(42);
}
expect: {
console.log(42);
}
}

View File

@@ -583,3 +583,84 @@ class_extends_regex: {
} }
expect_exact: "function f(){class rx1 extends(/rx/){}}" expect_exact: "function f(){class rx1 extends(/rx/){}}"
} }
issue_2028: {
options = {
side_effects: true,
}
input: {
var a = {};
(function(x) {
x.X = function() {
return X;
};
class X {
static hello() {
console.log("hello");
}
}
}(a));
a.X().hello();
}
expect: {
var a = {};
(function(x) {
x.X = function() {
return X;
};
class X {
static hello() {
console.log("hello");
}
}
}(a));
a.X().hello();
}
expect_stdout: "hello"
node_version: ">=6"
}
class_expression_statement: {
options = {
toplevel: false,
side_effects: false,
unused: false,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: "(class{});(class NamedClassExpr{});let expr=class AnotherClassExpr{};class C{}"
}
class_expression_statement_unused: {
options = {
toplevel: false,
side_effects: true,
unused: true,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: "let expr=class{};class C{}"
}
class_expression_statement_unused_toplevel: {
options = {
toplevel: true,
side_effects: true,
unused: true,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: ""
}

View File

@@ -302,3 +302,85 @@ issue_1437_conditionals: {
} }
} }
} }
issue_512: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
}
input: {
function a() {
if (b()) {
c();
return;
}
throw e;
}
}
expect: {
function a() {
if (!b()) throw e;
c();
}
}
}
issue_1317: {
options = {
if_return: true,
}
input: {
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect: {
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect_stdout: "1"
node_version: ">=6"
}
issue_1317_strict: {
options = {
if_return: true,
}
input: {
"use strict";
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect: {
"use strict";
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect_stdout: "1"
node_version: ">=4"
}

View File

@@ -116,3 +116,137 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
] ]
} }
non_hoisted_function_after_return_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
console.log(foo(0), foo(1));
}
expect_stdout: "8 7"
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]"
]
}
non_hoisted_function_after_return_2a_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2, warnings: "verbose"
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) {
return 7 - x;
}
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]",
]
}
non_hoisted_function_after_return_2b_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
]
}

View File

@@ -1,90 +1,91 @@
multiple_functions: { multiple_functions: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
function g() {} function g() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
function g() {}
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ); if ( window );
function f() {}
function g() {}
} )(); } )();
} }
} }
single_function: { single_function: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
if ( window ); if ( window );
function f() {}
} )(); } )();
} }
} }
deeply_nested: { deeply_nested: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
function g() {} function g() {}
if ( !document ) { if ( !document ) {
return; return;
} }
function h() {} function h() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
function g() {}
function h() {}
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ) if ( window )
if (document); if (document);
function f() {}
function g() {}
function h() {}
} )(); } )();
} }
} }
not_hoisted_when_already_nested: { not_hoisted_when_already_nested: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
if ( foo ) function f() {} if ( foo ) function f() {}
} )(); } )();
} }
expect: { expect: {
@@ -94,3 +95,69 @@ not_hoisted_when_already_nested: {
} )(); } )();
} }
} }
defun_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}
defun_hoist_funs: {
options = {
hoist_funs: true,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
function h() {}
if (window) function g() {}
}
}
}
defun_else_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (window) function g() {}
else return;
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}

View File

@@ -1,6 +1,7 @@
unary_prefix: { unary_prefix: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }

281
test/compress/issue-2001.js Normal file
View File

@@ -0,0 +1,281 @@
export_func_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export function f(){};
}
expect_exact: "export function f(){};"
}
export_func_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export function f(){}(1);
}
expect_exact: "export function f(){};1;"
}
export_func_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export function f(){}(1);
}
expect_exact: "export function f(){};"
}
export_default_func_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){};
}
expect_exact: "export default function(){};"
}
export_default_func_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export default function f(){}(1);
}
expect_exact: "export default function(){};1;"
}
export_default_func_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){}(1);
}
expect_exact: "export default function(){};"
}
export_class_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export class C {};
}
expect_exact: "export class C{};"
}
export_class_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export class C {}(1);
}
expect_exact: "export class C{};1;"
}
export_class_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export class C {}(1);
}
expect_exact: "export class C{};"
}
export_default_class_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default class C {};
}
expect_exact: "export default class{};"
}
export_default_class_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export default class C {}(1);
}
expect_exact: "export default class{};1;"
}
export_default_class_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export default class C {}(1);
}
expect_exact: "export default class{};"
}
export_mangle_1: {
mangle = {
toplevel: true,
}
input: {
export function foo(one, two) {
return one - two;
};
}
expect_exact: "export function foo(n,o){return n-o};"
}
export_mangle_2: {
mangle = {
toplevel: true,
}
input: {
export default function foo(one, two) {
return one - two;
};
}
expect_exact: "export default function n(n,r){return n-r};"
}
export_mangle_3: {
options = {
collapse_vars: true,
}
mangle = {
toplevel: true,
}
input: {
export class C {
go(one, two) {
var z = one;
return one - two + z;
}
};
}
expect_exact: "export class C{go(n,r){return n-r+n}};"
}
export_mangle_4: {
options = {
collapse_vars: true,
}
mangle = {
toplevel: true,
}
input: {
export default class C {
go(one, two) {
var z = one;
return one - two + z;
}
};
}
expect_exact: "export default class n{go(n,r){return n-r+n}};"
}
export_mangle_5: {
mangle = {
toplevel: true,
}
input: {
export default {
prop: function(one, two) {
return one - two;
}
};
}
expect_exact: "export default{prop:function(n,r){return n-r}};"
}
export_mangle_6: {
mangle = {
toplevel: true,
}
input: {
var baz = 2;
export let foo = 1, bar = baz;
}
expect_exact: "var a=2;export let foo=1,bar=a;"
}
export_toplevel_1: {
options = {
toplevel: true,
unused: true,
}
input: {
function f(){}
export function g(){};
export default function h(){};
}
expect: {
export function g(){};
export default function(){};
}
}
export_toplevel_2: {
options = {
toplevel: true,
unused: true,
}
input: {
class A {}
export class B {};
export default class C {};
}
expect: {
export class B {};
export default class {};
}
}
export_default_func_ref: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){};
f();
}
expect_exact: "export default function f(){};f();"
}

View File

@@ -1,7 +1,8 @@
compress_new_function: { compress_new_function: {
options = { options = {
unsafe: true unsafe: true,
unsafe_Func: true,
} }
input: { input: {
new Function("aa, bb", 'return aa;'); new Function("aa, bb", 'return aa;');
@@ -14,6 +15,7 @@ compress_new_function: {
compress_new_function_with_destruct: { compress_new_function_with_destruct: {
options = { options = {
unsafe: true, unsafe: true,
unsafe_Func: true,
ecma: 6 ecma: 6
} }
beautify = { beautify = {
@@ -26,9 +28,7 @@ compress_new_function_with_destruct: {
} }
expect: { expect: {
Function("a", "[b]", "return a"); Function("a", "[b]", "return a");
Function("a", "{bb}", "return a"); Function("a", "{bb:b}", "return a");
Function("[[a]]", "[{bb}]", 'return a'); Function("[[a]]", "[{bb:b}]", 'return a');
} }
} }

483
test/compress/issue-281.js Normal file
View File

@@ -0,0 +1,483 @@
collapse_vars_constants: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f1(x) {
var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
return b + (function() { return d - a * e - c; })();
}
function f2(x) {
var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
return b + (function() { return -a * e - c; })();
}
}
expect: {
function f1(x) {
var b = x.prop, d = sideeffect1(), e = sideeffect2();
return b + (d - 4 * e - 5);
}
function f2(x) {
var b = x.prop;
sideeffect1();
return b + (-4 * sideeffect2() - 5);
}
}
}
modified: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f5(1));
}
expect: {
function f5(b) {
var a = b;
return b++ + a;
}
console.log(f5(1));
}
expect_stdout: "2"
}
ref_scope: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
console.log(function() {
var a = 1, b = 2, c = 3;
var a = c++, b = b /= a;
return function() {
return a;
}() + b;
}());
}
expect: {
console.log(function() {
var a = 1, b = 2, c = 3;
b = b /= a = c++;
return a + b;
}());
}
expect_stdout: true
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
inline: true,
unsafe: false,
unused: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}(1)());
}
expect: {
var a, c;
console.log(a ? b : c ? d : void 0);
}
expect_stdout: true
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
t ? console.log(true) : console.log(false), void console.log("something");
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(void console.log("test"))()()();'
}
negate_iife_issue_1073: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
sequences: true,
unused: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new function() {
this.x = 7,
console.log(this);
}();
}
expect_stdout: true
}
issue_1288_side_effects: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
unused: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || (x = {});
y;
}
}
inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
for (b in (function() {
return x(a, b, c);
})()) {
var c = 3, d = 4;
x(a, b, c, d);
}
x(a, b, c, d);
}
}
expect: {
function f() {
var a = 1, b = 2;
for (b in x(1, b, c)) {
var c = 3, d = 4;
x(1, b, c, d);
}
x(1, b, c, d);
}
}
}
issue_1595_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
g(3);
}
}
issue_1758: {
options = {
inline: true,
sequences: true,
side_effects: true,
}
input: {
console.log(function(c) {
var undefined = 42;
return function() {
c--;
c--, c.toString();
return;
}();
}());
}
expect: {
console.log(function(c) {
var undefined = 42;
return c--, c--, void c.toString();
}());
}
expect_stdout: "undefined"
}
wrap_iife: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
}
wrap_iife_in_expression: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=bar();'
}
wrap_iife_in_return_call: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(void console.log("test"))();'
}
pure_annotation: {
options = {
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function() {
console.log("hello");
}());
}
expect_exact: ""
}
drop_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
!function() {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}
keep_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: true,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
!function(a_1) {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}

View File

@@ -22,7 +22,8 @@ negate_iife_1_off: {
negate_iife_2: { negate_iife_2: {
options = { options = {
negate_iife: true inline: true,
negate_iife: true,
}; };
input: { input: {
(function(){ return {} })().x = 10; (function(){ return {} })().x = 10;
@@ -32,6 +33,7 @@ negate_iife_2: {
negate_iife_2_side_effects: { negate_iife_2_side_effects: {
options = { options = {
inline: true,
negate_iife: true, negate_iife: true,
side_effects: true, side_effects: true,
} }
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
options = { options = {
conditionals: true, conditionals: true,
evaluate: true, evaluate: true,
inline: true,
negate_iife: true, negate_iife: true,
} }
input: { input: {
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
options = { options = {
conditionals: true, conditionals: true,
evaluate: true, evaluate: true,
inline: true,
negate_iife: false, negate_iife: false,
} }
input: { input: {

View File

@@ -1,4 +1,4 @@
eval_let: { eval_let_6: {
input: { input: {
eval("let a;"); eval("let a;");
console.log(); console.log();
@@ -10,3 +10,29 @@ eval_let: {
expect_stdout: "" expect_stdout: ""
node_version: ">=6" node_version: ">=6"
} }
eval_let_4: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode")
node_version: "4"
}
eval_let_0: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Unexpected identifier")
node_version: "<=0.12"
}

View File

@@ -557,3 +557,105 @@ native_prototype: {
"".indexOf.call(e, "bar"); "".indexOf.call(e, "bar");
} }
} }
accessor_boolean: {
input: {
var a = 1;
var b = {
get true() {
return a;
},
set false(c) {
a = c;
}
};
console.log(b.true, b.false = 2, b.true);
}
expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);'
expect_stdout: "1 2 2"
}
accessor_get_set: {
input: {
var a = 1;
var b = {
get set() {
return a;
},
set get(c) {
a = c;
}
};
console.log(b.set, b.get = 2, b.set);
}
expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);'
expect_stdout: "1 2 2"
}
accessor_null_undefined: {
input: {
var a = 1;
var b = {
get null() {
return a;
},
set undefined(c) {
a = c;
}
};
console.log(b.null, b.undefined = 2, b.null);
}
expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);'
expect_stdout: "1 2 2"
}
accessor_number: {
input: {
var a = 1;
var b = {
get 42() {
return a;
},
set 42(c) {
a = c;
}
};
console.log(b[42], b[42] = 2, b[42]);
}
expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);'
expect_stdout: "1 2 2"
}
accessor_string: {
input: {
var a = 1;
var b = {
get "a-b"() {
return a;
},
set "a-b"(c) {
a = c;
}
};
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
}
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
expect_stdout: "1 2 2"
}
accessor_this: {
input: {
var a = 1;
var b = {
get this() {
return a;
},
set this(c) {
a = c;
}
};
console.log(b.this, b.this = 2, b.this);
}
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
expect_stdout: "1 2 2"
}

View File

@@ -2,6 +2,7 @@ reduce_vars: {
options = { options = {
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
inline : true,
global_defs : { global_defs : {
C : 0 C : 0
}, },
@@ -1032,6 +1033,7 @@ defun_inline_2: {
defun_inline_3: { defun_inline_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
passes: 2, passes: 2,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1054,6 +1056,7 @@ defun_inline_3: {
defun_call: { defun_call: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1080,6 +1083,7 @@ defun_call: {
defun_redefine: { defun_redefine: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1112,6 +1116,7 @@ defun_redefine: {
func_inline: { func_inline: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1138,6 +1143,7 @@ func_inline: {
func_modified: { func_modified: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1311,19 +1317,47 @@ iife_func_side_effects: {
unused: true, unused: true,
} }
input: { input: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) { (function(a, b, c) {
return b(); function y() {
console.log("FAIL");
}
return y + b();
})(x(), function() { })(x(), function() {
return y(); return y();
}, z()); }, z());
} }
expect: { expect: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) { (function(a, b, c) {
return function() { return function() {
return y(); console.log("FAIL");
}(); } + b();
})(x(), 0, z()); })(x(), function() {
return y();
}, z());
} }
expect_stdout: [
"x",
"z",
"y",
]
} }
issue_1595_1: { issue_1595_1: {
@@ -1687,6 +1721,7 @@ redefine_arguments_1: {
redefine_arguments_2: { redefine_arguments_2: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1723,6 +1758,7 @@ redefine_arguments_2: {
redefine_arguments_3: { redefine_arguments_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
passes: 3, passes: 3,
reduce_vars: true, reduce_vars: true,
@@ -1799,6 +1835,7 @@ redefine_farg_1: {
redefine_farg_2: { redefine_farg_2: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1835,6 +1872,7 @@ redefine_farg_2: {
redefine_farg_3: { redefine_farg_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
passes: 3, passes: 3,
reduce_vars: true, reduce_vars: true,
@@ -2537,3 +2575,53 @@ accessor: {
} }
expect_stdout: "1 1" expect_stdout: "1 1"
} }
issue_2090_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
var x = 1;
[].forEach(() => x = 2);
return x;
}());
}
expect: {
console.log(function() {
var x = 1;
[].forEach(() => x = 2);
return x;
}());
}
expect_stdout: "1"
node_version: ">=4"
}
issue_2090_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
var x = 1;
[].forEach(() => {
x = 2;
});
return x;
}());
}
expect: {
console.log(function() {
var x = 1;
[].forEach(() => {
x = 2;
});
return x;
}());
}
expect_stdout: "1"
node_version: ">=4"
}

14
test/compress/sandbox.js Normal file
View File

@@ -0,0 +1,14 @@
console_log: {
input: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect_stdout: [
"%% %s",
"% %s",
]
}

View File

@@ -734,3 +734,23 @@ reassign_const: {
} }
expect_stdout: true expect_stdout: true
} }
issue_2062: {
options = {
booleans: true,
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
var a = 1;
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
console.log(a);
}
expect: {
var a = 1;
a || (a++, a--), a++, --a && a.var;
console.log(a);
}
expect_stdout: "1"
}

View File

@@ -190,3 +190,12 @@ yield_sub: {
} }
expect_exact: 'function*foo(){yield x["foo"];(yield x)["foo"];yield(yield obj.foo())["bar"]()}' expect_exact: 'function*foo(){yield x["foo"];(yield x)["foo"];yield(yield obj.foo())["bar"]()}'
} }
yield_as_ES5_property: {
input: {
"use strict";
console.log({yield: 42}.yield);
}
expect_exact: '"use strict";console.log({yield:42}.yield);'
expect_stdout: "42"
}

31
test/fetch.js Normal file
View File

@@ -0,0 +1,31 @@
var fs = require("fs");
var path = require("path");
try {
fs.mkdirSync("./tmp");
} catch (e) {
if (e.code != "EEXIST") throw e;
}
function local(url) {
return path.join("./tmp", encodeURIComponent(url));
}
function read(url) {
return fs.createReadStream(local(url));
}
module.exports = function(url, callback) {
var result = read(url);
result.on("error", function(e) {
if (e.code != "ENOENT") return callback(e);
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
if (res.statusCode !== 200) return callback(res);
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
callback(null, read(url));
}));
});
}).on("open", function() {
callback(null, result);
});
};

View File

@@ -0,0 +1 @@
console.log(x);

View File

@@ -0,0 +1 @@
{"version": 3,"sources": ["index.js"],"mappings": ";"}

View File

@@ -0,0 +1,5 @@
function test(callback) {
'aaaaaaaaaaaaaaaa';
callback(err, data);
callback(err, data);
}

View File

@@ -0,0 +1,5 @@
function test(a){
"aaaaaaaaaaaaaaaa"
;a(err,data),a(err,data)
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9

View File

@@ -1,2 +1,2 @@
new function(){console.log(3)}; new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxHQUFyQyxZQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19

View File

@@ -3,7 +3,7 @@
"use strict"; "use strict";
var site = "http://browserbench.org/JetStream/"; var site = "http://browserbench.org/JetStream";
if (typeof phantom == "undefined") { if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit() // workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){ [process.stdout, process.stderr].forEach(function(stream){
@@ -11,45 +11,69 @@ if (typeof phantom == "undefined") {
stream._handle.setBlocking(true); stream._handle.setBlocking(true);
}); });
var args = process.argv.slice(2); var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {
args.splice(debug, 1);
debug = true;
} else {
debug = false;
}
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mcb", "beautify=false,webkit");
} }
args.push("--stats"); args.push("--timings");
var child_process = require("child_process"); var child_process = require("child_process");
try { var fetch = require("./fetch");
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var http = require("http"); var http = require("http");
var server = http.createServer(function(request, response) { var server = http.createServer(function(request, response) {
request.resume(); request.resume();
var url = decodeURIComponent(request.url.slice(1)); var url = site + request.url;
var stderr = ""; fetch(url, function(err, res) {
var uglifyjs = child_process.fork("bin/uglifyjs", args, { if (err) throw err;
silent: true response.writeHead(200, {
}).on("exit", function(code) { "Content-Type": {
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" ")); css: "text/css",
console.log(stderr); js: "application/javascript",
if (code) throw new Error("uglifyjs failed with code " + code); png: "image/png"
}); }[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
uglifyjs.stderr.on("data", function(data) { });
stderr += data; if (/\.js$/.test(url)) {
}).setEncoding("utf8"); var stderr = "";
uglifyjs.stdout.pipe(response); var uglifyjs = child_process.fork("bin/uglifyjs", args, {
http.get(url, function(res) { silent: true
res.pipe(uglifyjs.stdin); }).on("exit", function(code) {
}); console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
}).listen().on("listening", function() { console.log(stderr);
var phantomjs = require("phantomjs-prebuilt"); if (code) throw new Error("uglifyjs failed with code " + code);
var program = phantomjs.exec(process.argv[1], server.address().port); });
program.stdout.pipe(process.stdout); uglifyjs.stderr.on("data", function(data) {
program.stderr.pipe(process.stderr); stderr += data;
program.on("exit", function(code) { }).setEncoding("utf8");
server.close(); uglifyjs.stdout.pipe(response);
if (code) throw new Error("JetStream failed!"); res.pipe(uglifyjs.stdin);
console.log("JetStream completed successfully."); } else {
res.pipe(response);
}
}); });
}).listen();
server.on("listening", function() {
var port = server.address().port;
if (debug) {
console.log("http://localhost:" + port + "/");
} else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
if (error) throw error;
var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
process.exit(0);
});
});
}
}); });
server.timeout = 0; server.timeout = 0;
} else { } else {
@@ -63,10 +87,6 @@ if (typeof phantom == "undefined") {
phantom.exit(1); phantom.exit(1);
}; };
var url = "http://localhost:" + require("system").args[1] + "/"; var url = "http://localhost:" + require("system").args[1] + "/";
page.onResourceRequested = function(requestData, networkRequest) {
if (/\.js$/.test(requestData.url))
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
}
page.onConsoleMessage = function(msg) { page.onConsoleMessage = function(msg) {
if (/Error:/i.test(msg)) { if (/Error:/i.test(msg)) {
console.error(msg); console.error(msg);
@@ -77,8 +97,8 @@ if (typeof phantom == "undefined") {
phantom.exit(); phantom.exit();
} }
}; };
page.open(site, function(status) { page.open(url, function(status) {
if (status != "success") phantomjs.exit(1); if (status != "success") phantom.exit(1);
page.evaluate(function() { page.evaluate(function() {
JetStream.switchToQuick(); JetStream.switchToQuick();
JetStream.start(); JetStream.start();

View File

@@ -9,7 +9,7 @@ function read(path) {
describe("bin/uglifyjs", function () { describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) { it("should produce a functional build when using --self", function (done) {
this.timeout(15000); this.timeout(30000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS'; var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
@@ -19,7 +19,9 @@ describe("bin/uglifyjs", function () {
eval(stdout); eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object'); assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(WrappedUglifyJS.minify("foo([true,,2+3]);").code, "foo([!0,,5]);"); var result = WrappedUglifyJS.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done(); done();
}); });
@@ -61,7 +63,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n"); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n");
done(); done();
}); });
}); });
@@ -75,6 +77,23 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("should not consider source map file content as source map file name (issue #2082)", function (done) {
var command = [
uglifyjscmd,
"test/input/issue-2082/sample.js",
"--source-map", "content=test/input/issue-2082/sample.js.map",
"--source-map", "url=inline",
].join(" ");
exec(command, function (err, stdout, stderr) {
if (err) throw err;
var stderrLines = stderr.split('\n');
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map');
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
done();
});
});
it("Should work with --keep-fnames (mangle only)", function (done) { it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m'; var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
@@ -173,7 +192,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();", "var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"", "",
].join("\n")); ].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n"); assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -527,7 +546,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
'"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));', '"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiWUFBQSxJQUFJQSxLQUFNLFFBQU5BLEtBQU1DLEdBQUEsTUFBSyxPQUFTQSxFQUN4QkMsU0FBUUMsSUFBSUgsSUFBSSJ9", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiYUFBQSxJQUFJQSxJQUFNLFNBQU5BLElBQU1DLEdBQUEsTUFBSyxPQUFTQSxHQUN4QkMsUUFBUUMsSUFBSUgsSUFBSSJ9",
"" ""
].join("\n")); ].join("\n"));
done(); done();
@@ -550,4 +569,33 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should print supported options on invalid option syntax", function(done) {
var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should work with --mangle reserved=[]", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n');
done();
});
});
it("Should work with --mangle reserved=false", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n');
done();
});
});
}); });

View File

@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n'); assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
done(); done();
}); });
}); });

View File

@@ -13,6 +13,13 @@ describe("minify", function() {
assert.strictEqual(result.code, 'function foo(n){return n?3:7}'); assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
}); });
it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)";
var result = Uglify.minify(files);
assert.strictEqual(result.code, "alert(2);");
});
describe("keep_quoted_props", function() { describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() { it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -106,7 +113,7 @@ describe("minify", function() {
content: "inline" content: "inline"
} }
}); });
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();"); assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
assert.strictEqual(warnings.length, 1); assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found"); assert.strictEqual(warnings[0], "inline source map not found");
} finally { } finally {
@@ -138,13 +145,25 @@ describe("minify", function() {
}); });
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" + assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0="); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
}); });
it("should not append source map to output js when sourceMapInline is not enabled", function() { it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };'); var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};"); assert.strictEqual(code, "var a=function(n){return n};");
}); });
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
}); });
describe("#__PURE__", function() { describe("#__PURE__", function() {
@@ -181,4 +200,31 @@ describe("minify", function() {
assert.strictEqual(err.col, 12); assert.strictEqual(err.col, 12);
}); });
}); });
describe("global_defs", function() {
it("should throw for non-trivial expressions", function() {
var result = Uglify.minify("alert(42);", {
compress: {
global_defs: {
"@alert": "debugger"
}
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
});
it("should skip inherited properties", function() {
var foo = Object.create({ skip: this });
foo.bar = 42;
var result = Uglify.minify("alert(FOO);", {
compress: {
global_defs: {
FOO: foo
}
}
});
assert.strictEqual(result.code, "alert({bar:42});");
});
});
}); });

View File

@@ -1,23 +1,20 @@
var assert = require("assert"); var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn; var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return; if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) { function run(command, args, done) {
var id = setInterval(function() {
process.stdout.write("\0");
}, 5 * 60 * 1000);
spawn(command, args, { spawn(command, args, {
stdio: "ignore" stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) { }).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0); assert.strictEqual(code, 0);
done(); done();
}); });
} }
describe("test/benchmark.js", function() { describe("test/benchmark.js", function() {
this.timeout(5 * 60 * 1000); this.timeout(10 * 60 * 1000);
[ [
"-b", "-b",
"-b bracketize", "-b bracketize",
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
}); });
}); });
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() { describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000); this.timeout(20 * 60 * 1000);
it("Should install phantomjs-prebuilt", function(done) {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
});
[ [
"-mc", "-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
@@ -48,6 +43,7 @@ describe("test/jetstream.js", function() {
it("Should pass with options " + options, function(done) { it("Should pass with options " + options, function(done) {
var args = options.split(/ /); var args = options.split(/ /);
args.unshift("test/jetstream.js"); args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done); run(process.argv[0], args, done);
}); });
}); });

View File

@@ -4,7 +4,7 @@ var uglify = require("../node");
describe("spidermonkey export/import sanity test", function() { describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) { it("should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(30000); this.timeout(60000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " + var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
@@ -15,7 +15,9 @@ describe("spidermonkey export/import sanity test", function() {
eval(stdout); eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object"); assert.strictEqual(typeof SpiderUglify, "object");
assert.strictEqual(SpiderUglify.minify("foo([true,,2+3]);").code, "foo([!0,,5]);"); var result = SpiderUglify.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done(); done();
}); });

View File

@@ -65,9 +65,9 @@ describe("Yield", function() {
); );
}); });
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() { it("Should not allow yield to be used as symbol, identifier or shorthand property outside generators in strict mode", function() {
var tests = [ var tests = [
// Fail as as_symbol // Fail in as_symbol
'"use strict"; import yield from "bar";', '"use strict"; import yield from "bar";',
'"use strict"; yield = 123;', '"use strict"; yield = 123;',
'"use strict"; yield: "123";', '"use strict"; yield: "123";',
@@ -79,13 +79,12 @@ describe("Yield", function() {
'"use strict"; var yield = "foo";', '"use strict"; var yield = "foo";',
'"use strict"; class yield {}', '"use strict"; class yield {}',
// Fail as maybe_assign // Fail in maybe_assign
'"use strict"; var foo = yield;', '"use strict"; var foo = yield;',
'"use strict"; var foo = bar = yield', '"use strict"; var foo = bar = yield',
// Fail as as_property_name // Fail in as_property_name
'"use strict"; var foo = {yield};', '"use strict"; var foo = {yield};',
'"use strict"; var bar = {yield: "foo"};'
]; ];
var fail = function(e) { var fail = function(e) {
@@ -103,4 +102,4 @@ describe("Yield", function() {
assert.throws(test(tests[i]), fail, tests[i]); assert.throws(test(tests[i]), fail, tests[i]);
} }
}); });
}); });

View File

@@ -294,8 +294,22 @@ function parse_test(file) {
if (label.name == "expect_exact" || label.name == "node_version") { if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat); test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") { } else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) { var body = stat.body;
test[label.name] = stat.body.value; if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
} else { } else {
test[label.name] = read_string(stat) + "\n"; test[label.name] = read_string(stat) + "\n";
} }

View File

@@ -19,23 +19,25 @@ function safe_log(arg, level) {
var FUNC_TOSTRING = [ var FUNC_TOSTRING = [
"Function.prototype.toString = Function.prototype.valueOf = function() {", "Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 0;", " var id = 100000;",
" return function() {", " return function() {",
' if (this === Array) return "[Function: Array]";', ' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";', ' if (this === Object) return "[Function: Object]";',
" var i = this.name;", " var i = this.name;",
' if (typeof i != "number") {', ' if (typeof i != "number") {',
" i = ++id;", " i = ++id;",
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {', ' Object.defineProperty(this, "name", {',
" get: function() {", " get: function() {",
" return i;", " return i;",
" }", " }",
" });", " });",
] : [], [
" }", " }",
' return "[Function: " + i + "]";', ' return "[Function: " + i + "]";',
" }", " }",
"}();", "}();",
].join("\n"); ]).join("\n");
exports.run_code = function(code) { exports.run_code = function(code) {
var stdout = ""; var stdout = "";
var original_write = process.stdout.write; var original_write = process.stdout.write;
@@ -50,7 +52,10 @@ exports.run_code = function(code) {
"}();", "}();",
].join("\n"), { ].join("\n"), {
console: { console: {
log: function() { log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) { return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3); return safe_log(arg, 3);
})); }));

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("./node"); var UglifyJS = require("..");
var ok = require("assert"); var ok = require("assert");
module.exports = function () { module.exports = function () {
@@ -26,11 +26,11 @@ module.exports = function () {
} }
function source_map(js) { function source_map(js) {
var source_map = UglifyJS.SourceMap(); return JSON.parse(UglifyJS.minify(js, {
var stream = UglifyJS.OutputStream({ source_map: source_map }); compress: false,
var parsed = UglifyJS.parse(js); mangle: false,
parsed.print(stream); sourceMap: true
return JSON.parse(source_map.toString()); }).map);
} }
// Run standalone // Run standalone

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true); stream._handle.setBlocking(true);
}); });
var UglifyJS = require("./node"); var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes; var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
@@ -102,23 +102,23 @@ for (var i = 2; i < process.argv.length; ++i) {
case '--help': case '--help':
case '-h': case '-h':
case '-?': case '-?':
console.log('** UglifyJS fuzzer help **'); println('** UglifyJS fuzzer help **');
console.log('Valid options (optional):'); println('Valid options (optional):');
console.log('<number>: generate this many cases (if used must be first arg)'); println('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case'); println('-v: print every generated test case');
console.log('-V: print every 100th generated test case'); println('-V: print every 100th generated test case');
console.log('-t <int>: generate this many toplevels per run (more take longer)'); println('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)'); println('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)'); println('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)'); println('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables'); println('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives'); println('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"'); println('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise'); println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated'); println('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate'); println('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID)); println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
console.log('** UglifyJS fuzzer exiting **'); println('** UglifyJS fuzzer exiting **');
return 0; return 0;
default: default:
// first arg may be a number. // first arg may be a number.
@@ -941,7 +941,17 @@ if (require.main !== module) {
return; return;
} }
function try_beautify(code, result) { function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg);
process.stdout.write("\n");
}
function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg);
process.stderr.write("\n");
}
function try_beautify(code, result, printfn) {
var beautified = UglifyJS.minify(code, { var beautified = UglifyJS.minify(code, {
compress: false, compress: false,
mangle: false, mangle: false,
@@ -951,51 +961,34 @@ function try_beautify(code, result) {
}, },
}); });
if (beautified.error) { if (beautified.error) {
console.log("// !!! beautify failed !!!"); printfn("// !!! beautify failed !!!");
console.log(beautified.error.stack); printfn(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) { } else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)"); printfn("// (beautified)");
console.log(beautified.code); printfn(beautified.code);
return; return;
} }
console.log("//"); printfn("//");
console.log(code); printfn(code);
} }
function infer_options(ctor) { var default_options = UglifyJS.default_options();
try {
ctor({ 0: 0 });
} catch (e) {
return e.defs;
}
}
var default_options = {
compress: infer_options(UglifyJS.Compressor),
mangle: {
"cache": null,
"eval": false,
"ie8": false,
"keep_fnames": false,
"toplevel": false,
},
output: infer_options(UglifyJS.OutputStream),
};
function log_suspects(minify_options, component) { function log_suspects(minify_options, component) {
var options = component in minify_options ? minify_options[component] : true; var options = component in minify_options ? minify_options[component] : true;
if (!options) return; if (!options) return;
options = UglifyJS.defaults(options, default_options[component]); if (typeof options != "object") options = {};
var suspects = Object.keys(default_options[component]).filter(function(name) { var defs = default_options[component];
if (options[name]) { var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options)); var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options)); var o = JSON.parse(JSON.stringify(options));
o[name] = false; o[name] = false;
m[component] = o; m[component] = o;
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (result.error) {
console.log("Error testing options." + component + "." + name); errorln("Error testing options." + component + "." + name);
console.log(result.error); errorln(result.error.stack);
} else { } else {
var r = sandbox.run_code(result.code); var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r); return sandbox.same_stdout(original_result, r);
@@ -1003,49 +996,49 @@ function log_suspects(minify_options, component) {
} }
}); });
if (suspects.length > 0) { if (suspects.length > 0) {
console.log("Suspicious", component, "options:"); errorln("Suspicious " + component + " options:");
suspects.forEach(function(name) { suspects.forEach(function(name) {
console.log(" " + name); errorln(" " + name);
}); });
console.log(); errorln();
} }
} }
function log(options) { function log(options) {
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n'); if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
console.log("//============================================================="); errorln("//=============================================================");
if (!ok) console.log("// !!!!!! Failed... round", round); if (!ok) errorln("// !!!!!! Failed... round " + round);
console.log("// original code"); errorln("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result, errorln);
console.log(); errorln();
console.log(); errorln();
console.log("//-------------------------------------------------------------"); errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") { if (typeof uglify_code == "string") {
console.log("// uglified code"); errorln("// uglified code");
try_beautify(uglify_code, uglify_result); try_beautify(uglify_code, uglify_result, errorln);
console.log(); errorln();
console.log(); errorln();
console.log("original result:"); errorln("original result:");
console.log(original_result); errorln(typeof original_result == "string" ? original_result : original_result.stack);
console.log("uglified result:"); errorln("uglified result:");
console.log(uglify_result); errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
} else { } else {
console.log("// !!! uglify failed !!!"); errorln("// !!! uglify failed !!!");
console.log(uglify_code.stack); errorln(uglify_code.stack);
if (typeof original_result != "string") { if (typeof original_result != "string") {
console.log(); errorln();
console.log(); errorln();
console.log("original stacktrace:"); errorln("original stacktrace:");
console.log(original_result.stack); errorln(original_result.stack);
} }
} }
console.log("minify(options):"); errorln("minify(options):");
options = JSON.parse(options); options = JSON.parse(options);
console.log(options); errorln(JSON.stringify(options, null, 2));
console.log(); errorln();
if (!ok && typeof uglify_code == "string") { if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options)); Object.keys(default_options).forEach(log_suspects.bind(null, options));
console.log("!!!!!! Failed... round", round); errorln("!!!!!! Failed... round " + round);
} }
} }
@@ -1075,19 +1068,19 @@ for (var round = 1; round <= num_iterations; round++) {
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") { else if (typeof original_result != "string") {
console.log("//============================================================="); println("//=============================================================");
console.log("// original code"); println("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result, println);
console.log(); println();
console.log(); println();
console.log("original result:"); println("original result:");
console.log(original_result); println(original_result.stack);
console.log(); println();
} }
if (!ok && isFinite(num_iterations)) { if (!ok && isFinite(num_iterations)) {
console.log(); println();
process.exit(1); process.exit(1);
} }
}); });
} }
console.log(); println();

View File

@@ -61,5 +61,22 @@ function describe_ast() {
} }
}; };
doitem(AST_Node); doitem(AST_Node);
return out + ""; return out + "\n";
} }
function infer_options(options) {
var result = UglifyJS.minify("", options);
return result.error && result.error.defs;
}
UglifyJS.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {};
options[component] = { 0: 0 };
if (options = infer_options(options)) {
defs[component] = options;
}
});
return defs;
};