Compare commits
193 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73d082df2e | ||
|
|
50b8d7272c | ||
|
|
7d11b96f48 | ||
|
|
eab99a1c3d | ||
|
|
19e2fb134d | ||
|
|
f4919e3a25 | ||
|
|
bb700daa4c | ||
|
|
263577d5eb | ||
|
|
63287c0e68 | ||
|
|
c5ed2292bf | ||
|
|
b70670b69f | ||
|
|
9dd97605bc | ||
|
|
e4c5302406 | ||
|
|
bea3d90771 | ||
|
|
785c6064cc | ||
|
|
b214d3786f | ||
|
|
7cf79c302b | ||
|
|
a14c6b6574 | ||
|
|
f1b7094a57 | ||
|
|
0358e376f0 | ||
|
|
b47f7b76b9 | ||
|
|
582cc55cff | ||
|
|
8979579e55 | ||
|
|
0d6e08c541 | ||
|
|
e2daee9a65 | ||
|
|
9cd118ca3d | ||
|
|
cfd5c6155c | ||
|
|
1a5a4bd631 | ||
|
|
63e1a8e1fd | ||
|
|
7055af8221 | ||
|
|
aafe2e1db3 | ||
|
|
118105db43 | ||
|
|
63d04fff69 | ||
|
|
8c9cc920fb | ||
|
|
d09f0adae3 | ||
|
|
3fa9265ce4 | ||
|
|
3a81f60982 | ||
|
|
f2348dd98b | ||
|
|
253c7c2325 | ||
|
|
bb0a762d12 | ||
|
|
8cc86fee60 | ||
|
|
88fb83aa81 | ||
|
|
95b4507c02 | ||
|
|
afdaeba37d | ||
|
|
037199bfe2 | ||
|
|
583fac0a0f | ||
|
|
e8158279ff | ||
|
|
78e98d2611 | ||
|
|
8d14efe818 | ||
|
|
83ba338bd0 | ||
|
|
7c10b25346 | ||
|
|
cb9d16fbe4 | ||
|
|
5d8da864c5 | ||
|
|
85b527ba3d | ||
|
|
1c6efdae34 | ||
|
|
b0ca896d98 | ||
|
|
78a217b94c | ||
|
|
a89d233318 | ||
|
|
c28e1a0237 | ||
|
|
1a95007ec1 | ||
|
|
ed80b4a534 | ||
|
|
4f09df238e | ||
|
|
d9ad3c7cbf | ||
|
|
6ea3f7fe34 | ||
|
|
4c4dc2137c | ||
|
|
4aa4b3e694 | ||
|
|
2604aadb37 | ||
|
|
964d5b9aa4 | ||
|
|
b7adbcab1f | ||
|
|
3435af494f | ||
|
|
41c627379c | ||
|
|
e54df2226f | ||
|
|
b1febde3e9 | ||
|
|
193049af19 | ||
|
|
4a0bab0fa3 | ||
|
|
9243b0cb9d | ||
|
|
fc9ba323c4 | ||
|
|
d0689c81bb | ||
|
|
02a84385a0 | ||
|
|
a4889a0f2e | ||
|
|
f29f07aabd | ||
|
|
188e28efd7 | ||
|
|
2df48924cc | ||
|
|
9fc6796d2a | ||
|
|
9fc8a52142 | ||
|
|
3a21861580 | ||
|
|
1dbffd48ea | ||
|
|
22a038e6a2 | ||
|
|
f652372c9a | ||
|
|
ad1fc3b71a | ||
|
|
2b40a5ac62 | ||
|
|
ca3388cf5a | ||
|
|
caa8896a8a | ||
|
|
d13aa3954d | ||
|
|
f64539fb76 | ||
|
|
d56ebd7d7b | ||
|
|
3edfe7d0ee | ||
|
|
7f77edadb3 | ||
|
|
a9511dfbe5 | ||
|
|
064e7aa1bb | ||
|
|
46814f88d9 | ||
|
|
4a19802d0c | ||
|
|
1e9f98aa51 | ||
|
|
11e24d53a1 | ||
|
|
0f509f8336 | ||
|
|
a6ed2c84ac | ||
|
|
a1958aad56 | ||
|
|
672699613e | ||
|
|
645d5bdbc5 | ||
|
|
9af2bbffde | ||
|
|
fcd544cc10 | ||
|
|
1e3bc0caa0 | ||
|
|
8227e8795b | ||
|
|
790b3bcdc6 | ||
|
|
d6e6458f68 | ||
|
|
a54b6703c0 | ||
|
|
8e6266136d | ||
|
|
5c22a1bdf5 | ||
|
|
9794ebf88c | ||
|
|
68394eed93 | ||
|
|
753b4b6cc8 | ||
|
|
a9c1b9f138 | ||
|
|
5af144522a | ||
|
|
483e0cadfb | ||
|
|
4b818056cf | ||
|
|
b956e5f1d9 | ||
|
|
37d7cb8565 | ||
|
|
2b8e206fec | ||
|
|
a869b854fa | ||
|
|
81f5efe39a | ||
|
|
69dde0462b | ||
|
|
7628bcac01 | ||
|
|
75f0bbe6e8 | ||
|
|
478bf4dbdd | ||
|
|
e0f67baf2d | ||
|
|
b14d3df3d2 | ||
|
|
24e58ee70c | ||
|
|
9b1a40dfc3 | ||
|
|
e4b078cff7 | ||
|
|
3bd7ca9961 | ||
|
|
f83aca65b7 | ||
|
|
aebafad41e | ||
|
|
26746ce316 | ||
|
|
dac6efb43d | ||
|
|
8880f4824c | ||
|
|
cb0c576bdd | ||
|
|
3a591c43fe | ||
|
|
db66eca958 | ||
|
|
f2767452e6 | ||
|
|
916faf0a48 | ||
|
|
f36e4e9a78 | ||
|
|
fdf8b5eb71 | ||
|
|
de7ec7f1b7 | ||
|
|
3c8a0bdff4 | ||
|
|
9e8ba27dcd | ||
|
|
719a8fd102 | ||
|
|
3a22e917de | ||
|
|
a9af2c9e62 | ||
|
|
31e99cebe7 | ||
|
|
a5b209470c | ||
|
|
e9a571b2a1 | ||
|
|
8bf83f42ea | ||
|
|
522566ea80 | ||
|
|
297af47c89 | ||
|
|
faa354f5ca | ||
|
|
1529ab965a | ||
|
|
605f330e69 | ||
|
|
f0909bdc8f | ||
|
|
c13e7e621d | ||
|
|
ad071f8017 | ||
|
|
c058d8b9cd | ||
|
|
1d8871a092 | ||
|
|
16953c2064 | ||
|
|
6b14f7c224 | ||
|
|
130c623be7 | ||
|
|
47c9895d59 | ||
|
|
ba403331c5 | ||
|
|
e82e89d1b0 | ||
|
|
83a4ebfedc | ||
|
|
9916d0e547 | ||
|
|
31c4a37e37 | ||
|
|
08219f0cee | ||
|
|
c4993e1e5c | ||
|
|
6064bea3db | ||
|
|
98978fc827 | ||
|
|
16430acc1f | ||
|
|
320c110b33 | ||
|
|
dbe33bbfc5 | ||
|
|
b5c3253b49 | ||
|
|
5cc90db7d0 | ||
|
|
f427e5efc7 | ||
|
|
e48802ad29 | ||
|
|
13c4dfcabd |
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.4"
|
||||||
|
- "0.6"
|
||||||
|
- "0.8"
|
||||||
|
- "0.10"
|
||||||
|
- "0.11"
|
||||||
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
UglifyJS is released under the BSD license:
|
||||||
|
|
||||||
|
Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
395
README.md
395
README.md
@@ -1,5 +1,6 @@
|
|||||||
UglifyJS 2
|
UglifyJS 2
|
||||||
==========
|
==========
|
||||||
|
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||||
|
|
||||||
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
||||||
|
|
||||||
@@ -12,7 +13,14 @@ Chrome and probably Safari).
|
|||||||
Install
|
Install
|
||||||
-------
|
-------
|
||||||
|
|
||||||
From NPM:
|
First make sure you have installed the latest version of [node.js](http://nodejs.org/)
|
||||||
|
(You may need to restart your computer after this step).
|
||||||
|
|
||||||
|
From NPM for use as a command line app:
|
||||||
|
|
||||||
|
npm install uglify-js -g
|
||||||
|
|
||||||
|
From NPM for programmatic use:
|
||||||
|
|
||||||
npm install uglify-js
|
npm install uglify-js
|
||||||
|
|
||||||
@@ -38,47 +46,68 @@ files.
|
|||||||
|
|
||||||
The available options are:
|
The available options are:
|
||||||
|
|
||||||
--source-map Specify an output file where to generate source map.
|
```
|
||||||
[string]
|
--source-map Specify an output file where to generate source map.
|
||||||
--source-map-root The path to the original source to be included in the
|
[string]
|
||||||
source map. [string]
|
--source-map-root The path to the original source to be included in the
|
||||||
--in-source-map Input source map, useful if you're compressing JS that was
|
source map. [string]
|
||||||
generated from some other original code.
|
--source-map-url The path to the source map to be added in //#
|
||||||
-p, --prefix Skip prefix for original filenames that appear in source
|
sourceMappingURL. Defaults to the value passed with
|
||||||
maps. For example -p 3 will drop 3 directories from file
|
--source-map. [string]
|
||||||
names and ensure they are relative paths.
|
--in-source-map Input source map, useful if you're compressing JS that was
|
||||||
-o, --output Output file (default STDOUT).
|
generated from some other original code.
|
||||||
-b, --beautify Beautify output/specify output options. [string]
|
--screw-ie8 Pass this flag if you don't care about full compliance
|
||||||
-m, --mangle Mangle names/pass mangler options. [string]
|
with Internet Explorer 6-8 quirks (by default UglifyJS
|
||||||
-r, --reserved Reserved names to exclude from mangling.
|
will try to be IE-proof). [boolean]
|
||||||
-c, --compress Enable compressor/pass compressor options. Pass options
|
--expr Parse a single expression, rather than a program (for
|
||||||
like -c hoist_vars=false,if_return=false. Use -c with no
|
parsing JSON) [boolean]
|
||||||
argument to use the default compression options. [string]
|
-p, --prefix Skip prefix for original filenames that appear in source
|
||||||
-d, --define Global definitions [string]
|
maps. For example -p 3 will drop 3 directories from file
|
||||||
--comments Preserve copyright comments in the output. By default this
|
names and ensure they are relative paths. You can also
|
||||||
works like Google Closure, keeping JSDoc-style comments
|
specify -p relative, which will make UglifyJS figure out
|
||||||
that contain "@license" or "@preserve". You can optionally
|
itself the relative paths between original sources, the
|
||||||
pass one of the following arguments to this flag:
|
source map and the output file. [string]
|
||||||
- "all" to keep all comments
|
-o, --output Output file (default STDOUT).
|
||||||
- a valid JS regexp (needs to start with a slash) to keep
|
-b, --beautify Beautify output/specify output options. [string]
|
||||||
only comments that match.
|
-m, --mangle Mangle names/pass mangler options. [string]
|
||||||
Note that currently not *all* comments can be kept when
|
-r, --reserved Reserved names to exclude from mangling.
|
||||||
compression is on, because of dead code removal or
|
-c, --compress Enable compressor/pass compressor options. Pass options
|
||||||
cascading statements into sequences. [string]
|
like -c hoist_vars=false,if_return=false. Use -c with no
|
||||||
--stats Display operations run time on STDERR. [boolean]
|
argument to use the default compression options. [string]
|
||||||
--acorn Use Acorn for parsing. [boolean]
|
-d, --define Global definitions [string]
|
||||||
--spidermonkey Assume input fles are SpiderMonkey AST format (as JSON).
|
-e, --enclose Embed everything in a big function, with a configurable
|
||||||
[boolean]
|
parameter/argument list. [string]
|
||||||
--self Build itself (UglifyJS2) as a library (implies
|
--comments Preserve copyright comments in the output. By default this
|
||||||
--wrap=UglifyJS --export-all) [boolean]
|
works like Google Closure, keeping JSDoc-style comments
|
||||||
--wrap Embed everything in a big function, making the “exports”
|
that contain "@license" or "@preserve". You can optionally
|
||||||
and “global” variables available. You need to pass an
|
pass one of the following arguments to this flag:
|
||||||
argument to this option to specify the name that your
|
- "all" to keep all comments
|
||||||
module will take when included in, say, a browser.
|
- a valid JS regexp (needs to start with a slash) to keep
|
||||||
[string]
|
only comments that match.
|
||||||
--export-all Only used when --wrap, this tells UglifyJS to add code to
|
Note that currently not *all* comments can be kept when
|
||||||
automatically export all globals. [boolean]
|
compression is on, because of dead code removal or
|
||||||
-v, --verbose Verbose [boolean]
|
cascading statements into sequences. [string]
|
||||||
|
--preamble Preamble to prepend to the output. You can use this to
|
||||||
|
insert a comment, for example for licensing information.
|
||||||
|
This will not be parsed, but the source map will adjust
|
||||||
|
for its presence.
|
||||||
|
--stats Display operations run time on STDERR. [boolean]
|
||||||
|
--acorn Use Acorn for parsing. [boolean]
|
||||||
|
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
|
||||||
|
[boolean]
|
||||||
|
--self Build itself (UglifyJS2) as a library (implies
|
||||||
|
--wrap=UglifyJS --export-all) [boolean]
|
||||||
|
--wrap Embed everything in a big function, making the “exports”
|
||||||
|
and “global” variables available. You need to pass an
|
||||||
|
argument to this option to specify the name that your
|
||||||
|
module will take when included in, say, a browser.
|
||||||
|
[string]
|
||||||
|
--export-all Only used when --wrap, this tells UglifyJS to add code to
|
||||||
|
automatically export all globals. [boolean]
|
||||||
|
--lint Display some scope warnings [boolean]
|
||||||
|
-v, --verbose Verbose [boolean]
|
||||||
|
-V, --version Print version number and exit. [boolean]
|
||||||
|
```
|
||||||
|
|
||||||
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
||||||
goes to STDOUT.
|
goes to STDOUT.
|
||||||
@@ -129,12 +158,19 @@ input files from the command line.
|
|||||||
|
|
||||||
## Mangler options
|
## Mangler options
|
||||||
|
|
||||||
To enable the mangler you need to pass `--mangle` (`-m`). Optionally you
|
To enable the mangler you need to pass `--mangle` (`-m`). The following
|
||||||
can pass `-m sort` (we'll possibly have other flags in the future) in order
|
(comma-separated) options are supported:
|
||||||
to assign shorter names to most frequently used variables. This saves a few
|
|
||||||
hundred bytes on jQuery before gzip, but the output is _bigger_ after gzip
|
- `sort` — to assign shorter names to most frequently used variables. This
|
||||||
(and seems to happen for other libraries I tried it on) therefore it's not
|
saves a few hundred bytes on jQuery before gzip, but the output is
|
||||||
enabled by default.
|
_bigger_ after gzip (and seems to happen for other libraries I tried it
|
||||||
|
on) therefore it's not enabled by default.
|
||||||
|
|
||||||
|
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||||
|
default).
|
||||||
|
|
||||||
|
- `eval` — mangle names visible in scopes where `eval` or `when` are used
|
||||||
|
(disabled by default).
|
||||||
|
|
||||||
When mangling is enabled but you want to prevent certain names from being
|
When mangling is enabled but you want to prevent certain names from being
|
||||||
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
||||||
@@ -151,46 +187,95 @@ you can pass a comma-separated list of options. Options are in the form
|
|||||||
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
||||||
to set `true`; it's effectively a shortcut for `foo=true`).
|
to set `true`; it's effectively a shortcut for `foo=true`).
|
||||||
|
|
||||||
The defaults should be tuned for maximum compression on most code. Here are
|
|
||||||
the available options (all are `true` by default, except `hoist_vars`):
|
|
||||||
|
|
||||||
- `sequences` -- join consecutive simple statements using the comma operator
|
- `sequences` -- join consecutive simple statements using the comma operator
|
||||||
|
|
||||||
- `properties` -- rewrite property access using the dot notation, for
|
- `properties` -- rewrite property access using the dot notation, for
|
||||||
example `foo["bar"] → foo.bar`
|
example `foo["bar"] → foo.bar`
|
||||||
- `dead-code` -- remove unreachable code
|
|
||||||
- `drop-debugger` -- remove `debugger;` statements
|
- `dead_code` -- remove unreachable code
|
||||||
- `unsafe` -- apply "unsafe" transformations (discussion below)
|
|
||||||
|
- `drop_debugger` -- remove `debugger;` statements
|
||||||
|
|
||||||
|
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
|
||||||
|
|
||||||
- `conditionals` -- apply optimizations for `if`-s and conditional
|
- `conditionals` -- apply optimizations for `if`-s and conditional
|
||||||
expressions
|
expressions
|
||||||
|
|
||||||
- `comparisons` -- apply certain optimizations to binary nodes, for example:
|
- `comparisons` -- apply certain optimizations to binary nodes, for example:
|
||||||
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
|
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
|
||||||
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
||||||
|
|
||||||
- `evaluate` -- attempt to evaluate constant expressions
|
- `evaluate` -- attempt to evaluate constant expressions
|
||||||
|
|
||||||
- `booleans` -- various optimizations for boolean context, for example `!!a
|
- `booleans` -- various optimizations for boolean context, for example `!!a
|
||||||
? b : c → a ? b : c`
|
? b : c → a ? b : c`
|
||||||
|
|
||||||
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
|
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
|
||||||
statically determine the condition
|
statically determine the condition
|
||||||
|
|
||||||
- `unused` -- drop unreferenced functions and variables
|
- `unused` -- drop unreferenced functions and variables
|
||||||
- `hoist-funs` -- hoist function declarations
|
|
||||||
- `hoist-vars` -- hoist `var` declarations (this is `false` by default
|
- `hoist_funs` -- hoist function declarations
|
||||||
because it seems to increase the size of the output in general)
|
|
||||||
- `if-return` -- optimizations for if/return and if/continue
|
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
|
||||||
- `join-vars` -- join consecutive `var` statements
|
by default because it seems to increase the size of the output in general)
|
||||||
|
|
||||||
|
- `if_return` -- optimizations for if/return and if/continue
|
||||||
|
|
||||||
|
- `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()`
|
||||||
|
|
||||||
- `warnings` -- display warnings when dropping unreachable code or unused
|
- `warnings` -- display warnings when dropping unreachable code or unused
|
||||||
declarations etc.
|
declarations etc.
|
||||||
|
|
||||||
|
- `negate_iife` -- negate "Immediately-Called Function Expressions"
|
||||||
|
where the return value is discarded, to avoid the parens that the
|
||||||
|
code generator would insert.
|
||||||
|
|
||||||
|
- `pure_getters` -- the default is `false`. If you pass `true` for
|
||||||
|
this, UglifyJS will assume that object property access
|
||||||
|
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
|
||||||
|
|
||||||
|
- `pure_funcs` -- default `null`. You can pass an array of names and
|
||||||
|
UglifyJS will assume that those functions do not produce side
|
||||||
|
effects. DANGER: will not check if the name is redefined in scope.
|
||||||
|
An example case here, for instance `var q = Math.floor(a/b)`. If
|
||||||
|
variable `q` is not used elsewhere, UglifyJS will drop it, but will
|
||||||
|
still keep the `Math.floor(a/b)`, not knowing what it does. You can
|
||||||
|
pass `pure_funcs: [ 'Math.floor' ]` to let it know that this
|
||||||
|
function won't produce any side effect, in which case the whole
|
||||||
|
statement would get discarded. The current implementation adds some
|
||||||
|
overhead (compression will be slower).
|
||||||
|
|
||||||
|
### The `unsafe` option
|
||||||
|
|
||||||
|
It enables some transformations that *might* break code logic in certain
|
||||||
|
contrived cases, but should be fine for most code. You might want to try it
|
||||||
|
on your own code, it should reduce the minified size. Here's what happens
|
||||||
|
when this flag is on:
|
||||||
|
|
||||||
|
- `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[1, 2, 3 ]`
|
||||||
|
- `new Object()` → `{}`
|
||||||
|
- `String(exp)` or `exp.toString()` → `"" + exp`
|
||||||
|
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
|
||||||
|
- `typeof foo == "undefined"` → `foo === void 0`
|
||||||
|
- `void 0` → `undefined` (if there is a variable named "undefined" in
|
||||||
|
scope; we do it because the variable name will be mangled, typically
|
||||||
|
reduced to a single character).
|
||||||
|
|
||||||
### Conditional compilation
|
### Conditional compilation
|
||||||
|
|
||||||
You can use the `--define` (`-d`) switch in order to declare global
|
You can use the `--define` (`-d`) switch in order to declare global
|
||||||
variables that UglifyJS will assume to be constants (unless defined in
|
variables that UglifyJS will assume to be constants (unless defined in
|
||||||
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
||||||
dead code removal UglifyJS will discard the following from the output:
|
dead code removal UglifyJS will discard the following from the output:
|
||||||
|
```javascript
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log("debug stuff");
|
console.log("debug stuff");
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
UglifyJS will warn about the condition being always false and about dropping
|
UglifyJS will warn about the condition being always false and about dropping
|
||||||
unreachable code; for now there is no option to turn off only this specific
|
unreachable code; for now there is no option to turn off only this specific
|
||||||
@@ -199,10 +284,11 @@ warning, you can pass `warnings=false` to turn off *all* warnings.
|
|||||||
Another way of doing that is to declare your globals as constants in a
|
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
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
const PRODUCTION = true;
|
const PRODUCTION = true;
|
||||||
// etc.
|
// etc.
|
||||||
|
```
|
||||||
|
|
||||||
and build your code like this:
|
and build your code like this:
|
||||||
|
|
||||||
@@ -213,6 +299,7 @@ will evaluate references to them to the value itself and drop unreachable
|
|||||||
code as usual. The possible downside of this approach is that the build
|
code as usual. The possible downside of this approach is that the build
|
||||||
will contain the `const` declarations.
|
will contain the `const` declarations.
|
||||||
|
|
||||||
|
<a name="codegen-options"></a>
|
||||||
## Beautifier options
|
## Beautifier options
|
||||||
|
|
||||||
The code generator tries to output shortest code possible by default. In
|
The code generator tries to output shortest code possible by default. In
|
||||||
@@ -238,9 +325,6 @@ can pass additional arguments that control the code output:
|
|||||||
It doesn't work very well currently, but it does make the code generated
|
It doesn't work very well currently, but it does make the code generated
|
||||||
by UglifyJS more readable.
|
by UglifyJS more readable.
|
||||||
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
||||||
- `ie-proof` (default `true`) -- generate “IE-proof” code (for now this
|
|
||||||
means add brackets around the do/while in code like this: `if (foo) do
|
|
||||||
something(); while (bar); else ...`.
|
|
||||||
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
||||||
`do`, `while` or `with` statements, even if their body is a single
|
`do`, `while` or `with` statements, even if their body is a single
|
||||||
statement.
|
statement.
|
||||||
@@ -248,6 +332,10 @@ can pass additional arguments that control the code output:
|
|||||||
you pass `false` then whenever possible we will use a newline instead of a
|
you pass `false` then whenever possible we will use a newline instead of a
|
||||||
semicolon, leading to more readable output of uglified code (size before
|
semicolon, leading to more readable output of uglified code (size before
|
||||||
gzip could be smaller; size after gzip insignificantly larger).
|
gzip could be smaller; size after gzip insignificantly larger).
|
||||||
|
- `preamble` (default `null`) -- when passed it must be a string and
|
||||||
|
it will be prepended to the output literally. The source map will
|
||||||
|
adjust for this text. Can be used to insert a comment containing
|
||||||
|
licensing information, for example.
|
||||||
|
|
||||||
### Keeping copyright notices or other comments
|
### Keeping copyright notices or other comments
|
||||||
|
|
||||||
@@ -260,14 +348,15 @@ keep only comments that match this regexp. For example `--comments
|
|||||||
|
|
||||||
Note, however, that there might be situations where comments are lost. For
|
Note, however, that there might be situations where comments are lost. For
|
||||||
example:
|
example:
|
||||||
|
```javascript
|
||||||
function f() {
|
function f() {
|
||||||
/** @preserve Foo Bar */
|
/** @preserve Foo Bar */
|
||||||
function g() {
|
function g() {
|
||||||
// this function is never called
|
// this function is never called
|
||||||
}
|
}
|
||||||
return something();
|
return something();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Even though it has "@preserve", the comment will be lost because the inner
|
Even though it has "@preserve", the comment will be lost because the inner
|
||||||
function `g` (which is the AST node to which the comment is attached to) is
|
function `g` (which is the AST node to which the comment is attached to) is
|
||||||
@@ -309,8 +398,9 @@ API Reference
|
|||||||
|
|
||||||
Assuming installation via NPM, you can load UglifyJS in your application
|
Assuming installation via NPM, you can load UglifyJS in your application
|
||||||
like this:
|
like this:
|
||||||
|
```javascript
|
||||||
var UglifyJS = require("uglify-js");
|
var UglifyJS = require("uglify-js");
|
||||||
|
```
|
||||||
|
|
||||||
It exports a lot of names, but I'll discuss here the basics that are needed
|
It exports a lot of names, but I'll discuss here the basics that are needed
|
||||||
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
||||||
@@ -321,44 +411,49 @@ parse, (2) compress, (3) mangle, (4) generate output code.
|
|||||||
There's a single toplevel function which combines all the steps. If you
|
There's a single toplevel function which combines all the steps. If you
|
||||||
don't need additional customization, you might want to go with `minify`.
|
don't need additional customization, you might want to go with `minify`.
|
||||||
Example:
|
Example:
|
||||||
|
```javascript
|
||||||
// see "fromString" below if you need to pass code instead of file name
|
var result = UglifyJS.minify("/path/to/file.js");
|
||||||
var result = UglifyJS.minify("/path/to/file.js");
|
console.log(result.code); // minified output
|
||||||
console.log(result.code); // minified output
|
// if you need to pass code instead of file name
|
||||||
|
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
|
||||||
|
```
|
||||||
|
|
||||||
You can also compress multiple files:
|
You can also compress multiple files:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
||||||
console.log(result.code);
|
console.log(result.code);
|
||||||
|
```
|
||||||
|
|
||||||
To generate a source map:
|
To generate a source map:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
outSourceMap: "out.js.map"
|
outSourceMap: "out.js.map"
|
||||||
});
|
});
|
||||||
console.log(result.code); // minified output
|
console.log(result.code); // minified output
|
||||||
console.log(result.map);
|
console.log(result.map);
|
||||||
|
```
|
||||||
|
|
||||||
Note that the source map is not saved in a file, it's just returned in
|
Note that the source map is not saved in a file, it's just returned in
|
||||||
`result.map`. The value passed for `outSourceMap` is only used to set the
|
`result.map`. The value passed for `outSourceMap` is only used to set the
|
||||||
`file` attribute in the source map (see [the spec][sm-spec]).
|
`file` attribute in the source map (see [the spec][sm-spec]).
|
||||||
|
|
||||||
You can also specify sourceRoot property to be included in source map:
|
You can also specify sourceRoot property to be included in source map:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
outSourceMap: "out.js.map",
|
outSourceMap: "out.js.map",
|
||||||
sourceRoot: "http://example.com/src"
|
sourceRoot: "http://example.com/src"
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
If you're compressing compiled JavaScript and have a source map for it, you
|
If you're compressing compiled JavaScript and have a source map for it, you
|
||||||
can use the `inSourceMap` argument:
|
can use the `inSourceMap` argument:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify("compiled.js", {
|
var result = UglifyJS.minify("compiled.js", {
|
||||||
inSourceMap: "compiled.js.map",
|
inSourceMap: "compiled.js.map",
|
||||||
outSourceMap: "minified.js.map"
|
outSourceMap: "minified.js.map"
|
||||||
});
|
});
|
||||||
// same as before, it returns `code` and `map`
|
// same as before, it returns `code` and `map`
|
||||||
|
```
|
||||||
|
|
||||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||||
no sense otherwise).
|
no sense otherwise).
|
||||||
@@ -366,9 +461,19 @@ no sense otherwise).
|
|||||||
Other options:
|
Other options:
|
||||||
|
|
||||||
- `warnings` (default `false`) — pass `true` to display compressor warnings.
|
- `warnings` (default `false`) — pass `true` to display compressor warnings.
|
||||||
|
|
||||||
- `fromString` (default `false`) — if you pass `true` then you can pass
|
- `fromString` (default `false`) — if you pass `true` then you can pass
|
||||||
JavaScript source code, rather than file names.
|
JavaScript source code, rather than file names.
|
||||||
|
|
||||||
|
- `mangle` — pass `false` to skip mangling names.
|
||||||
|
|
||||||
|
- `output` (default `null`) — pass an object if you wish to specify
|
||||||
|
additional [output options][codegen]. The defaults are optimized
|
||||||
|
for best compression.
|
||||||
|
|
||||||
|
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
||||||
|
Pass an object to specify custom [compressor options][compressor].
|
||||||
|
|
||||||
We could add more options to `UglifyJS.minify` — if you need additional
|
We could add more options to `UglifyJS.minify` — if you need additional
|
||||||
functionality please suggest!
|
functionality please suggest!
|
||||||
|
|
||||||
@@ -378,8 +483,9 @@ Following there's more detailed API info, in case the `minify` function is
|
|||||||
too simple for your needs.
|
too simple for your needs.
|
||||||
|
|
||||||
#### The parser
|
#### The parser
|
||||||
|
```javascript
|
||||||
var toplevel_ast = UglifyJS.parse(code, options);
|
var toplevel_ast = UglifyJS.parse(code, options);
|
||||||
|
```
|
||||||
|
|
||||||
`options` is optional and if present it must be an object. The following
|
`options` is optional and if present it must be an object. The following
|
||||||
properties are available:
|
properties are available:
|
||||||
@@ -393,15 +499,16 @@ properties are available:
|
|||||||
The last two options are useful when you'd like to minify multiple files and
|
The last two options are useful when you'd like to minify multiple files and
|
||||||
get a single file as the output and a proper source map. Our CLI tool does
|
get a single file as the output and a proper source map. Our CLI tool does
|
||||||
something like this:
|
something like this:
|
||||||
|
```javascript
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
var code = fs.readFileSync(file);
|
var code = fs.readFileSync(file);
|
||||||
toplevel = UglifyJS.parse(code, {
|
toplevel = UglifyJS.parse(code, {
|
||||||
filename: file,
|
filename: file,
|
||||||
toplevel: toplevel
|
toplevel: toplevel
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
After this, we have in `toplevel` a big AST containing all our files, with
|
After this, we have in `toplevel` a big AST containing all our files, with
|
||||||
each token having proper information about where it came from.
|
each token having proper information about where it came from.
|
||||||
@@ -415,15 +522,17 @@ referenced, if it is a global or not, if a function is using `eval` or the
|
|||||||
`with` statement etc. I will discuss this some place else, for now what's
|
`with` statement etc. I will discuss this some place else, for now what's
|
||||||
important to know is that you need to call the following before doing
|
important to know is that you need to call the following before doing
|
||||||
anything with the tree:
|
anything with the tree:
|
||||||
|
```javascript
|
||||||
toplevel.figure_out_scope()
|
toplevel.figure_out_scope()
|
||||||
|
```
|
||||||
|
|
||||||
#### Compression
|
#### Compression
|
||||||
|
|
||||||
Like this:
|
Like this:
|
||||||
|
```javascript
|
||||||
var compressor = UglifyJS.Compressor(options);
|
var compressor = UglifyJS.Compressor(options);
|
||||||
var compressed_ast = toplevel.transform(compressor);
|
var compressed_ast = toplevel.transform(compressor);
|
||||||
|
```
|
||||||
|
|
||||||
The `options` can be missing. Available options are discussed above in
|
The `options` can be missing. Available options are discussed above in
|
||||||
“Compressor options”. Defaults should lead to best compression in most
|
“Compressor options”. Defaults should lead to best compression in most
|
||||||
@@ -439,23 +548,26 @@ the compressor might drop unused variables / unreachable code and this might
|
|||||||
change the number of identifiers or their position). Optionally, you can
|
change the number of identifiers or their position). Optionally, you can
|
||||||
call a trick that helps after Gzip (counting character frequency in
|
call a trick that helps after Gzip (counting character frequency in
|
||||||
non-mangleable words). Example:
|
non-mangleable words). Example:
|
||||||
|
```javascript
|
||||||
compressed_ast.figure_out_scope();
|
compressed_ast.figure_out_scope();
|
||||||
compressed_ast.compute_char_frequency();
|
compressed_ast.compute_char_frequency();
|
||||||
compressed_ast.mangle_names();
|
compressed_ast.mangle_names();
|
||||||
|
```
|
||||||
|
|
||||||
#### Generating output
|
#### Generating output
|
||||||
|
|
||||||
AST nodes have a `print` method that takes an output stream. Essentially,
|
AST nodes have a `print` method that takes an output stream. Essentially,
|
||||||
to generate code you do this:
|
to generate code you do this:
|
||||||
|
```javascript
|
||||||
var stream = UglifyJS.OutputStream(options);
|
var stream = UglifyJS.OutputStream(options);
|
||||||
compressed_ast.print(stream);
|
compressed_ast.print(stream);
|
||||||
var code = stream.toString(); // this is your minified code
|
var code = stream.toString(); // this is your minified code
|
||||||
|
```
|
||||||
|
|
||||||
or, for a shortcut you can do:
|
or, for a shortcut you can do:
|
||||||
|
```javascript
|
||||||
var code = compressed_ast.print_to_string(options);
|
var code = compressed_ast.print_to_string(options);
|
||||||
|
```
|
||||||
|
|
||||||
As usual, `options` is optional. The output stream accepts a lot of otions,
|
As usual, `options` is optional. The output stream accepts a lot of otions,
|
||||||
most of them documented above in section “Beautifier options”. The two
|
most of them documented above in section “Beautifier options”. The two
|
||||||
@@ -493,16 +605,17 @@ to be a `SourceMap` object (which is a thin wrapper on top of the
|
|||||||
[source-map][source-map] library).
|
[source-map][source-map] library).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```javascript
|
||||||
|
var source_map = UglifyJS.SourceMap(source_map_options);
|
||||||
|
var stream = UglifyJS.OutputStream({
|
||||||
|
...
|
||||||
|
source_map: source_map
|
||||||
|
});
|
||||||
|
compressed_ast.print(stream);
|
||||||
|
|
||||||
var source_map = UglifyJS.SourceMap(source_map_options);
|
var code = stream.toString();
|
||||||
var stream = UglifyJS.OutputStream({
|
var map = source_map.toString(); // json output for your source map
|
||||||
...
|
```
|
||||||
source_map: source_map
|
|
||||||
});
|
|
||||||
compressed_ast.print(stream);
|
|
||||||
|
|
||||||
var code = stream.toString();
|
|
||||||
var map = source_map.toString(); // json output for your source map
|
|
||||||
|
|
||||||
The `source_map_options` (optional) can contain the following properties:
|
The `source_map_options` (optional) can contain the following properties:
|
||||||
|
|
||||||
@@ -516,3 +629,5 @@ The `source_map_options` (optional) can contain the following properties:
|
|||||||
[acorn]: https://github.com/marijnh/acorn
|
[acorn]: https://github.com/marijnh/acorn
|
||||||
[source-map]: https://github.com/mozilla/source-map
|
[source-map]: https://github.com/mozilla/source-map
|
||||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
||||||
|
[codegen]: http://lisperator.net/uglifyjs/codegen
|
||||||
|
[compressor]: http://lisperator.net/uglifyjs/compress
|
||||||
|
|||||||
287
bin/uglifyjs
287
bin/uglifyjs
@@ -7,6 +7,8 @@ var UglifyJS = require("../tools/node");
|
|||||||
var sys = require("util");
|
var sys = require("util");
|
||||||
var optimist = require("optimist");
|
var optimist = require("optimist");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
var async = require("async");
|
||||||
var acorn;
|
var acorn;
|
||||||
var ARGS = optimist
|
var ARGS = optimist
|
||||||
.usage("$0 input1.js [input2.js ...] [options]\n\
|
.usage("$0 input1.js [input2.js ...] [options]\n\
|
||||||
@@ -19,9 +21,14 @@ mangling you need to use `-c` and `-m`.\
|
|||||||
")
|
")
|
||||||
.describe("source-map", "Specify an output file where to generate source map.")
|
.describe("source-map", "Specify an output file where to generate source map.")
|
||||||
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
||||||
|
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
|
||||||
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
||||||
|
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
|
||||||
|
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
|
||||||
.describe("p", "Skip prefix for original filenames that appear in source maps. \
|
.describe("p", "Skip prefix for original filenames that appear in source maps. \
|
||||||
For example -p 3 will drop 3 directories from file names and ensure they are relative paths.")
|
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
|
||||||
|
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
|
||||||
|
the source map and the output file.")
|
||||||
.describe("o", "Output file (default STDOUT).")
|
.describe("o", "Output file (default STDOUT).")
|
||||||
.describe("b", "Beautify output/specify output options.")
|
.describe("b", "Beautify output/specify output options.")
|
||||||
.describe("m", "Mangle names/pass mangler options.")
|
.describe("m", "Mangle names/pass mangler options.")
|
||||||
@@ -30,6 +37,7 @@ For example -p 3 will drop 3 directories from file names and ensure they are rel
|
|||||||
Pass options like -c hoist_vars=false,if_return=false. \
|
Pass options like -c hoist_vars=false,if_return=false. \
|
||||||
Use -c with no argument to use the default compression options.")
|
Use -c with no argument to use the default compression options.")
|
||||||
.describe("d", "Global definitions")
|
.describe("d", "Global definitions")
|
||||||
|
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
|
||||||
|
|
||||||
.describe("comments", "Preserve copyright comments in the output. \
|
.describe("comments", "Preserve copyright comments in the output. \
|
||||||
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
||||||
@@ -40,15 +48,20 @@ You can optionally pass one of the following arguments to this flag:\n\
|
|||||||
Note that currently not *all* comments can be kept when compression is on, \
|
Note that currently not *all* comments can be kept when compression is on, \
|
||||||
because of dead code removal or cascading statements into sequences.")
|
because of dead code removal or cascading statements into sequences.")
|
||||||
|
|
||||||
|
.describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
|
||||||
|
comment, for example for licensing information. This will not be \
|
||||||
|
parsed, but the source map will adjust for its presence.")
|
||||||
|
|
||||||
.describe("stats", "Display operations run time on STDERR.")
|
.describe("stats", "Display operations run time on STDERR.")
|
||||||
.describe("acorn", "Use Acorn for parsing.")
|
.describe("acorn", "Use Acorn for parsing.")
|
||||||
.describe("spidermonkey", "Assume input fles are SpiderMonkey AST format (as JSON).")
|
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
|
||||||
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
||||||
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
||||||
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
||||||
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
|
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
|
||||||
.describe("lint", "Display some scope warnings")
|
.describe("lint", "Display some scope warnings")
|
||||||
.describe("v", "Verbose")
|
.describe("v", "Verbose")
|
||||||
|
.describe("V", "Print version number and exit.")
|
||||||
|
|
||||||
.alias("p", "prefix")
|
.alias("p", "prefix")
|
||||||
.alias("o", "output")
|
.alias("o", "output")
|
||||||
@@ -58,15 +71,23 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.alias("c", "compress")
|
.alias("c", "compress")
|
||||||
.alias("d", "define")
|
.alias("d", "define")
|
||||||
.alias("r", "reserved")
|
.alias("r", "reserved")
|
||||||
|
.alias("V", "version")
|
||||||
|
.alias("e", "enclose")
|
||||||
|
|
||||||
.string("source-map")
|
.string("source-map")
|
||||||
.string("source-map-root")
|
.string("source-map-root")
|
||||||
|
.string("source-map-url")
|
||||||
.string("b")
|
.string("b")
|
||||||
.string("m")
|
.string("m")
|
||||||
.string("c")
|
.string("c")
|
||||||
.string("d")
|
.string("d")
|
||||||
|
.string("e")
|
||||||
.string("comments")
|
.string("comments")
|
||||||
.string("wrap")
|
.string("wrap")
|
||||||
|
.string("p")
|
||||||
|
|
||||||
|
.boolean("expr")
|
||||||
|
.boolean("screw-ie8")
|
||||||
.boolean("export-all")
|
.boolean("export-all")
|
||||||
.boolean("self")
|
.boolean("self")
|
||||||
.boolean("v")
|
.boolean("v")
|
||||||
@@ -74,6 +95,7 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.boolean("acorn")
|
.boolean("acorn")
|
||||||
.boolean("spidermonkey")
|
.boolean("spidermonkey")
|
||||||
.boolean("lint")
|
.boolean("lint")
|
||||||
|
.boolean("V")
|
||||||
|
|
||||||
.wrap(80)
|
.wrap(80)
|
||||||
|
|
||||||
@@ -82,6 +104,12 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
|
|
||||||
normalize(ARGS);
|
normalize(ARGS);
|
||||||
|
|
||||||
|
if (ARGS.version || ARGS.V) {
|
||||||
|
var json = require("../package.json");
|
||||||
|
sys.puts(json.name + ' ' + json.version);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (ARGS.ast_help) {
|
if (ARGS.ast_help) {
|
||||||
var desc = UglifyJS.describe_ast();
|
var desc = UglifyJS.describe_ast();
|
||||||
sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
|
sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
|
||||||
@@ -101,18 +129,25 @@ var COMPRESS = getOptions("c", true);
|
|||||||
var MANGLE = getOptions("m", true);
|
var MANGLE = getOptions("m", true);
|
||||||
var BEAUTIFY = getOptions("b", true);
|
var BEAUTIFY = getOptions("b", true);
|
||||||
|
|
||||||
if (COMPRESS && ARGS.d) {
|
if (ARGS.d) {
|
||||||
COMPRESS.global_defs = getOptions("d");
|
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MANGLE && ARGS.r) {
|
if (ARGS.r) {
|
||||||
MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
var OUTPUT_OPTIONS = {
|
var OUTPUT_OPTIONS = {
|
||||||
beautify: BEAUTIFY ? true : false
|
beautify: BEAUTIFY ? true : false,
|
||||||
|
preamble: ARGS.preamble || null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (ARGS.screw_ie8) {
|
||||||
|
if (COMPRESS) COMPRESS.screw_ie8 = true;
|
||||||
|
if (MANGLE) MANGLE.screw_ie8 = true;
|
||||||
|
OUTPUT_OPTIONS.screw_ie8 = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (BEAUTIFY)
|
if (BEAUTIFY)
|
||||||
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
||||||
|
|
||||||
@@ -174,9 +209,10 @@ if (files.filter(function(el){ return el == "-" }).length > 1) {
|
|||||||
var STATS = {};
|
var STATS = {};
|
||||||
var OUTPUT_FILE = ARGS.o;
|
var OUTPUT_FILE = ARGS.o;
|
||||||
var TOPLEVEL = null;
|
var TOPLEVEL = null;
|
||||||
|
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
||||||
|
|
||||||
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
||||||
file: OUTPUT_FILE,
|
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
||||||
root: ARGS.source_map_root,
|
root: ARGS.source_map_root,
|
||||||
orig: ORIG_MAP,
|
orig: ORIG_MAP,
|
||||||
}) : null;
|
}) : null;
|
||||||
@@ -195,100 +231,139 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
files.forEach(function(file) {
|
async.eachLimit(files, 1, function (file, cb) {
|
||||||
var code = read_whole_file(file);
|
read_whole_file(file, function (err, code) {
|
||||||
if (ARGS.p != null) {
|
if (err) {
|
||||||
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
sys.error("ERROR: can't read file: " + file);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (ARGS.p != null) {
|
||||||
|
if (P_RELATIVE) {
|
||||||
|
file = path.relative(path.dirname(ARGS.source_map), file);
|
||||||
|
} else {
|
||||||
|
var p = parseInt(ARGS.p, 10);
|
||||||
|
if (!isNaN(p)) {
|
||||||
|
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time_it("parse", function(){
|
||||||
|
if (ARGS.spidermonkey) {
|
||||||
|
var program = JSON.parse(code);
|
||||||
|
if (!TOPLEVEL) TOPLEVEL = program;
|
||||||
|
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
||||||
|
}
|
||||||
|
else if (ARGS.acorn) {
|
||||||
|
TOPLEVEL = acorn.parse(code, {
|
||||||
|
locations : true,
|
||||||
|
sourceFile : file,
|
||||||
|
program : TOPLEVEL
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
TOPLEVEL = UglifyJS.parse(code, {
|
||||||
|
filename : file,
|
||||||
|
toplevel : TOPLEVEL,
|
||||||
|
expression : ARGS.expr,
|
||||||
|
});
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||||
|
sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col);
|
||||||
|
sys.error(ex.message);
|
||||||
|
sys.error(ex.stack);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
|
||||||
|
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ARGS.wrap) {
|
||||||
|
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
|
||||||
}
|
}
|
||||||
time_it("parse", function(){
|
|
||||||
if (ARGS.spidermonkey) {
|
if (ARGS.enclose) {
|
||||||
var program = JSON.parse(code);
|
var arg_parameter_list = ARGS.enclose;
|
||||||
if (!TOPLEVEL) TOPLEVEL = program;
|
if (arg_parameter_list === true) {
|
||||||
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
arg_parameter_list = [];
|
||||||
}
|
}
|
||||||
else if (ARGS.acorn) {
|
else if (!(arg_parameter_list instanceof Array)) {
|
||||||
TOPLEVEL = acorn.parse(code, {
|
arg_parameter_list = [arg_parameter_list];
|
||||||
locations : true,
|
|
||||||
trackComments : true,
|
|
||||||
sourceFile : file,
|
|
||||||
program : TOPLEVEL
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
|
||||||
TOPLEVEL = UglifyJS.parse(code, {
|
}
|
||||||
filename: file,
|
|
||||||
toplevel: TOPLEVEL
|
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
|
||||||
});
|
|
||||||
};
|
if (SCOPE_IS_NEEDED) {
|
||||||
|
time_it("scope", function(){
|
||||||
|
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
|
||||||
|
if (ARGS.lint) {
|
||||||
|
TOPLEVEL.scope_warnings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COMPRESS) {
|
||||||
|
time_it("squeeze", function(){
|
||||||
|
TOPLEVEL = TOPLEVEL.transform(compressor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SCOPE_IS_NEEDED) {
|
||||||
|
time_it("scope", function(){
|
||||||
|
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
|
||||||
|
if (MANGLE) {
|
||||||
|
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MANGLE) time_it("mangle", function(){
|
||||||
|
TOPLEVEL.mangle_names(MANGLE);
|
||||||
});
|
});
|
||||||
});
|
time_it("generate", function(){
|
||||||
|
TOPLEVEL.print(output);
|
||||||
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
|
|
||||||
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ARGS.wrap) {
|
|
||||||
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
|
|
||||||
}
|
|
||||||
|
|
||||||
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
|
|
||||||
|
|
||||||
if (SCOPE_IS_NEEDED) {
|
|
||||||
time_it("scope", function(){
|
|
||||||
TOPLEVEL.figure_out_scope();
|
|
||||||
if (ARGS.lint) {
|
|
||||||
TOPLEVEL.scope_warnings();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (COMPRESS) {
|
output = output.get();
|
||||||
time_it("squeeze", function(){
|
|
||||||
TOPLEVEL = TOPLEVEL.transform(compressor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SCOPE_IS_NEEDED) {
|
if (SOURCE_MAP) {
|
||||||
time_it("scope", function(){
|
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
||||||
TOPLEVEL.figure_out_scope();
|
var source_map_url = ARGS.source_map_url || (
|
||||||
if (MANGLE) {
|
P_RELATIVE
|
||||||
TOPLEVEL.compute_char_frequency(MANGLE);
|
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
|
||||||
}
|
: ARGS.source_map
|
||||||
});
|
);
|
||||||
}
|
output += "\n//# sourceMappingURL=" + source_map_url;
|
||||||
|
}
|
||||||
|
|
||||||
if (MANGLE) time_it("mangle", function(){
|
if (OUTPUT_FILE) {
|
||||||
TOPLEVEL.mangle_names(MANGLE);
|
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||||
});
|
} else {
|
||||||
time_it("generate", function(){
|
sys.print(output);
|
||||||
TOPLEVEL.print(output);
|
sys.error("\n");
|
||||||
});
|
}
|
||||||
|
|
||||||
output = output.get();
|
if (ARGS.stats) {
|
||||||
|
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
||||||
if (SOURCE_MAP) {
|
count: files.length
|
||||||
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
|
||||||
output += "\n//@ sourceMappingURL=" + ARGS.source_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OUTPUT_FILE) {
|
|
||||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
|
||||||
} else {
|
|
||||||
sys.print(output);
|
|
||||||
sys.error("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ARGS.stats) {
|
|
||||||
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
|
||||||
count: files.length
|
|
||||||
}));
|
|
||||||
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
|
|
||||||
sys.error(UglifyJS.string_template("- {name}: {time}s", {
|
|
||||||
name: i,
|
|
||||||
time: (STATS[i] / 1000).toFixed(3)
|
|
||||||
}));
|
}));
|
||||||
|
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
|
||||||
|
sys.error(UglifyJS.string_template("- {name}: {time}s", {
|
||||||
|
name: i,
|
||||||
|
time: (STATS[i] / 1000).toFixed(3)
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
/* -----[ functions ]----- */
|
||||||
|
|
||||||
@@ -306,7 +381,7 @@ function getOptions(x, constants) {
|
|||||||
if (x !== true) {
|
if (x !== true) {
|
||||||
var ast;
|
var ast;
|
||||||
try {
|
try {
|
||||||
ast = UglifyJS.parse(x);
|
ast = UglifyJS.parse(x, { expression: true });
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||||
sys.error("Error parsing arguments in: " + x);
|
sys.error("Error parsing arguments in: " + x);
|
||||||
@@ -314,8 +389,6 @@ function getOptions(x, constants) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.walk(new UglifyJS.TreeWalker(function(node){
|
ast.walk(new UglifyJS.TreeWalker(function(node){
|
||||||
if (node instanceof UglifyJS.AST_Toplevel) return; // descend
|
|
||||||
if (node instanceof UglifyJS.AST_SimpleStatement) return; // descend
|
|
||||||
if (node instanceof UglifyJS.AST_Seq) return; // descend
|
if (node instanceof UglifyJS.AST_Seq) return; // descend
|
||||||
if (node instanceof UglifyJS.AST_Assign) {
|
if (node instanceof UglifyJS.AST_Assign) {
|
||||||
var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_");
|
var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_");
|
||||||
@@ -325,6 +398,11 @@ function getOptions(x, constants) {
|
|||||||
ret[name] = value;
|
ret[name] = value;
|
||||||
return true; // no descend
|
return true; // no descend
|
||||||
}
|
}
|
||||||
|
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
|
||||||
|
var name = node.print_to_string({ beautify: false }).replace(/-/g, "_");
|
||||||
|
ret[name] = true;
|
||||||
|
return true; // no descend
|
||||||
|
}
|
||||||
sys.error(node.TYPE)
|
sys.error(node.TYPE)
|
||||||
sys.error("Error parsing arguments in: " + x);
|
sys.error("Error parsing arguments in: " + x);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -333,17 +411,18 @@ function getOptions(x, constants) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_whole_file(filename) {
|
function read_whole_file(filename, cb) {
|
||||||
if (filename == "-") {
|
if (filename == "-") {
|
||||||
// XXX: this sucks. How does one read the whole STDIN
|
var chunks = [];
|
||||||
// synchronously?
|
process.stdin.setEncoding('utf-8');
|
||||||
filename = "/dev/stdin";
|
process.stdin.on('data', function (chunk) {
|
||||||
}
|
chunks.push(chunk);
|
||||||
try {
|
}).on('end', function () {
|
||||||
return fs.readFileSync(filename, "utf8");
|
cb(null, chunks.join(""));
|
||||||
} catch(ex) {
|
});
|
||||||
sys.error("ERROR: can't read file: " + filename);
|
process.openStdin();
|
||||||
process.exit(1);
|
} else {
|
||||||
|
fs.readFile(filename, "utf-8", cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
lib/ast.js
73
lib/ast.js
@@ -197,6 +197,10 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
|||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_StatementWithBody);
|
||||||
|
|
||||||
|
var AST_IterationStatement = DEFNODE("IterationStatement", null, {
|
||||||
|
$documentation: "Internal class. All loops inherit from it."
|
||||||
|
}, AST_StatementWithBody);
|
||||||
|
|
||||||
var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
||||||
$documentation: "Base class for do/while statements",
|
$documentation: "Base class for do/while statements",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
@@ -208,7 +212,7 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
|||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
var AST_Do = DEFNODE("Do", null, {
|
var AST_Do = DEFNODE("Do", null, {
|
||||||
$documentation: "A `do` statement",
|
$documentation: "A `do` statement",
|
||||||
@@ -233,7 +237,7 @@ var AST_For = DEFNODE("For", "init condition step", {
|
|||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
||||||
$documentation: "A `for ... in` statement",
|
$documentation: "A `for ... in` statement",
|
||||||
@@ -249,7 +253,7 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
|||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
var AST_With = DEFNODE("With", "expression", {
|
var AST_With = DEFNODE("With", "expression", {
|
||||||
$documentation: "A `with` statement",
|
$documentation: "A `with` statement",
|
||||||
@@ -285,11 +289,32 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
$propdoc: {
|
$propdoc: {
|
||||||
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
||||||
},
|
},
|
||||||
|
wrap_enclose: function(arg_parameter_pairs) {
|
||||||
|
var self = this;
|
||||||
|
var args = [];
|
||||||
|
var parameters = [];
|
||||||
|
|
||||||
|
arg_parameter_pairs.forEach(function(pair) {
|
||||||
|
var split = pair.split(":");
|
||||||
|
|
||||||
|
args.push(split[0]);
|
||||||
|
parameters.push(split[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
||||||
|
wrapped_tl = parse(wrapped_tl);
|
||||||
|
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
|
||||||
|
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||||
|
return MAP.splice(self.body);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return wrapped_tl;
|
||||||
|
},
|
||||||
wrap_commonjs: function(name, export_all) {
|
wrap_commonjs: function(name, export_all) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var to_export = [];
|
||||||
if (export_all) {
|
if (export_all) {
|
||||||
self.figure_out_scope();
|
self.figure_out_scope();
|
||||||
var to_export = [];
|
|
||||||
self.walk(new TreeWalker(function(node){
|
self.walk(new TreeWalker(function(node){
|
||||||
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
|
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
|
||||||
if (!find_if(function(n){ return n.name == node.name }, to_export))
|
if (!find_if(function(n){ return n.name == node.name }, to_export))
|
||||||
@@ -346,7 +371,7 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
|||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Accessor = DEFNODE("Accessor", null, {
|
var AST_Accessor = DEFNODE("Accessor", null, {
|
||||||
$documentation: "A setter/getter function"
|
$documentation: "A setter/getter function. The `name` property is always null."
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
|
|
||||||
var AST_Function = DEFNODE("Function", null, {
|
var AST_Function = DEFNODE("Function", null, {
|
||||||
@@ -731,7 +756,7 @@ var AST_Object = DEFNODE("Object", "properties", {
|
|||||||
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||||
$documentation: "Base class for literal object properties",
|
$documentation: "Base class for literal object properties",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
key: "[string] the property name; it's always a plain string in our AST, no matter if it was a string, number or identifier in original code",
|
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.",
|
||||||
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
|
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
|
||||||
},
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
@@ -800,7 +825,11 @@ var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
|||||||
var AST_Label = DEFNODE("Label", "references", {
|
var AST_Label = DEFNODE("Label", "references", {
|
||||||
$documentation: "Symbol naming a label (declaration)",
|
$documentation: "Symbol naming a label (declaration)",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
references: "[AST_LabelRef*] a list of nodes referring to this label"
|
references: "[AST_LoopControl*] a list of nodes referring to this label"
|
||||||
|
},
|
||||||
|
initialize: function() {
|
||||||
|
this.references = [];
|
||||||
|
this.thedef = this;
|
||||||
}
|
}
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
@@ -863,6 +892,11 @@ var AST_Undefined = DEFNODE("Undefined", null, {
|
|||||||
value: (function(){}())
|
value: (function(){}())
|
||||||
}, AST_Atom);
|
}, AST_Atom);
|
||||||
|
|
||||||
|
var AST_Hole = DEFNODE("Hole", null, {
|
||||||
|
$documentation: "A hole in an array",
|
||||||
|
value: (function(){}())
|
||||||
|
}, AST_Atom);
|
||||||
|
|
||||||
var AST_Infinity = DEFNODE("Infinity", null, {
|
var AST_Infinity = DEFNODE("Infinity", null, {
|
||||||
$documentation: "The `Infinity` value",
|
$documentation: "The `Infinity` value",
|
||||||
value: 1/0
|
value: 1/0
|
||||||
@@ -919,6 +953,9 @@ TreeWalker.prototype = {
|
|||||||
if (x instanceof type) return x;
|
if (x instanceof type) return x;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
has_directive: function(type) {
|
||||||
|
return this.find_parent(AST_Scope).has_directive(type);
|
||||||
|
},
|
||||||
in_boolean_context: function() {
|
in_boolean_context: function() {
|
||||||
var stack = this.stack;
|
var stack = this.stack;
|
||||||
var i = stack.length, self = stack[--i];
|
var i = stack.length, self = stack[--i];
|
||||||
@@ -939,21 +976,15 @@ TreeWalker.prototype = {
|
|||||||
},
|
},
|
||||||
loopcontrol_target: function(label) {
|
loopcontrol_target: function(label) {
|
||||||
var stack = this.stack;
|
var stack = this.stack;
|
||||||
if (label) {
|
if (label) for (var i = stack.length; --i >= 0;) {
|
||||||
for (var i = stack.length; --i >= 0;) {
|
var x = stack[i];
|
||||||
var x = stack[i];
|
if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
|
||||||
if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
|
return x.body;
|
||||||
return x.body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (var i = stack.length; --i >= 0;) {
|
|
||||||
var x = stack[i];
|
|
||||||
if (x instanceof AST_Switch
|
|
||||||
|| x instanceof AST_For
|
|
||||||
|| x instanceof AST_ForIn
|
|
||||||
|| x instanceof AST_DWLoop) return x;
|
|
||||||
}
|
}
|
||||||
|
} else for (var i = stack.length; --i >= 0;) {
|
||||||
|
var x = stack[i];
|
||||||
|
if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
700
lib/compress.js
700
lib/compress.js
@@ -52,7 +52,7 @@ function Compressor(options, false_by_default) {
|
|||||||
properties : !false_by_default,
|
properties : !false_by_default,
|
||||||
dead_code : !false_by_default,
|
dead_code : !false_by_default,
|
||||||
drop_debugger : !false_by_default,
|
drop_debugger : !false_by_default,
|
||||||
unsafe : !false_by_default,
|
unsafe : false,
|
||||||
unsafe_comps : false,
|
unsafe_comps : false,
|
||||||
conditionals : !false_by_default,
|
conditionals : !false_by_default,
|
||||||
comparisons : !false_by_default,
|
comparisons : !false_by_default,
|
||||||
@@ -66,6 +66,10 @@ function Compressor(options, false_by_default) {
|
|||||||
join_vars : !false_by_default,
|
join_vars : !false_by_default,
|
||||||
cascade : !false_by_default,
|
cascade : !false_by_default,
|
||||||
side_effects : !false_by_default,
|
side_effects : !false_by_default,
|
||||||
|
pure_getters : false,
|
||||||
|
pure_funcs : null,
|
||||||
|
negate_iife : !false_by_default,
|
||||||
|
screw_ie8 : false,
|
||||||
|
|
||||||
warnings : true,
|
warnings : true,
|
||||||
global_defs : {}
|
global_defs : {}
|
||||||
@@ -81,22 +85,16 @@ merge(Compressor.prototype, {
|
|||||||
},
|
},
|
||||||
before: function(node, descend, in_list) {
|
before: function(node, descend, in_list) {
|
||||||
if (node._squeezed) return node;
|
if (node._squeezed) return node;
|
||||||
|
var was_scope = false;
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
node.drop_unused(this);
|
|
||||||
node = node.hoist_declarations(this);
|
node = node.hoist_declarations(this);
|
||||||
|
was_scope = true;
|
||||||
}
|
}
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
node = node.optimize(this);
|
node = node.optimize(this);
|
||||||
if (node instanceof AST_Scope) {
|
if (was_scope && node instanceof AST_Scope) {
|
||||||
// dead code removal might leave further unused declarations.
|
|
||||||
// this'll usually save very few bytes, but the performance
|
|
||||||
// hit seems negligible so I'll just drop it here.
|
|
||||||
|
|
||||||
// no point to repeat warnings.
|
|
||||||
var save_warnings = this.options.warnings;
|
|
||||||
this.options.warnings = false;
|
|
||||||
node.drop_unused(this);
|
node.drop_unused(this);
|
||||||
this.options.warnings = save_warnings;
|
descend(node, this);
|
||||||
}
|
}
|
||||||
node._squeezed = true;
|
node._squeezed = true;
|
||||||
return node;
|
return node;
|
||||||
@@ -156,7 +154,7 @@ merge(Compressor.prototype, {
|
|||||||
value: val
|
value: val
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return make_node(val ? AST_True : AST_False, orig);
|
return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
|
||||||
case "undefined":
|
case "undefined":
|
||||||
return make_node(AST_Undefined, orig).optimize(compressor);
|
return make_node(AST_Undefined, orig).optimize(compressor);
|
||||||
default:
|
default:
|
||||||
@@ -213,6 +211,11 @@ merge(Compressor.prototype, {
|
|||||||
statements = join_consecutive_vars(statements, compressor);
|
statements = join_consecutive_vars(statements, compressor);
|
||||||
}
|
}
|
||||||
} while (CHANGED);
|
} while (CHANGED);
|
||||||
|
|
||||||
|
if (compressor.option("negate_iife")) {
|
||||||
|
negate_iifes(statements, compressor);
|
||||||
|
}
|
||||||
|
|
||||||
return statements;
|
return statements;
|
||||||
|
|
||||||
function eliminate_spurious_blocks(statements) {
|
function eliminate_spurious_blocks(statements) {
|
||||||
@@ -317,7 +320,7 @@ merge(Compressor.prototype, {
|
|||||||
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||||
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||||
if (ab.label) {
|
if (ab.label) {
|
||||||
remove(ab.label.thedef.references, ab.label);
|
remove(ab.label.thedef.references, ab);
|
||||||
}
|
}
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
var body = as_statement_array(stat.body).slice(0, -1);
|
var body = as_statement_array(stat.body).slice(0, -1);
|
||||||
@@ -339,7 +342,7 @@ merge(Compressor.prototype, {
|
|||||||
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||||
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||||
if (ab.label) {
|
if (ab.label) {
|
||||||
remove(ab.label.thedef.references, ab.label);
|
remove(ab.label.thedef.references, ab);
|
||||||
}
|
}
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
@@ -378,7 +381,7 @@ merge(Compressor.prototype, {
|
|||||||
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
||||||
&& loop_body(lct) === self)) {
|
&& loop_body(lct) === self)) {
|
||||||
if (stat.label) {
|
if (stat.label) {
|
||||||
remove(stat.label.thedef.references, stat.label);
|
remove(stat.label.thedef.references, stat);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
a.push(stat);
|
a.push(stat);
|
||||||
@@ -496,6 +499,40 @@ merge(Compressor.prototype, {
|
|||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function negate_iifes(statements, compressor) {
|
||||||
|
statements.forEach(function(stat){
|
||||||
|
if (stat instanceof AST_SimpleStatement) {
|
||||||
|
stat.body = (function transform(thing) {
|
||||||
|
return thing.transform(new TreeTransformer(function(node){
|
||||||
|
if (node instanceof AST_Call && node.expression instanceof AST_Function) {
|
||||||
|
return make_node(AST_UnaryPrefix, node, {
|
||||||
|
operator: "!",
|
||||||
|
expression: node
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Call) {
|
||||||
|
node.expression = transform(node.expression);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Seq) {
|
||||||
|
node.car = transform(node.car);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Conditional) {
|
||||||
|
var expr = transform(node.condition);
|
||||||
|
if (expr !== node.condition) {
|
||||||
|
// it has been negated, reverse
|
||||||
|
node.condition = expr;
|
||||||
|
var tmp = node.consequent;
|
||||||
|
node.consequent = node.alternative;
|
||||||
|
node.alternative = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}));
|
||||||
|
})(stat.body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
||||||
@@ -589,14 +626,14 @@ merge(Compressor.prototype, {
|
|||||||
// elements. If the node has been successfully reduced to a
|
// elements. If the node has been successfully reduced to a
|
||||||
// constant, then the second element tells us the value;
|
// constant, then the second element tells us the value;
|
||||||
// otherwise the second element is missing. The first element
|
// otherwise the second element is missing. The first element
|
||||||
// of the array is always an AST_Node descendant; when
|
// of the array is always an AST_Node descendant; if
|
||||||
// evaluation was successful it's a node that represents the
|
// evaluation was successful it's a node that represents the
|
||||||
// constant; otherwise it's the original node.
|
// constant; otherwise it's the original or a replacement node.
|
||||||
AST_Node.DEFMETHOD("evaluate", function(compressor){
|
AST_Node.DEFMETHOD("evaluate", function(compressor){
|
||||||
if (!compressor.option("evaluate")) return [ this ];
|
if (!compressor.option("evaluate")) return [ this ];
|
||||||
try {
|
try {
|
||||||
var val = this._eval(), ast = make_node_from_constant(compressor, val, this);
|
var val = this._eval(compressor);
|
||||||
return [ best_of(ast, this), val ];
|
return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
if (ex !== def) throw ex;
|
if (ex !== def) throw ex;
|
||||||
return [ this ];
|
return [ this ];
|
||||||
@@ -610,10 +647,12 @@ merge(Compressor.prototype, {
|
|||||||
// inherits from AST_Statement; however, an AST_Function
|
// inherits from AST_Statement; however, an AST_Function
|
||||||
// isn't really a statement. This could byte in other
|
// isn't really a statement. This could byte in other
|
||||||
// places too. :-( Wish JS had multiple inheritance.
|
// places too. :-( Wish JS had multiple inheritance.
|
||||||
return [ this ];
|
throw def;
|
||||||
});
|
});
|
||||||
function ev(node) {
|
function ev(node, compressor) {
|
||||||
return node._eval();
|
if (!compressor) throw new Error("Compressor must be passed");
|
||||||
|
|
||||||
|
return node._eval(compressor);
|
||||||
};
|
};
|
||||||
def(AST_Node, function(){
|
def(AST_Node, function(){
|
||||||
throw def; // not constant
|
throw def; // not constant
|
||||||
@@ -621,60 +660,69 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Constant, function(){
|
def(AST_Constant, function(){
|
||||||
return this.getValue();
|
return this.getValue();
|
||||||
});
|
});
|
||||||
def(AST_UnaryPrefix, function(){
|
def(AST_UnaryPrefix, function(compressor){
|
||||||
var e = this.expression;
|
var e = this.expression;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
case "!": return !ev(e);
|
case "!": return !ev(e, compressor);
|
||||||
case "typeof": return typeof ev(e);
|
case "typeof":
|
||||||
case "void": return void ev(e);
|
// Function would be evaluated to an array and so typeof would
|
||||||
case "~": return ~ev(e);
|
// incorrectly return 'object'. Hence making is a special case.
|
||||||
case "-": return -ev(e);
|
if (e instanceof AST_Function) return typeof function(){};
|
||||||
case "+": return +ev(e);
|
|
||||||
|
e = ev(e, compressor);
|
||||||
|
|
||||||
|
// typeof <RegExp> returns "object" or "function" on different platforms
|
||||||
|
// so cannot evaluate reliably
|
||||||
|
if (e instanceof RegExp) throw def;
|
||||||
|
|
||||||
|
return typeof e;
|
||||||
|
case "void": return void ev(e, compressor);
|
||||||
|
case "~": return ~ev(e, compressor);
|
||||||
|
case "-":
|
||||||
|
e = ev(e, compressor);
|
||||||
|
if (e === 0) throw def;
|
||||||
|
return -e;
|
||||||
|
case "+": return +ev(e, compressor);
|
||||||
}
|
}
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
def(AST_Binary, function(){
|
def(AST_Binary, function(c){
|
||||||
var left = this.left, right = this.right;
|
var left = this.left, right = this.right;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
case "&&" : return ev(left) && ev(right);
|
case "&&" : return ev(left, c) && ev(right, c);
|
||||||
case "||" : return ev(left) || ev(right);
|
case "||" : return ev(left, c) || ev(right, c);
|
||||||
case "|" : return ev(left) | ev(right);
|
case "|" : return ev(left, c) | ev(right, c);
|
||||||
case "&" : return ev(left) & ev(right);
|
case "&" : return ev(left, c) & ev(right, c);
|
||||||
case "^" : return ev(left) ^ ev(right);
|
case "^" : return ev(left, c) ^ ev(right, c);
|
||||||
case "+" : return ev(left) + ev(right);
|
case "+" : return ev(left, c) + ev(right, c);
|
||||||
case "*" : return ev(left) * ev(right);
|
case "*" : return ev(left, c) * ev(right, c);
|
||||||
case "/" : return ev(left) / ev(right);
|
case "/" : return ev(left, c) / ev(right, c);
|
||||||
case "%" : return ev(left) % ev(right);
|
case "%" : return ev(left, c) % ev(right, c);
|
||||||
case "-" : return ev(left) - ev(right);
|
case "-" : return ev(left, c) - ev(right, c);
|
||||||
case "<<" : return ev(left) << ev(right);
|
case "<<" : return ev(left, c) << ev(right, c);
|
||||||
case ">>" : return ev(left) >> ev(right);
|
case ">>" : return ev(left, c) >> ev(right, c);
|
||||||
case ">>>" : return ev(left) >>> ev(right);
|
case ">>>" : return ev(left, c) >>> ev(right, c);
|
||||||
case "==" : return ev(left) == ev(right);
|
case "==" : return ev(left, c) == ev(right, c);
|
||||||
case "===" : return ev(left) === ev(right);
|
case "===" : return ev(left, c) === ev(right, c);
|
||||||
case "!=" : return ev(left) != ev(right);
|
case "!=" : return ev(left, c) != ev(right, c);
|
||||||
case "!==" : return ev(left) !== ev(right);
|
case "!==" : return ev(left, c) !== ev(right, c);
|
||||||
case "<" : return ev(left) < ev(right);
|
case "<" : return ev(left, c) < ev(right, c);
|
||||||
case "<=" : return ev(left) <= ev(right);
|
case "<=" : return ev(left, c) <= ev(right, c);
|
||||||
case ">" : return ev(left) > ev(right);
|
case ">" : return ev(left, c) > ev(right, c);
|
||||||
case ">=" : return ev(left) >= ev(right);
|
case ">=" : return ev(left, c) >= ev(right, c);
|
||||||
case "in" : return ev(left) in ev(right);
|
case "in" : return ev(left, c) in ev(right, c);
|
||||||
case "instanceof" : return ev(left) instanceof ev(right);
|
case "instanceof" : return ev(left, c) instanceof ev(right, c);
|
||||||
}
|
}
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
def(AST_Conditional, function(){
|
def(AST_Conditional, function(compressor){
|
||||||
return ev(this.condition)
|
return ev(this.condition, compressor)
|
||||||
? ev(this.consequent)
|
? ev(this.consequent, compressor)
|
||||||
: ev(this.alternative);
|
: ev(this.alternative, compressor);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(){
|
def(AST_SymbolRef, function(compressor){
|
||||||
var d = this.definition();
|
var d = this.definition();
|
||||||
if (d && d.constant) {
|
if (d && d.constant && d.init) return ev(d.init, compressor);
|
||||||
var orig = d.orig[0];
|
|
||||||
if (orig) orig = orig.init[0];
|
|
||||||
orig = orig && orig.value;
|
|
||||||
if (orig) return ev(orig);
|
|
||||||
}
|
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
@@ -750,70 +798,78 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
// determine if expression has side effects
|
// determine if expression has side effects
|
||||||
(function(def){
|
(function(def){
|
||||||
def(AST_Node, function(){ return true });
|
def(AST_Node, function(compressor){ return true });
|
||||||
|
|
||||||
def(AST_EmptyStatement, function(){ return false });
|
def(AST_EmptyStatement, function(compressor){ return false });
|
||||||
def(AST_Constant, function(){ return false });
|
def(AST_Constant, function(compressor){ return false });
|
||||||
def(AST_This, function(){ return false });
|
def(AST_This, function(compressor){ return false });
|
||||||
|
|
||||||
def(AST_Block, function(){
|
def(AST_Call, function(compressor){
|
||||||
|
var pure = compressor.option("pure_funcs");
|
||||||
|
if (!pure) return true;
|
||||||
|
return pure.indexOf(this.expression.print_to_string()) < 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
def(AST_Block, function(compressor){
|
||||||
for (var i = this.body.length; --i >= 0;) {
|
for (var i = this.body.length; --i >= 0;) {
|
||||||
if (this.body[i].has_side_effects())
|
if (this.body[i].has_side_effects(compressor))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
def(AST_SimpleStatement, function(){
|
def(AST_SimpleStatement, function(compressor){
|
||||||
return this.body.has_side_effects();
|
return this.body.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Defun, function(){ return true });
|
def(AST_Defun, function(compressor){ return true });
|
||||||
def(AST_Function, function(){ return false });
|
def(AST_Function, function(compressor){ return false });
|
||||||
def(AST_Binary, function(){
|
def(AST_Binary, function(compressor){
|
||||||
return this.left.has_side_effects()
|
return this.left.has_side_effects(compressor)
|
||||||
|| this.right.has_side_effects();
|
|| this.right.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Assign, function(){ return true });
|
def(AST_Assign, function(compressor){ return true });
|
||||||
def(AST_Conditional, function(){
|
def(AST_Conditional, function(compressor){
|
||||||
return this.condition.has_side_effects()
|
return this.condition.has_side_effects(compressor)
|
||||||
|| this.consequent.has_side_effects()
|
|| this.consequent.has_side_effects(compressor)
|
||||||
|| this.alternative.has_side_effects();
|
|| this.alternative.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Unary, function(){
|
def(AST_Unary, function(compressor){
|
||||||
return this.operator == "delete"
|
return this.operator == "delete"
|
||||||
|| this.operator == "++"
|
|| this.operator == "++"
|
||||||
|| this.operator == "--"
|
|| this.operator == "--"
|
||||||
|| this.expression.has_side_effects();
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(){ return false });
|
def(AST_SymbolRef, function(compressor){ return false });
|
||||||
def(AST_Object, function(){
|
def(AST_Object, function(compressor){
|
||||||
for (var i = this.properties.length; --i >= 0;)
|
for (var i = this.properties.length; --i >= 0;)
|
||||||
if (this.properties[i].has_side_effects())
|
if (this.properties[i].has_side_effects(compressor))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
def(AST_ObjectProperty, function(){
|
def(AST_ObjectProperty, function(compressor){
|
||||||
return this.value.has_side_effects();
|
return this.value.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Array, function(){
|
def(AST_Array, function(compressor){
|
||||||
for (var i = this.elements.length; --i >= 0;)
|
for (var i = this.elements.length; --i >= 0;)
|
||||||
if (this.elements[i].has_side_effects())
|
if (this.elements[i].has_side_effects(compressor))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
// def(AST_Dot, function(){
|
def(AST_Dot, function(compressor){
|
||||||
// return this.expression.has_side_effects();
|
if (!compressor.option("pure_getters")) return true;
|
||||||
// });
|
return this.expression.has_side_effects(compressor);
|
||||||
// def(AST_Sub, function(){
|
|
||||||
// return this.expression.has_side_effects()
|
|
||||||
// || this.property.has_side_effects();
|
|
||||||
// });
|
|
||||||
def(AST_PropAccess, function(){
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
def(AST_Seq, function(){
|
def(AST_Sub, function(compressor){
|
||||||
return this.car.has_side_effects()
|
if (!compressor.option("pure_getters")) return true;
|
||||||
|| this.cdr.has_side_effects();
|
return this.expression.has_side_effects(compressor)
|
||||||
|
|| this.property.has_side_effects(compressor);
|
||||||
|
});
|
||||||
|
def(AST_PropAccess, function(compressor){
|
||||||
|
return !compressor.option("pure_getters");
|
||||||
|
});
|
||||||
|
def(AST_Seq, function(compressor){
|
||||||
|
return this.car.has_side_effects(compressor)
|
||||||
|
|| this.cdr.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
node.DEFMETHOD("has_side_effects", func);
|
node.DEFMETHOD("has_side_effects", func);
|
||||||
@@ -883,18 +939,23 @@ merge(Compressor.prototype, {
|
|||||||
&& !self.uses_eval
|
&& !self.uses_eval
|
||||||
) {
|
) {
|
||||||
var in_use = [];
|
var in_use = [];
|
||||||
|
var initializations = new Dictionary();
|
||||||
// pass 1: find out which symbols are directly used in
|
// pass 1: find out which symbols are directly used in
|
||||||
// this scope (not in nested scopes).
|
// this scope (not in nested scopes).
|
||||||
var scope = this;
|
var scope = this;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node !== self) {
|
if (node !== self) {
|
||||||
if (node instanceof AST_Defun) {
|
if (node instanceof AST_Defun) {
|
||||||
|
initializations.add(node.name.name, node);
|
||||||
return true; // don't go in nested scopes
|
return true; // don't go in nested scopes
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Definitions && scope === self) {
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
if (def.value && def.value.has_side_effects()) {
|
if (def.value) {
|
||||||
def.value.walk(tw);
|
initializations.add(def.name.name, def.value);
|
||||||
|
if (def.value.has_side_effects(compressor)) {
|
||||||
|
def.value.walk(tw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -919,22 +980,21 @@ merge(Compressor.prototype, {
|
|||||||
for (var i = 0; i < in_use.length; ++i) {
|
for (var i = 0; i < in_use.length; ++i) {
|
||||||
in_use[i].orig.forEach(function(decl){
|
in_use[i].orig.forEach(function(decl){
|
||||||
// undeclared globals will be instanceof AST_SymbolRef
|
// undeclared globals will be instanceof AST_SymbolRef
|
||||||
if (decl instanceof AST_SymbolDeclaration) {
|
var init = initializations.get(decl.name);
|
||||||
decl.init.forEach(function(init){
|
if (init) init.forEach(function(init){
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
push_uniq(in_use, node.definition());
|
push_uniq(in_use, node.definition());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
init.walk(tw);
|
|
||||||
});
|
});
|
||||||
}
|
init.walk(tw);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 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_Lambda) {
|
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
var sym = a[i];
|
var sym = a[i];
|
||||||
if (sym.unreferenced()) {
|
if (sym.unreferenced()) {
|
||||||
@@ -970,7 +1030,7 @@ merge(Compressor.prototype, {
|
|||||||
line : def.name.start.line,
|
line : def.name.start.line,
|
||||||
col : def.name.start.col
|
col : def.name.start.col
|
||||||
};
|
};
|
||||||
if (def.value && def.value.has_side_effects()) {
|
if (def.value && def.value.has_side_effects(compressor)) {
|
||||||
def._unused_side_effects = true;
|
def._unused_side_effects = true;
|
||||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return true;
|
return true;
|
||||||
@@ -1024,18 +1084,23 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_For && node.init instanceof AST_BlockStatement) {
|
if (node instanceof AST_For) {
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
// certain combination of unused name + side effect leads to:
|
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/44
|
if (node.init instanceof AST_BlockStatement) {
|
||||||
// that's an invalid AST.
|
// certain combination of unused name + side effect leads to:
|
||||||
// We fix it at this stage by moving the `var` outside the `for`.
|
// https://github.com/mishoo/UglifyJS2/issues/44
|
||||||
var body = node.init.body.slice(0, -1);
|
// that's an invalid AST.
|
||||||
node.init = node.init.body.slice(-1)[0].body;
|
// We fix it at this stage by moving the `var` outside the `for`.
|
||||||
body.push(node);
|
|
||||||
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
var body = node.init.body.slice(0, -1);
|
||||||
body: body
|
node.init = node.init.body.slice(-1)[0].body;
|
||||||
});
|
body.push(node);
|
||||||
|
|
||||||
|
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== self)
|
if (node instanceof AST_Scope && node !== self)
|
||||||
return node;
|
return node;
|
||||||
@@ -1100,13 +1165,71 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
self = self.transform(tt);
|
self = self.transform(tt);
|
||||||
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
if (vars_found > 0) {
|
||||||
definitions: vars.map(function(def){
|
// collect only vars which don't show up in self's arguments list
|
||||||
def = def.clone();
|
var defs = [];
|
||||||
def.value = null;
|
vars.each(function(def, name){
|
||||||
return def;
|
if (self instanceof AST_Lambda
|
||||||
})
|
&& find_if(function(x){ return x.name == def.name.name },
|
||||||
}));
|
self.argnames)) {
|
||||||
|
vars.del(name);
|
||||||
|
} else {
|
||||||
|
def = def.clone();
|
||||||
|
def.value = null;
|
||||||
|
defs.push(def);
|
||||||
|
vars.set(name, def);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (defs.length > 0) {
|
||||||
|
// try to merge in assignments
|
||||||
|
for (var i = 0; i < self.body.length;) {
|
||||||
|
if (self.body[i] instanceof AST_SimpleStatement) {
|
||||||
|
var expr = self.body[i].body, sym, assign;
|
||||||
|
if (expr instanceof AST_Assign
|
||||||
|
&& expr.operator == "="
|
||||||
|
&& (sym = expr.left) instanceof AST_Symbol
|
||||||
|
&& vars.has(sym.name))
|
||||||
|
{
|
||||||
|
var def = vars.get(sym.name);
|
||||||
|
if (def.value) break;
|
||||||
|
def.value = expr.right;
|
||||||
|
remove(defs, def);
|
||||||
|
defs.push(def);
|
||||||
|
self.body.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (expr instanceof AST_Seq
|
||||||
|
&& (assign = expr.car) instanceof AST_Assign
|
||||||
|
&& assign.operator == "="
|
||||||
|
&& (sym = assign.left) instanceof AST_Symbol
|
||||||
|
&& vars.has(sym.name))
|
||||||
|
{
|
||||||
|
var def = vars.get(sym.name);
|
||||||
|
if (def.value) break;
|
||||||
|
def.value = assign.right;
|
||||||
|
remove(defs, def);
|
||||||
|
defs.push(def);
|
||||||
|
self.body[i].body = expr.cdr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self.body[i] instanceof AST_EmptyStatement) {
|
||||||
|
self.body.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (self.body[i] instanceof AST_BlockStatement) {
|
||||||
|
var tmp = [ i, 1 ].concat(self.body[i].body);
|
||||||
|
self.body.splice.apply(self.body, tmp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
defs = make_node(AST_Var, self, {
|
||||||
|
definitions: defs
|
||||||
|
});
|
||||||
|
hoisted.push(defs);
|
||||||
|
};
|
||||||
|
}
|
||||||
self.body = dirs.concat(hoisted, self.body);
|
self.body = dirs.concat(hoisted, self.body);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -1114,7 +1237,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_SimpleStatement, function(self, compressor){
|
OPT(AST_SimpleStatement, function(self, compressor){
|
||||||
if (compressor.option("side_effects")) {
|
if (compressor.option("side_effects")) {
|
||||||
if (!self.body.has_side_effects()) {
|
if (!self.body.has_side_effects(compressor)) {
|
||||||
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_EmptyStatement, self);
|
return make_node(AST_EmptyStatement, self);
|
||||||
}
|
}
|
||||||
@@ -1137,8 +1260,6 @@ merge(Compressor.prototype, {
|
|||||||
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
||||||
return make_node(AST_BlockStatement, self, { body: a });
|
return make_node(AST_BlockStatement, self, { body: a });
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return self.body;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -1356,11 +1477,18 @@ merge(Compressor.prototype, {
|
|||||||
body: self.expression
|
body: self.expression
|
||||||
}).transform(compressor);
|
}).transform(compressor);
|
||||||
}
|
}
|
||||||
var last_branch = self.body[self.body.length - 1];
|
for(;;) {
|
||||||
if (last_branch) {
|
var last_branch = self.body[self.body.length - 1];
|
||||||
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
if (last_branch) {
|
||||||
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
||||||
last_branch.body.pop();
|
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
||||||
|
last_branch.body.pop();
|
||||||
|
if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
|
||||||
|
self.body.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
var exp = self.expression.evaluate(compressor);
|
var exp = self.expression.evaluate(compressor);
|
||||||
out: if (exp.length == 2) try {
|
out: if (exp.length == 2) try {
|
||||||
@@ -1430,7 +1558,7 @@ merge(Compressor.prototype, {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tt.stack = compressor.stack; // so that's able to see parent nodes
|
tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
|
||||||
self = self.transform(tt);
|
self = self.transform(tt);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
if (ex !== self) throw ex;
|
if (ex !== self) throw ex;
|
||||||
@@ -1493,7 +1621,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.args.length != 1) {
|
if (self.args.length != 1) {
|
||||||
return make_node(AST_Array, self, {
|
return make_node(AST_Array, self, {
|
||||||
elements: self.args
|
elements: self.args
|
||||||
});
|
}).transform(compressor);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "Object":
|
case "Object":
|
||||||
@@ -1504,11 +1632,83 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "String":
|
case "String":
|
||||||
return make_node(AST_Binary, self, {
|
if (self.args.length == 0) return make_node(AST_String, self, {
|
||||||
|
value: ""
|
||||||
|
});
|
||||||
|
if (self.args.length <= 1) return make_node(AST_Binary, self, {
|
||||||
left: self.args[0],
|
left: self.args[0],
|
||||||
operator: "+",
|
operator: "+",
|
||||||
right: make_node(AST_String, self, { value: "" })
|
right: make_node(AST_String, self, { value: "" })
|
||||||
|
}).transform(compressor);
|
||||||
|
break;
|
||||||
|
case "Number":
|
||||||
|
if (self.args.length == 0) return make_node(AST_Number, self, {
|
||||||
|
value: 0
|
||||||
});
|
});
|
||||||
|
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
||||||
|
expression: self.args[0],
|
||||||
|
operator: "+"
|
||||||
|
}).transform(compressor);
|
||||||
|
case "Boolean":
|
||||||
|
if (self.args.length == 0) return make_node(AST_False, self);
|
||||||
|
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
||||||
|
expression: make_node(AST_UnaryPrefix, null, {
|
||||||
|
expression: self.args[0],
|
||||||
|
operator: "!"
|
||||||
|
}),
|
||||||
|
operator: "!"
|
||||||
|
}).transform(compressor);
|
||||||
|
break;
|
||||||
|
case "Function":
|
||||||
|
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);
|
||||||
|
ast.figure_out_scope();
|
||||||
|
var comp = new Compressor(compressor.options);
|
||||||
|
ast = ast.transform(comp);
|
||||||
|
ast.figure_out_scope();
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
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 {
|
||||||
|
console.log(ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||||
@@ -1518,15 +1718,62 @@ merge(Compressor.prototype, {
|
|||||||
right: exp.expression
|
right: exp.expression
|
||||||
}).transform(compressor);
|
}).transform(compressor);
|
||||||
}
|
}
|
||||||
|
else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
|
||||||
|
var separator = self.args.length == 0 ? "," : self.args[0].evaluate(compressor)[1];
|
||||||
|
if (separator == null) break EXIT; // not a constant
|
||||||
|
var elements = exp.expression.elements.reduce(function(a, el){
|
||||||
|
el = el.evaluate(compressor);
|
||||||
|
if (a.length == 0 || el.length == 1) {
|
||||||
|
a.push(el);
|
||||||
|
} else {
|
||||||
|
var last = a[a.length - 1];
|
||||||
|
if (last.length == 2) {
|
||||||
|
// it's a constant
|
||||||
|
var val = "" + last[1] + separator + el[1];
|
||||||
|
a[a.length - 1] = [ make_node_from_constant(compressor, val, last[0]), val ];
|
||||||
|
} else {
|
||||||
|
a.push(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}, []);
|
||||||
|
if (elements.length == 0) return make_node(AST_String, self, { value: "" });
|
||||||
|
if (elements.length == 1) return elements[0][0];
|
||||||
|
if (separator == "") {
|
||||||
|
var first;
|
||||||
|
if (elements[0][0] instanceof AST_String
|
||||||
|
|| elements[1][0] instanceof AST_String) {
|
||||||
|
first = elements.shift()[0];
|
||||||
|
} else {
|
||||||
|
first = make_node(AST_String, self, { value: "" });
|
||||||
|
}
|
||||||
|
return elements.reduce(function(prev, el){
|
||||||
|
return make_node(AST_Binary, el[0], {
|
||||||
|
operator : "+",
|
||||||
|
left : prev,
|
||||||
|
right : el[0],
|
||||||
|
});
|
||||||
|
}, first).transform(compressor);
|
||||||
|
}
|
||||||
|
// need this awkward cloning to not affect original element
|
||||||
|
// best_of will decide which one to get through.
|
||||||
|
var node = self.clone();
|
||||||
|
node.expression = node.expression.clone();
|
||||||
|
node.expression.expression = node.expression.expression.clone();
|
||||||
|
node.expression.expression.elements = elements.map(function(el){
|
||||||
|
return el[0];
|
||||||
|
});
|
||||||
|
return best_of(self, node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (compressor.option("side_effects")) {
|
if (compressor.option("side_effects")) {
|
||||||
if (self.expression instanceof AST_Function
|
if (self.expression instanceof AST_Function
|
||||||
&& self.args.length == 0
|
&& self.args.length == 0
|
||||||
&& !AST_Block.prototype.has_side_effects.call(self.expression)) {
|
&& !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
|
||||||
return make_node(AST_Undefined, self).transform(compressor);
|
return make_node(AST_Undefined, self).transform(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self.evaluate(compressor)[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_New, function(self, compressor){
|
OPT(AST_New, function(self, compressor){
|
||||||
@@ -1539,7 +1786,7 @@ merge(Compressor.prototype, {
|
|||||||
case "Function":
|
case "Function":
|
||||||
case "Error":
|
case "Error":
|
||||||
case "Array":
|
case "Array":
|
||||||
return make_node(AST_Call, self, self);
|
return make_node(AST_Call, self, self).transform(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1549,7 +1796,7 @@ merge(Compressor.prototype, {
|
|||||||
OPT(AST_Seq, function(self, compressor){
|
OPT(AST_Seq, function(self, compressor){
|
||||||
if (!compressor.option("side_effects"))
|
if (!compressor.option("side_effects"))
|
||||||
return self;
|
return self;
|
||||||
if (!self.car.has_side_effects()) {
|
if (!self.car.has_side_effects(compressor)) {
|
||||||
// we shouldn't compress (1,eval)(something) to
|
// we shouldn't compress (1,eval)(something) to
|
||||||
// eval(something) because that changes the meaning of
|
// eval(something) because that changes the meaning of
|
||||||
// eval (becomes lexical instead of global).
|
// eval (becomes lexical instead of global).
|
||||||
@@ -1564,12 +1811,12 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (compressor.option("cascade")) {
|
if (compressor.option("cascade")) {
|
||||||
if (self.car instanceof AST_Assign
|
if (self.car instanceof AST_Assign
|
||||||
&& !self.car.left.has_side_effects()
|
&& !self.car.left.has_side_effects(compressor)
|
||||||
&& self.car.left.equivalent_to(self.cdr)) {
|
&& self.car.left.equivalent_to(self.cdr)) {
|
||||||
return self.car;
|
return self.car;
|
||||||
}
|
}
|
||||||
if (!self.car.has_side_effects()
|
if (!self.car.has_side_effects(compressor)
|
||||||
&& !self.cdr.has_side_effects()
|
&& !self.cdr.has_side_effects(compressor)
|
||||||
&& self.car.equivalent_to(self.cdr)) {
|
&& self.car.equivalent_to(self.cdr)) {
|
||||||
return self.car;
|
return self.car;
|
||||||
}
|
}
|
||||||
@@ -1619,6 +1866,14 @@ merge(Compressor.prototype, {
|
|||||||
return self.evaluate(compressor)[0];
|
return self.evaluate(compressor)[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function has_side_effects_or_prop_access(node, compressor) {
|
||||||
|
var save_pure_getters = compressor.option("pure_getters");
|
||||||
|
compressor.options.pure_getters = false;
|
||||||
|
var ret = node.has_side_effects(compressor);
|
||||||
|
compressor.options.pure_getters = save_pure_getters;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
|
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
|
||||||
if (compressor.option("sequences")) {
|
if (compressor.option("sequences")) {
|
||||||
if (this.left instanceof AST_Seq) {
|
if (this.left instanceof AST_Seq) {
|
||||||
@@ -1630,8 +1885,8 @@ merge(Compressor.prototype, {
|
|||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
if (this.right instanceof AST_Seq
|
if (this.right instanceof AST_Seq
|
||||||
&& !(this.operator == "||" || this.operator == "&&")
|
&& this instanceof AST_Assign
|
||||||
&& !this.left.has_side_effects()) {
|
&& !has_side_effects_or_prop_access(this.left, compressor)) {
|
||||||
var seq = this.right;
|
var seq = this.right;
|
||||||
var x = seq.to_array();
|
var x = seq.to_array();
|
||||||
this.right = x.pop();
|
this.right = x.pop();
|
||||||
@@ -1646,18 +1901,52 @@ merge(Compressor.prototype, {
|
|||||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor){
|
OPT(AST_Binary, function(self, compressor){
|
||||||
function reverse(op) {
|
var reverse = compressor.has_directive("use asm") ? noop
|
||||||
if (!(self.left.has_side_effects() && self.right.has_side_effects())) {
|
: function(op, force) {
|
||||||
if (op) self.operator = op;
|
if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
|
||||||
var tmp = self.left;
|
if (op) self.operator = op;
|
||||||
self.left = self.right;
|
var tmp = self.left;
|
||||||
self.right = tmp;
|
self.left = self.right;
|
||||||
}
|
self.right = tmp;
|
||||||
};
|
}
|
||||||
|
};
|
||||||
if (commutativeOperators(self.operator)) {
|
if (commutativeOperators(self.operator)) {
|
||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
&& !(self.left instanceof AST_Constant)) {
|
&& !(self.left instanceof AST_Constant)) {
|
||||||
reverse();
|
// if right is a constant, whatever side effects the
|
||||||
|
// left side might have could not influence the
|
||||||
|
// result. hence, force switch.
|
||||||
|
|
||||||
|
if (!(self.left instanceof AST_Binary
|
||||||
|
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
|
||||||
|
reverse(null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (/^[!=]==?$/.test(self.operator)) {
|
||||||
|
if (self.left instanceof AST_SymbolRef && self.right instanceof AST_Conditional) {
|
||||||
|
if (self.right.consequent instanceof AST_SymbolRef
|
||||||
|
&& self.right.consequent.definition() === self.left.definition()) {
|
||||||
|
if (/^==/.test(self.operator)) return self.right.condition;
|
||||||
|
if (/^!=/.test(self.operator)) return self.right.condition.negate(compressor);
|
||||||
|
}
|
||||||
|
if (self.right.alternative instanceof AST_SymbolRef
|
||||||
|
&& self.right.alternative.definition() === self.left.definition()) {
|
||||||
|
if (/^==/.test(self.operator)) return self.right.condition.negate(compressor);
|
||||||
|
if (/^!=/.test(self.operator)) return self.right.condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self.right instanceof AST_SymbolRef && self.left instanceof AST_Conditional) {
|
||||||
|
if (self.left.consequent instanceof AST_SymbolRef
|
||||||
|
&& self.left.consequent.definition() === self.right.definition()) {
|
||||||
|
if (/^==/.test(self.operator)) return self.left.condition;
|
||||||
|
if (/^!=/.test(self.operator)) return self.left.condition.negate(compressor);
|
||||||
|
}
|
||||||
|
if (self.left.alternative instanceof AST_SymbolRef
|
||||||
|
&& self.left.alternative.definition() === self.right.definition()) {
|
||||||
|
if (/^==/.test(self.operator)) return self.left.condition.negate(compressor);
|
||||||
|
if (/^!=/.test(self.operator)) return self.left.condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self = self.lift_sequences(compressor);
|
self = self.lift_sequences(compressor);
|
||||||
@@ -1674,11 +1963,12 @@ merge(Compressor.prototype, {
|
|||||||
if (self.left instanceof AST_String
|
if (self.left instanceof AST_String
|
||||||
&& self.left.value == "undefined"
|
&& self.left.value == "undefined"
|
||||||
&& self.right instanceof AST_UnaryPrefix
|
&& self.right instanceof AST_UnaryPrefix
|
||||||
&& self.right.operator == "typeof") {
|
&& self.right.operator == "typeof"
|
||||||
|
&& compressor.option("unsafe")) {
|
||||||
if (!(self.right.expression instanceof AST_SymbolRef)
|
if (!(self.right.expression instanceof AST_SymbolRef)
|
||||||
|| !self.right.expression.undeclared()) {
|
|| !self.right.expression.undeclared()) {
|
||||||
self.left = self.right.expression;
|
self.right = self.right.expression;
|
||||||
self.right = make_node(AST_Undefined, self.left).optimize(compressor);
|
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
||||||
if (self.operator.length == 2) self.operator += "=";
|
if (self.operator.length == 2) self.operator += "=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1723,11 +2013,6 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var exp = self.evaluate(compressor);
|
|
||||||
if (exp.length > 1) {
|
|
||||||
if (best_of(exp[0], self) !== self)
|
|
||||||
return exp[0];
|
|
||||||
}
|
|
||||||
if (compressor.option("comparisons")) {
|
if (compressor.option("comparisons")) {
|
||||||
if (!(compressor.parent() instanceof AST_Binary)
|
if (!(compressor.parent() instanceof AST_Binary)
|
||||||
|| compressor.parent() instanceof AST_Assign) {
|
|| compressor.parent() instanceof AST_Assign) {
|
||||||
@@ -1747,7 +2032,76 @@ merge(Compressor.prototype, {
|
|||||||
&& self.left.operator == "+" && self.left.is_string(compressor)) {
|
&& self.left.operator == "+" && self.left.is_string(compressor)) {
|
||||||
return self.left;
|
return self.left;
|
||||||
}
|
}
|
||||||
return self;
|
if (compressor.option("evaluate")) {
|
||||||
|
if (self.operator == "+") {
|
||||||
|
if (self.left instanceof AST_Constant
|
||||||
|
&& self.right instanceof AST_Binary
|
||||||
|
&& self.right.operator == "+"
|
||||||
|
&& self.right.left instanceof AST_Constant
|
||||||
|
&& self.right.is_string(compressor)) {
|
||||||
|
self = make_node(AST_Binary, self, {
|
||||||
|
operator: "+",
|
||||||
|
left: make_node(AST_String, null, {
|
||||||
|
value: "" + self.left.getValue() + self.right.left.getValue(),
|
||||||
|
start: self.left.start,
|
||||||
|
end: self.right.left.end
|
||||||
|
}),
|
||||||
|
right: self.right.right
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (self.right instanceof AST_Constant
|
||||||
|
&& self.left instanceof AST_Binary
|
||||||
|
&& self.left.operator == "+"
|
||||||
|
&& self.left.right instanceof AST_Constant
|
||||||
|
&& self.left.is_string(compressor)) {
|
||||||
|
self = make_node(AST_Binary, self, {
|
||||||
|
operator: "+",
|
||||||
|
left: self.left.left,
|
||||||
|
right: make_node(AST_String, null, {
|
||||||
|
value: "" + self.left.right.getValue() + self.right.getValue(),
|
||||||
|
start: self.left.right.start,
|
||||||
|
end: self.right.end
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (self.left instanceof AST_Binary
|
||||||
|
&& self.left.operator == "+"
|
||||||
|
&& self.left.is_string(compressor)
|
||||||
|
&& self.left.right instanceof AST_Constant
|
||||||
|
&& self.right instanceof AST_Binary
|
||||||
|
&& self.right.operator == "+"
|
||||||
|
&& self.right.left instanceof AST_Constant
|
||||||
|
&& self.right.is_string(compressor)) {
|
||||||
|
self = make_node(AST_Binary, self, {
|
||||||
|
operator: "+",
|
||||||
|
left: make_node(AST_Binary, self.left, {
|
||||||
|
operator: "+",
|
||||||
|
left: self.left.left,
|
||||||
|
right: make_node(AST_String, null, {
|
||||||
|
value: "" + self.left.right.getValue() + self.right.left.getValue(),
|
||||||
|
start: self.left.right.start,
|
||||||
|
end: self.right.left.end
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
right: self.right.right
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// x * (y * z) ==> x * y * z
|
||||||
|
if (self.right instanceof AST_Binary
|
||||||
|
&& self.right.operator == self.operator
|
||||||
|
&& (self.operator == "*" || self.operator == "&&" || self.operator == "||"))
|
||||||
|
{
|
||||||
|
self.left = make_node(AST_Binary, self.left, {
|
||||||
|
operator : self.operator,
|
||||||
|
left : self.left,
|
||||||
|
right : self.right.left
|
||||||
|
});
|
||||||
|
self.right = self.right.right;
|
||||||
|
return self.transform(compressor);
|
||||||
|
}
|
||||||
|
return self.evaluate(compressor)[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_SymbolRef, function(self, compressor){
|
OPT(AST_SymbolRef, function(self, compressor){
|
||||||
@@ -1881,7 +2235,7 @@ merge(Compressor.prototype, {
|
|||||||
var prop = self.property;
|
var prop = self.property;
|
||||||
if (prop instanceof AST_String && compressor.option("properties")) {
|
if (prop instanceof AST_String && compressor.option("properties")) {
|
||||||
prop = prop.getValue();
|
prop = prop.getValue();
|
||||||
if (is_identifier(prop)) {
|
if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
|
||||||
return make_node(AST_Dot, self, {
|
return make_node(AST_Dot, self, {
|
||||||
expression : self.expression,
|
expression : self.expression,
|
||||||
property : prop
|
property : prop
|
||||||
|
|||||||
@@ -148,12 +148,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function From_Moz_Unary(M) {
|
function From_Moz_Unary(M) {
|
||||||
return new (M.prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
var prefix = "prefix" in M ? M.prefix
|
||||||
|
: M.type == "UnaryExpression" ? true : false;
|
||||||
|
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
operator : M.operator,
|
operator : M.operator,
|
||||||
expression : from_moz(M.argument)
|
expression : from_moz(M.argument)
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var ME_TO_MOZ = {};
|
var ME_TO_MOZ = {};
|
||||||
|
|||||||
193
lib/output.js
193
lib/output.js
@@ -54,13 +54,14 @@ function OutputStream(options) {
|
|||||||
inline_script : false,
|
inline_script : false,
|
||||||
width : 80,
|
width : 80,
|
||||||
max_line_len : 32000,
|
max_line_len : 32000,
|
||||||
ie_proof : true,
|
|
||||||
beautify : false,
|
beautify : false,
|
||||||
source_map : null,
|
source_map : null,
|
||||||
bracketize : false,
|
bracketize : false,
|
||||||
semicolons : true,
|
semicolons : true,
|
||||||
comments : false,
|
comments : false,
|
||||||
preserve_line : false
|
preserve_line : false,
|
||||||
|
screw_ie8 : false,
|
||||||
|
preamble : null,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
@@ -69,11 +70,16 @@ function OutputStream(options) {
|
|||||||
var current_pos = 0;
|
var current_pos = 0;
|
||||||
var OUTPUT = "";
|
var OUTPUT = "";
|
||||||
|
|
||||||
function to_ascii(str) {
|
function to_ascii(str, identifier) {
|
||||||
return str.replace(/[\u0080-\uffff]/g, function(ch) {
|
return str.replace(/[\u0080-\uffff]/g, function(ch) {
|
||||||
var code = ch.charCodeAt(0).toString(16);
|
var code = ch.charCodeAt(0).toString(16);
|
||||||
while (code.length < 4) code = "0" + code;
|
if (code.length <= 2 && !identifier) {
|
||||||
return "\\u" + code;
|
while (code.length < 2) code = "0" + code;
|
||||||
|
return "\\x" + code;
|
||||||
|
} else {
|
||||||
|
while (code.length < 4) code = "0" + code;
|
||||||
|
return "\\u" + code;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,7 +96,7 @@ function OutputStream(options) {
|
|||||||
case "\u2029": return "\\u2029";
|
case "\u2029": return "\\u2029";
|
||||||
case '"': ++dq; return '"';
|
case '"': ++dq; return '"';
|
||||||
case "'": ++sq; return "'";
|
case "'": ++sq; return "'";
|
||||||
case "\0": return "\\0";
|
case "\0": return "\\x00";
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
@@ -109,7 +115,7 @@ function OutputStream(options) {
|
|||||||
function make_name(name) {
|
function make_name(name) {
|
||||||
name = name.toString();
|
name = name.toString();
|
||||||
if (options.ascii_only)
|
if (options.ascii_only)
|
||||||
name = to_ascii(name);
|
name = to_ascii(name, true);
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -294,6 +300,10 @@ function OutputStream(options) {
|
|||||||
return OUTPUT;
|
return OUTPUT;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options.preamble) {
|
||||||
|
print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
|
||||||
|
}
|
||||||
|
|
||||||
var stack = [];
|
var stack = [];
|
||||||
return {
|
return {
|
||||||
get : get,
|
get : get,
|
||||||
@@ -340,24 +350,25 @@ function OutputStream(options) {
|
|||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
function DEFPRINT(nodetype, generator) {
|
function DEFPRINT(nodetype, generator) {
|
||||||
nodetype.DEFMETHOD("print", function(stream){
|
nodetype.DEFMETHOD("_codegen", generator);
|
||||||
var self = this;
|
|
||||||
stream.push_node(self);
|
|
||||||
if (self.needs_parens(stream)) {
|
|
||||||
stream.with_parens(function(){
|
|
||||||
self.add_comments(stream);
|
|
||||||
self.add_source_map(stream);
|
|
||||||
generator(self, stream);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.add_comments(stream);
|
|
||||||
self.add_source_map(stream);
|
|
||||||
generator(self, stream);
|
|
||||||
}
|
|
||||||
stream.pop_node();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||||
|
var self = this, generator = self._codegen;
|
||||||
|
function doit() {
|
||||||
|
self.add_comments(stream);
|
||||||
|
self.add_source_map(stream);
|
||||||
|
generator(self, stream);
|
||||||
|
}
|
||||||
|
stream.push_node(self);
|
||||||
|
if (force_parens || self.needs_parens(stream)) {
|
||||||
|
stream.with_parens(doit);
|
||||||
|
} else {
|
||||||
|
doit();
|
||||||
|
}
|
||||||
|
stream.pop_node();
|
||||||
|
});
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("print_to_string", function(options){
|
AST_Node.DEFMETHOD("print_to_string", function(options){
|
||||||
var s = OutputStream(options);
|
var s = OutputStream(options);
|
||||||
this.print(s);
|
this.print(s);
|
||||||
@@ -372,7 +383,18 @@ function OutputStream(options) {
|
|||||||
var start = self.start;
|
var start = self.start;
|
||||||
if (start && !start._comments_dumped) {
|
if (start && !start._comments_dumped) {
|
||||||
start._comments_dumped = true;
|
start._comments_dumped = true;
|
||||||
var comments = start.comments_before;
|
var comments = start.comments_before || [];
|
||||||
|
|
||||||
|
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
|
||||||
|
// if this node is `return` or `throw`, we cannot allow comments before
|
||||||
|
// the returned or thrown value.
|
||||||
|
if (self instanceof AST_Exit && self.value
|
||||||
|
&& self.value.start.comments_before
|
||||||
|
&& self.value.start.comments_before.length > 0) {
|
||||||
|
comments = comments.concat(self.value.start.comments_before);
|
||||||
|
self.value.start.comments_before = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (c.test) {
|
if (c.test) {
|
||||||
comments = comments.filter(function(comment){
|
comments = comments.filter(function(comment){
|
||||||
return c.test(comment.value);
|
return c.test(comment.value);
|
||||||
@@ -383,7 +405,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
comments.forEach(function(c){
|
comments.forEach(function(c){
|
||||||
if (c.type == "comment1") {
|
if (/comment[134]/.test(c.type)) {
|
||||||
output.print("//" + c.value + "\n");
|
output.print("//" + c.value + "\n");
|
||||||
output.indent();
|
output.indent();
|
||||||
}
|
}
|
||||||
@@ -459,26 +481,10 @@ function OutputStream(options) {
|
|||||||
var so = this.operator, sp = PRECEDENCE[so];
|
var so = this.operator, sp = PRECEDENCE[so];
|
||||||
if (pp > sp
|
if (pp > sp
|
||||||
|| (pp == sp
|
|| (pp == sp
|
||||||
&& this === p.right
|
&& this === p.right)) {
|
||||||
&& !(so == po &&
|
|
||||||
(so == "*" ||
|
|
||||||
so == "&&" ||
|
|
||||||
so == "||")))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for (var i = (foo in bar);;); ← perhaps useless, but valid syntax
|
|
||||||
if (this.operator == "in") {
|
|
||||||
// the “NoIn” stuff :-\
|
|
||||||
// UglifyJS 1.3.3 misses this one.
|
|
||||||
if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this)
|
|
||||||
return true;
|
|
||||||
if (p instanceof AST_VarDef) {
|
|
||||||
var v = output.parent(1), p2 = output.parent(2);
|
|
||||||
if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_PropAccess, function(output){
|
PARENS(AST_PropAccess, function(output){
|
||||||
@@ -502,18 +508,39 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Call, function(output){
|
PARENS(AST_Call, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent(), p1;
|
||||||
return p instanceof AST_New && p.expression === this;
|
if (p instanceof AST_New && p.expression === this)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// workaround for Safari bug.
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=123506
|
||||||
|
return this.expression instanceof AST_Function
|
||||||
|
&& p instanceof AST_PropAccess
|
||||||
|
&& p.expression === this
|
||||||
|
&& (p1 = output.parent(1)) instanceof AST_Assign
|
||||||
|
&& p1.left === p;
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_New, function(output){
|
PARENS(AST_New, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (no_constructor_parens(this, output)
|
if (no_constructor_parens(this, output)
|
||||||
&& (p instanceof AST_Dot // (new Date).getTime()
|
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|
||||||
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS(AST_Number, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
PARENS(AST_NaN, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
if (p instanceof AST_PropAccess && p.expression === this)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
function assign_and_conditional_paren_rules(output) {
|
function assign_and_conditional_paren_rules(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
@@ -622,7 +649,11 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
if (self.init) {
|
if (self.init) {
|
||||||
self.init.print(output);
|
if (self.init instanceof AST_Definitions) {
|
||||||
|
self.init.print(output);
|
||||||
|
} else {
|
||||||
|
parenthesize_for_noin(self.init, output, true);
|
||||||
|
}
|
||||||
output.print(";");
|
output.print(";");
|
||||||
output.space();
|
output.space();
|
||||||
} else {
|
} else {
|
||||||
@@ -734,9 +765,9 @@ function OutputStream(options) {
|
|||||||
// to the inner IF). This function checks for this case and
|
// to the inner IF). This function checks for this case and
|
||||||
// adds the block brackets if needed.
|
// adds the block brackets if needed.
|
||||||
if (!self.body)
|
if (!self.body)
|
||||||
return output.semicolon();
|
return output.force_semicolon();
|
||||||
if (self.body instanceof AST_Do
|
if (self.body instanceof AST_Do
|
||||||
&& output.option("ie_proof")) {
|
&& !output.option("screw_ie8")) {
|
||||||
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
|
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
|
||||||
// croaks with "syntax error" on code like this: if (foo)
|
// croaks with "syntax error" on code like this: if (foo)
|
||||||
// do ... while(cond); else ... we need block brackets
|
// do ... while(cond); else ... we need block brackets
|
||||||
@@ -758,7 +789,7 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
self.body.print(output);
|
force_statement(self.body, output);
|
||||||
};
|
};
|
||||||
DEFPRINT(AST_If, function(self, output){
|
DEFPRINT(AST_If, function(self, output){
|
||||||
output.print("if");
|
output.print("if");
|
||||||
@@ -866,13 +897,32 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Const, function(self, output){
|
DEFPRINT(AST_Const, function(self, output){
|
||||||
self._do_print(output, "const");
|
self._do_print(output, "const");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function parenthesize_for_noin(node, output, noin) {
|
||||||
|
if (!noin) node.print(output);
|
||||||
|
else try {
|
||||||
|
// need to take some precautions here:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/60
|
||||||
|
node.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Binary && node.operator == "in")
|
||||||
|
throw output;
|
||||||
|
}));
|
||||||
|
node.print(output);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== output) throw ex;
|
||||||
|
node.print(output, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
DEFPRINT(AST_VarDef, function(self, output){
|
DEFPRINT(AST_VarDef, function(self, output){
|
||||||
self.name.print(output);
|
self.name.print(output);
|
||||||
if (self.value) {
|
if (self.value) {
|
||||||
output.space();
|
output.space();
|
||||||
output.print("=");
|
output.print("=");
|
||||||
output.space();
|
output.space();
|
||||||
self.value.print(output);
|
var p = output.parent(1);
|
||||||
|
var noin = p instanceof AST_For || p instanceof AST_ForIn;
|
||||||
|
parenthesize_for_noin(self.value, output, noin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -891,7 +941,7 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_New, function(self, output){
|
DEFPRINT(AST_New, function(self, output){
|
||||||
output.print("new");
|
output.print("new");
|
||||||
output.space();
|
output.space();
|
||||||
AST_Call.prototype.print.call(self, output);
|
AST_Call.prototype._codegen(self, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Seq.DEFMETHOD("_do_print", function(output){
|
AST_Seq.DEFMETHOD("_do_print", function(output){
|
||||||
@@ -919,7 +969,7 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Dot, function(self, output){
|
DEFPRINT(AST_Dot, function(self, output){
|
||||||
var expr = self.expression;
|
var expr = self.expression;
|
||||||
expr.print(output);
|
expr.print(output);
|
||||||
if (expr instanceof AST_Number) {
|
if (expr instanceof AST_Number && expr.getValue() >= 0) {
|
||||||
if (!/[xa-f.]/i.test(output.last())) {
|
if (!/[xa-f.]/i.test(output.last())) {
|
||||||
output.print(".");
|
output.print(".");
|
||||||
}
|
}
|
||||||
@@ -950,7 +1000,18 @@ function OutputStream(options) {
|
|||||||
self.left.print(output);
|
self.left.print(output);
|
||||||
output.space();
|
output.space();
|
||||||
output.print(self.operator);
|
output.print(self.operator);
|
||||||
output.space();
|
if (self.operator == "<"
|
||||||
|
&& self.right instanceof AST_UnaryPrefix
|
||||||
|
&& self.right.operator == "!"
|
||||||
|
&& self.right.expression instanceof AST_UnaryPrefix
|
||||||
|
&& self.right.expression.operator == "--") {
|
||||||
|
// space is mandatory to avoid outputting <!--
|
||||||
|
// http://javascript.spec.whatwg.org/#comment-syntax
|
||||||
|
output.print(" ");
|
||||||
|
} else {
|
||||||
|
// the space is optional depending on "beautify"
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
self.right.print(output);
|
self.right.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Conditional, function(self, output){
|
DEFPRINT(AST_Conditional, function(self, output){
|
||||||
@@ -971,8 +1032,12 @@ function OutputStream(options) {
|
|||||||
if (len > 0) output.space();
|
if (len > 0) output.space();
|
||||||
a.forEach(function(exp, i){
|
a.forEach(function(exp, i){
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
if (!(exp instanceof AST_Undefined))
|
exp.print(output);
|
||||||
exp.print(output);
|
// If the final element is a hole, we need to make sure it
|
||||||
|
// doesn't look like a trailing comma, by inserting an actual
|
||||||
|
// trailing comma.
|
||||||
|
if (i === len - 1 && exp instanceof AST_Hole)
|
||||||
|
output.comma();
|
||||||
});
|
});
|
||||||
if (len > 0) output.space();
|
if (len > 0) output.space();
|
||||||
});
|
});
|
||||||
@@ -994,26 +1059,30 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||||
var key = self.key;
|
var key = self.key;
|
||||||
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"
|
||||||
|| !output.option("beautify")
|
|| !output.option("beautify")
|
||||||
&& +key + "" == key)
|
&& +key + "" == key)
|
||||||
&& parseFloat(key) >= 0) {
|
&& parseFloat(key) >= 0) {
|
||||||
output.print(make_num(key));
|
output.print(make_num(key));
|
||||||
} else if (!is_identifier(key)) {
|
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
||||||
output.print_string(key);
|
|
||||||
} else {
|
|
||||||
output.print_name(key);
|
output.print_name(key);
|
||||||
|
} else {
|
||||||
|
output.print_string(key);
|
||||||
}
|
}
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||||
output.print("set");
|
output.print("set");
|
||||||
|
output.space();
|
||||||
|
self.key.print(output);
|
||||||
self.value._do_print(output, true);
|
self.value._do_print(output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectGetter, function(self, output){
|
DEFPRINT(AST_ObjectGetter, function(self, output){
|
||||||
output.print("get");
|
output.print("get");
|
||||||
|
output.space();
|
||||||
|
self.key.print(output);
|
||||||
self.value._do_print(output, true);
|
self.value._do_print(output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Symbol, function(self, output){
|
DEFPRINT(AST_Symbol, function(self, output){
|
||||||
@@ -1023,6 +1092,7 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Undefined, function(self, output){
|
DEFPRINT(AST_Undefined, function(self, output){
|
||||||
output.print("void 0");
|
output.print("void 0");
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_Hole, noop);
|
||||||
DEFPRINT(AST_Infinity, function(self, output){
|
DEFPRINT(AST_Infinity, function(self, output){
|
||||||
output.print("1/0");
|
output.print("1/0");
|
||||||
});
|
});
|
||||||
@@ -1046,6 +1116,9 @@ function OutputStream(options) {
|
|||||||
if (output.option("ascii_only"))
|
if (output.option("ascii_only"))
|
||||||
str = output.to_ascii(str);
|
str = output.to_ascii(str);
|
||||||
output.print(str);
|
output.print(str);
|
||||||
|
var p = output.parent();
|
||||||
|
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
||||||
|
output.print(" ");
|
||||||
});
|
});
|
||||||
|
|
||||||
function force_statement(stat, output) {
|
function force_statement(stat, output) {
|
||||||
@@ -1076,7 +1149,7 @@ function OutputStream(options) {
|
|||||||
if (p instanceof AST_Statement && p.body === node)
|
if (p instanceof AST_Statement && p.body === node)
|
||||||
return true;
|
return true;
|
||||||
if ((p instanceof AST_Seq && p.car === node ) ||
|
if ((p instanceof AST_Seq && p.car === node ) ||
|
||||||
(p instanceof AST_Call && p.expression === node ) ||
|
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
|
||||||
(p instanceof AST_Dot && p.expression === node ) ||
|
(p instanceof AST_Dot && p.expression === node ) ||
|
||||||
(p instanceof AST_Sub && p.expression === node ) ||
|
(p instanceof AST_Sub && p.expression === node ) ||
|
||||||
(p instanceof AST_Conditional && p.condition === node ) ||
|
(p instanceof AST_Conditional && p.condition === node ) ||
|
||||||
|
|||||||
183
lib/parse.js
183
lib/parse.js
@@ -149,7 +149,7 @@ function is_unicode_connector_punctuation(ch) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function is_identifier(name) {
|
function is_identifier(name) {
|
||||||
return /^[a-z_$][a-z0-9_$]*$/i.test(name) && !RESERVED_WORDS(name);
|
return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
function is_identifier_start(code) {
|
function is_identifier_start(code) {
|
||||||
@@ -167,6 +167,17 @@ function is_identifier_char(ch) {
|
|||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function is_identifier_string(str){
|
||||||
|
var i = str.length;
|
||||||
|
if (i == 0) return false;
|
||||||
|
if (!is_identifier_start(str.charCodeAt(0))) return false;
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (!is_identifier_char(str.charAt(i)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
function parse_js_number(num) {
|
function parse_js_number(num) {
|
||||||
if (RE_HEX_NUMBER.test(num)) {
|
if (RE_HEX_NUMBER.test(num)) {
|
||||||
return parseInt(num.substr(2), 16);
|
return parseInt(num.substr(2), 16);
|
||||||
@@ -190,12 +201,6 @@ JS_Parse_Error.prototype.toString = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function js_error(message, filename, line, col, pos) {
|
function js_error(message, filename, line, col, pos) {
|
||||||
AST_Node.warn("ERROR: {message} [{file}:{line},{col}]", {
|
|
||||||
message: message,
|
|
||||||
file: filename,
|
|
||||||
line: line,
|
|
||||||
col: col
|
|
||||||
});
|
|
||||||
throw new JS_Parse_Error(message, line, col, pos);
|
throw new JS_Parse_Error(message, line, col, pos);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -205,7 +210,7 @@ function is_token(token, type, val) {
|
|||||||
|
|
||||||
var EX_EOF = {};
|
var EX_EOF = {};
|
||||||
|
|
||||||
function tokenizer($TEXT, filename) {
|
function tokenizer($TEXT, filename, html5_comments) {
|
||||||
|
|
||||||
var S = {
|
var S = {
|
||||||
text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
|
text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
|
||||||
@@ -237,6 +242,14 @@ function tokenizer($TEXT, filename) {
|
|||||||
return ch;
|
return ch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function forward(i) {
|
||||||
|
while (i-- > 0) next();
|
||||||
|
};
|
||||||
|
|
||||||
|
function looking_at(str) {
|
||||||
|
return S.text.substr(S.pos, str.length) == str;
|
||||||
|
};
|
||||||
|
|
||||||
function find(what, signal_eof) {
|
function find(what, signal_eof) {
|
||||||
var pos = S.text.indexOf(what, S.pos);
|
var pos = S.text.indexOf(what, S.pos);
|
||||||
if (signal_eof && pos == -1) throw EX_EOF;
|
if (signal_eof && pos == -1) throw EX_EOF;
|
||||||
@@ -249,10 +262,12 @@ function tokenizer($TEXT, filename) {
|
|||||||
S.tokpos = S.pos;
|
S.tokpos = S.pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var prev_was_dot = false;
|
||||||
function token(type, value, is_comment) {
|
function token(type, value, is_comment) {
|
||||||
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX[value]) ||
|
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
|
||||||
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
|
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
|
||||||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
|
(type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
|
||||||
|
prev_was_dot = (type == "punc" && value == ".");
|
||||||
var ret = {
|
var ret = {
|
||||||
type : type,
|
type : type,
|
||||||
value : value,
|
value : value,
|
||||||
@@ -374,8 +389,8 @@ function tokenizer($TEXT, filename) {
|
|||||||
return token("string", ret);
|
return token("string", ret);
|
||||||
});
|
});
|
||||||
|
|
||||||
function read_line_comment() {
|
function skip_line_comment(type) {
|
||||||
next();
|
var regex_allowed = S.regex_allowed;
|
||||||
var i = find("\n"), ret;
|
var i = find("\n"), ret;
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
ret = S.text.substr(S.pos);
|
ret = S.text.substr(S.pos);
|
||||||
@@ -384,11 +399,13 @@ function tokenizer($TEXT, filename) {
|
|||||||
ret = S.text.substring(S.pos, i);
|
ret = S.text.substring(S.pos, i);
|
||||||
S.pos = i;
|
S.pos = i;
|
||||||
}
|
}
|
||||||
return token("comment1", ret, true);
|
S.comments_before.push(token(type, ret, true));
|
||||||
|
S.regex_allowed = regex_allowed;
|
||||||
|
return next_token();
|
||||||
};
|
};
|
||||||
|
|
||||||
var read_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
|
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
|
||||||
next();
|
var regex_allowed = S.regex_allowed;
|
||||||
var i = find("*/", true);
|
var i = find("*/", true);
|
||||||
var text = S.text.substring(S.pos, i);
|
var text = S.text.substring(S.pos, i);
|
||||||
var a = text.split("\n"), n = a.length;
|
var a = text.split("\n"), n = a.length;
|
||||||
@@ -398,8 +415,11 @@ function tokenizer($TEXT, filename) {
|
|||||||
if (n > 1) S.col = a[n - 1].length;
|
if (n > 1) S.col = a[n - 1].length;
|
||||||
else S.col += a[n - 1].length;
|
else S.col += a[n - 1].length;
|
||||||
S.col += 2;
|
S.col += 2;
|
||||||
S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
|
var nlb = S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
|
||||||
return token("comment2", text, true);
|
S.comments_before.push(token("comment2", text, true));
|
||||||
|
S.regex_allowed = regex_allowed;
|
||||||
|
S.newline_before = nlb;
|
||||||
|
return next_token();
|
||||||
});
|
});
|
||||||
|
|
||||||
function read_name() {
|
function read_name() {
|
||||||
@@ -463,16 +483,13 @@ function tokenizer($TEXT, filename) {
|
|||||||
|
|
||||||
function handle_slash() {
|
function handle_slash() {
|
||||||
next();
|
next();
|
||||||
var regex_allowed = S.regex_allowed;
|
|
||||||
switch (peek()) {
|
switch (peek()) {
|
||||||
case "/":
|
case "/":
|
||||||
S.comments_before.push(read_line_comment());
|
next();
|
||||||
S.regex_allowed = regex_allowed;
|
return skip_line_comment("comment1");
|
||||||
return next_token();
|
|
||||||
case "*":
|
case "*":
|
||||||
S.comments_before.push(read_multiline_comment());
|
next();
|
||||||
S.regex_allowed = regex_allowed;
|
return skip_multiline_comment();
|
||||||
return next_token();
|
|
||||||
}
|
}
|
||||||
return S.regex_allowed ? read_regexp("") : read_operator("/");
|
return S.regex_allowed ? read_regexp("") : read_operator("/");
|
||||||
};
|
};
|
||||||
@@ -486,6 +503,7 @@ function tokenizer($TEXT, filename) {
|
|||||||
|
|
||||||
function read_word() {
|
function read_word() {
|
||||||
var word = read_name();
|
var word = read_name();
|
||||||
|
if (prev_was_dot) return token("name", word);
|
||||||
return KEYWORDS_ATOM(word) ? token("atom", word)
|
return KEYWORDS_ATOM(word) ? token("atom", word)
|
||||||
: !KEYWORDS(word) ? token("name", word)
|
: !KEYWORDS(word) ? token("name", word)
|
||||||
: OPERATORS(word) ? token("operator", word)
|
: OPERATORS(word) ? token("operator", word)
|
||||||
@@ -508,6 +526,16 @@ function tokenizer($TEXT, filename) {
|
|||||||
return read_regexp(force_regexp);
|
return read_regexp(force_regexp);
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
start_token();
|
start_token();
|
||||||
|
if (html5_comments) {
|
||||||
|
if (looking_at("<!--")) {
|
||||||
|
forward(4);
|
||||||
|
return skip_line_comment("comment3");
|
||||||
|
}
|
||||||
|
if (looking_at("-->") && S.newline_before) {
|
||||||
|
forward(3);
|
||||||
|
return skip_line_comment("comment4");
|
||||||
|
}
|
||||||
|
}
|
||||||
var ch = peek();
|
var ch = peek();
|
||||||
if (!ch) return token("eof");
|
if (!ch) return token("eof");
|
||||||
var code = ch.charCodeAt(0);
|
var code = ch.charCodeAt(0);
|
||||||
@@ -551,10 +579,10 @@ var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
|
|||||||
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
|
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
|
||||||
|
|
||||||
var PRECEDENCE = (function(a, ret){
|
var PRECEDENCE = (function(a, ret){
|
||||||
for (var i = 0, n = 1; i < a.length; ++i, ++n) {
|
for (var i = 0; i < a.length; ++i) {
|
||||||
var b = a[i];
|
var b = a[i];
|
||||||
for (var j = 0; j < b.length; ++j) {
|
for (var j = 0; j < b.length; ++j) {
|
||||||
ret[b[j]] = n;
|
ret[b[j]] = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -583,13 +611,18 @@ var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "nam
|
|||||||
function parse($TEXT, options) {
|
function parse($TEXT, options) {
|
||||||
|
|
||||||
options = defaults(options, {
|
options = defaults(options, {
|
||||||
strict : false,
|
strict : false,
|
||||||
filename : null,
|
filename : null,
|
||||||
toplevel : null
|
toplevel : null,
|
||||||
|
expression : false,
|
||||||
|
html5_comments : true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var S = {
|
var S = {
|
||||||
input : typeof $TEXT == "string" ? tokenizer($TEXT, options.filename) : $TEXT,
|
input : (typeof $TEXT == "string"
|
||||||
|
? tokenizer($TEXT, options.filename,
|
||||||
|
options.html5_comments)
|
||||||
|
: $TEXT),
|
||||||
token : null,
|
token : null,
|
||||||
prev : null,
|
prev : null,
|
||||||
peeked : null,
|
peeked : null,
|
||||||
@@ -682,12 +715,16 @@ function parse($TEXT, options) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var statement = embed_tokens(function() {
|
function handle_regexp() {
|
||||||
var tmp;
|
|
||||||
if (is("operator", "/") || is("operator", "/=")) {
|
if (is("operator", "/") || is("operator", "/=")) {
|
||||||
S.peeked = null;
|
S.peeked = null;
|
||||||
S.token = S.input(S.token.value.substr(1)); // force regexp
|
S.token = S.input(S.token.value.substr(1)); // force regexp
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var statement = embed_tokens(function() {
|
||||||
|
var tmp;
|
||||||
|
handle_regexp();
|
||||||
switch (S.token.type) {
|
switch (S.token.type) {
|
||||||
case "string":
|
case "string":
|
||||||
var dir = S.in_directives, stat = simple_statement();
|
var dir = S.in_directives, stat = simple_statement();
|
||||||
@@ -752,7 +789,7 @@ function parse($TEXT, options) {
|
|||||||
return for_();
|
return for_();
|
||||||
|
|
||||||
case "function":
|
case "function":
|
||||||
return function_(true);
|
return function_(AST_Defun);
|
||||||
|
|
||||||
case "if":
|
case "if":
|
||||||
return if_();
|
return if_();
|
||||||
@@ -815,6 +852,18 @@ function parse($TEXT, options) {
|
|||||||
S.labels.push(label);
|
S.labels.push(label);
|
||||||
var stat = statement();
|
var stat = statement();
|
||||||
S.labels.pop();
|
S.labels.pop();
|
||||||
|
if (!(stat instanceof AST_IterationStatement)) {
|
||||||
|
// check for `continue` that refers to this label.
|
||||||
|
// those should be reported as syntax errors.
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/287
|
||||||
|
label.references.forEach(function(ref){
|
||||||
|
if (ref instanceof AST_Continue) {
|
||||||
|
ref = ref.label.start;
|
||||||
|
croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
|
||||||
|
ref.line, ref.col, ref.pos);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return new AST_LabeledStatement({ body: stat, label: label });
|
return new AST_LabeledStatement({ body: stat, label: label });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -823,18 +872,22 @@ function parse($TEXT, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function break_cont(type) {
|
function break_cont(type) {
|
||||||
var label = null;
|
var label = null, ldef;
|
||||||
if (!can_insert_semicolon()) {
|
if (!can_insert_semicolon()) {
|
||||||
label = as_symbol(AST_LabelRef, true);
|
label = as_symbol(AST_LabelRef, true);
|
||||||
}
|
}
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
if (!find_if(function(l){ return l.name == label.name }, S.labels))
|
ldef = find_if(function(l){ return l.name == label.name }, S.labels);
|
||||||
|
if (!ldef)
|
||||||
croak("Undefined label " + label.name);
|
croak("Undefined label " + label.name);
|
||||||
|
label.thedef = ldef;
|
||||||
}
|
}
|
||||||
else if (S.in_loop == 0)
|
else if (S.in_loop == 0)
|
||||||
croak(type.TYPE + " not inside a loop or switch");
|
croak(type.TYPE + " not inside a loop or switch");
|
||||||
semicolon();
|
semicolon();
|
||||||
return new type({ label: label });
|
var stat = new type({ label: label });
|
||||||
|
if (ldef) ldef.references.push(stat);
|
||||||
|
return stat;
|
||||||
};
|
};
|
||||||
|
|
||||||
function for_() {
|
function for_() {
|
||||||
@@ -880,16 +933,12 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var function_ = function(in_statement, ctor) {
|
var function_ = function(ctor) {
|
||||||
var name = is("name") ? as_symbol(in_statement
|
var in_statement = ctor === AST_Defun;
|
||||||
? AST_SymbolDefun
|
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
|
||||||
: ctor === AST_Accessor
|
|
||||||
? AST_SymbolAccessor
|
|
||||||
: AST_SymbolLambda) : null;
|
|
||||||
if (in_statement && !name)
|
if (in_statement && !name)
|
||||||
unexpected();
|
unexpected();
|
||||||
expect("(");
|
expect("(");
|
||||||
if (!ctor) ctor = in_statement ? AST_Defun : AST_Function;
|
|
||||||
return new ctor({
|
return new ctor({
|
||||||
name: name,
|
name: name,
|
||||||
argnames: (function(first, a){
|
argnames: (function(first, a){
|
||||||
@@ -1060,7 +1109,9 @@ function parse($TEXT, options) {
|
|||||||
var tok = S.token, ret;
|
var tok = S.token, ret;
|
||||||
switch (tok.type) {
|
switch (tok.type) {
|
||||||
case "name":
|
case "name":
|
||||||
return as_symbol(AST_SymbolRef);
|
case "keyword":
|
||||||
|
ret = _make_symbol(AST_SymbolRef);
|
||||||
|
break;
|
||||||
case "num":
|
case "num":
|
||||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||||
break;
|
break;
|
||||||
@@ -1111,7 +1162,7 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
if (is("keyword", "function")) {
|
if (is("keyword", "function")) {
|
||||||
next();
|
next();
|
||||||
var func = function_(false);
|
var func = function_(AST_Function);
|
||||||
func.start = start;
|
func.start = start;
|
||||||
func.end = prev();
|
func.end = prev();
|
||||||
return subscripts(func, allow_calls);
|
return subscripts(func, allow_calls);
|
||||||
@@ -1128,7 +1179,7 @@ function parse($TEXT, options) {
|
|||||||
if (first) first = false; else expect(",");
|
if (first) first = false; else expect(",");
|
||||||
if (allow_trailing_comma && is("punc", closing)) break;
|
if (allow_trailing_comma && is("punc", closing)) break;
|
||||||
if (is("punc", ",") && allow_empty) {
|
if (is("punc", ",") && allow_empty) {
|
||||||
a.push(new AST_Undefined({ start: S.token, end: S.token }));
|
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||||
} else {
|
} else {
|
||||||
a.push(expression(false));
|
a.push(expression(false));
|
||||||
}
|
}
|
||||||
@@ -1159,8 +1210,8 @@ function parse($TEXT, options) {
|
|||||||
if (name == "get") {
|
if (name == "get") {
|
||||||
a.push(new AST_ObjectGetter({
|
a.push(new AST_ObjectGetter({
|
||||||
start : start,
|
start : start,
|
||||||
key : name,
|
key : as_atom_node(),
|
||||||
value : function_(false, AST_Accessor),
|
value : function_(AST_Accessor),
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
@@ -1168,8 +1219,8 @@ function parse($TEXT, options) {
|
|||||||
if (name == "set") {
|
if (name == "set") {
|
||||||
a.push(new AST_ObjectSetter({
|
a.push(new AST_ObjectSetter({
|
||||||
start : start,
|
start : start,
|
||||||
key : name,
|
key : as_atom_node(),
|
||||||
value : function_(false, AST_Accessor),
|
value : function_(AST_Accessor),
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
@@ -1217,17 +1268,21 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function _make_symbol(type) {
|
||||||
|
var name = S.token.value;
|
||||||
|
return new (name == "this" ? AST_This : type)({
|
||||||
|
name : String(name),
|
||||||
|
start : S.token,
|
||||||
|
end : S.token
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function as_symbol(type, noerror) {
|
function as_symbol(type, noerror) {
|
||||||
if (!is("name")) {
|
if (!is("name")) {
|
||||||
if (!noerror) croak("Name expected");
|
if (!noerror) croak("Name expected");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var name = S.token.value;
|
var sym = _make_symbol(type);
|
||||||
var sym = new (name == "this" ? AST_This : type)({
|
|
||||||
name : String(S.token.value),
|
|
||||||
start : S.token,
|
|
||||||
end : S.token
|
|
||||||
});
|
|
||||||
next();
|
next();
|
||||||
return sym;
|
return sym;
|
||||||
};
|
};
|
||||||
@@ -1270,6 +1325,7 @@ function parse($TEXT, options) {
|
|||||||
var start = S.token;
|
var start = S.token;
|
||||||
if (is("operator") && UNARY_PREFIX(start.value)) {
|
if (is("operator") && UNARY_PREFIX(start.value)) {
|
||||||
next();
|
next();
|
||||||
|
handle_regexp();
|
||||||
var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
|
var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
|
||||||
ex.start = start;
|
ex.start = start;
|
||||||
ex.end = prev();
|
ex.end = prev();
|
||||||
@@ -1333,15 +1389,8 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function is_assignable(expr) {
|
function is_assignable(expr) {
|
||||||
if (!options.strict) return true;
|
if (!options.strict) return true;
|
||||||
switch (expr[0]+"") {
|
if (expr instanceof AST_This) return false;
|
||||||
case "dot":
|
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
|
||||||
case "sub":
|
|
||||||
case "new":
|
|
||||||
case "call":
|
|
||||||
return true;
|
|
||||||
case "name":
|
|
||||||
return expr[1] != "this";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var maybe_assign = function(no_in) {
|
var maybe_assign = function(no_in) {
|
||||||
@@ -1355,7 +1404,7 @@ function parse($TEXT, options) {
|
|||||||
left : left,
|
left : left,
|
||||||
operator : val,
|
operator : val,
|
||||||
right : maybe_assign(no_in),
|
right : maybe_assign(no_in),
|
||||||
end : peek()
|
end : prev()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
croak("Invalid assignment");
|
croak("Invalid assignment");
|
||||||
@@ -1385,6 +1434,10 @@ function parse($TEXT, options) {
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options.expression) {
|
||||||
|
return expression(true);
|
||||||
|
}
|
||||||
|
|
||||||
return (function(){
|
return (function(){
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var body = [];
|
var body = [];
|
||||||
|
|||||||
101
lib/scope.js
101
lib/scope.js
@@ -57,13 +57,17 @@ function SymbolDef(scope, index, orig) {
|
|||||||
|
|
||||||
SymbolDef.prototype = {
|
SymbolDef.prototype = {
|
||||||
unmangleable: function(options) {
|
unmangleable: function(options) {
|
||||||
return this.global
|
return (this.global && !(options && options.toplevel))
|
||||||
|| this.undeclared
|
|| this.undeclared
|
||||||
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
||||||
},
|
},
|
||||||
mangle: function(options) {
|
mangle: function(options) {
|
||||||
if (!this.mangled_name && !this.unmangleable(options))
|
if (!this.mangled_name && !this.unmangleable(options)) {
|
||||||
this.mangled_name = this.scope.next_mangled(options);
|
var s = this.scope;
|
||||||
|
if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
|
||||||
|
s = s.parent_scope;
|
||||||
|
this.mangled_name = s.next_mangled(options, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,7 +82,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var self = this;
|
var self = this;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
var labels = new Dictionary();
|
|
||||||
var nesting = 0;
|
var nesting = 0;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
@@ -101,35 +104,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
s.uses_with = true;
|
s.uses_with = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabeledStatement) {
|
|
||||||
var l = node.label;
|
|
||||||
if (labels.has(l.name))
|
|
||||||
throw new Error(string_template("Label {name} defined twice", l));
|
|
||||||
labels.set(l.name, l);
|
|
||||||
descend();
|
|
||||||
labels.del(l.name);
|
|
||||||
return true; // no descend again
|
|
||||||
}
|
|
||||||
if (node instanceof AST_SymbolDeclaration) {
|
|
||||||
node.init_scope_vars();
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Label) {
|
|
||||||
node.thedef = node;
|
|
||||||
node.init_scope_vars();
|
|
||||||
}
|
|
||||||
if (node instanceof AST_SymbolLambda) {
|
if (node instanceof AST_SymbolLambda) {
|
||||||
//scope.def_function(node);
|
scope.def_function(node);
|
||||||
//
|
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/24 — MSIE
|
|
||||||
// leaks function expression names into the containing
|
|
||||||
// scope. Don't like this fix but seems we can't do any
|
|
||||||
// better. IE: please die. Please!
|
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
|
||||||
|
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
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
|
||||||
@@ -138,14 +117,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// instanceof AST_Scope) but we get to the symbol a bit
|
// instanceof AST_Scope) but we get to the symbol a bit
|
||||||
// later.
|
// later.
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = scope.parent_scope).def_function(node);
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar
|
else if (node instanceof AST_SymbolVar
|
||||||
|| node instanceof AST_SymbolConst) {
|
|| node instanceof AST_SymbolConst) {
|
||||||
var def = scope.def_variable(node);
|
var def = scope.def_variable(node);
|
||||||
def.constant = node instanceof AST_SymbolConst;
|
def.constant = node instanceof AST_SymbolConst;
|
||||||
def = tw.parent();
|
def.init = tw.parent().value;
|
||||||
if (def.value) node.init.push(def);
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||||
@@ -156,15 +133,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// identifier in the enclosing scope)
|
// identifier in the enclosing scope)
|
||||||
scope.def_variable(node);
|
scope.def_variable(node);
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabelRef) {
|
|
||||||
var sym = labels.get(node.name);
|
|
||||||
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
|
||||||
name: node.name,
|
|
||||||
line: node.start.line,
|
|
||||||
col: node.start.col
|
|
||||||
}));
|
|
||||||
node.thedef = sym;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
|
|
||||||
@@ -179,10 +147,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
func = prev_func;
|
func = prev_func;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabelRef) {
|
|
||||||
node.reference();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
var name = node.name;
|
var name = node.name;
|
||||||
var sym = node.scope.find_variable(name);
|
var sym = node.scope.find_variable(name);
|
||||||
@@ -193,6 +157,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
} else {
|
} else {
|
||||||
g = new SymbolDef(self, globals.size(), node);
|
g = new SymbolDef(self, globals.size(), node);
|
||||||
g.undeclared = true;
|
g.undeclared = true;
|
||||||
|
g.global = true;
|
||||||
globals.set(name, g);
|
globals.set(name, g);
|
||||||
}
|
}
|
||||||
node.thedef = g;
|
node.thedef = g;
|
||||||
@@ -200,7 +165,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
|
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
|
||||||
s.uses_eval = true;
|
s.uses_eval = true;
|
||||||
}
|
}
|
||||||
if (name == "arguments") {
|
if (func && name == "arguments") {
|
||||||
func.uses_arguments = true;
|
func.uses_arguments = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -246,18 +211,6 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
this.frame = this.scope.nesting - def.scope.nesting;
|
this.frame = this.scope.nesting - def.scope.nesting;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
|
||||||
this.init = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
|
||||||
this.references = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_LabelRef.DEFMETHOD("reference", function(){
|
|
||||||
this.thedef.references.push(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("find_variable", function(name){
|
AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||||
if (name instanceof AST_Symbol) name = name.name;
|
if (name instanceof AST_Symbol) name = name.name;
|
||||||
return this.variables.get(name)
|
return this.variables.get(name)
|
||||||
@@ -287,14 +240,14 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||||
var ext = this.enclosed, n = ext.length;
|
var ext = this.enclosed;
|
||||||
out: while (true) {
|
out: while (true) {
|
||||||
var m = base54(++this.cname);
|
var m = base54(++this.cname);
|
||||||
if (!is_identifier(m)) continue; // skip over "do"
|
if (!is_identifier(m)) continue; // skip over "do"
|
||||||
// we must ensure that the mangled name does not shadow a name
|
// we must ensure that the mangled name does not shadow a name
|
||||||
// from some parent scope that is referenced in this or in
|
// from some parent scope that is referenced in this or in
|
||||||
// inner scopes.
|
// inner scopes.
|
||||||
for (var i = n; --i >= 0;) {
|
for (var i = ext.length; --i >= 0;) {
|
||||||
var sym = ext[i];
|
var sym = ext[i];
|
||||||
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
||||||
if (m == name) continue out;
|
if (m == name) continue out;
|
||||||
@@ -303,6 +256,19 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Function.DEFMETHOD("next_mangled", function(options, def){
|
||||||
|
// #179, #326
|
||||||
|
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
|
||||||
|
// a function expression's argument cannot shadow the function expression's name
|
||||||
|
|
||||||
|
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
|
||||||
|
while (true) {
|
||||||
|
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
|
||||||
|
if (!(tricky_def && tricky_def.mangled_name == name))
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("references", function(sym){
|
AST_Scope.DEFMETHOD("references", function(sym){
|
||||||
if (sym instanceof AST_Symbol) sym = sym.definition();
|
if (sym instanceof AST_Symbol) sym = sym.definition();
|
||||||
return this.enclosed.indexOf(sym) < 0 ? null : sym;
|
return this.enclosed.indexOf(sym) < 0 ? null : sym;
|
||||||
@@ -349,8 +315,11 @@ AST_Symbol.DEFMETHOD("global", function(){
|
|||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||||
return defaults(options, {
|
return defaults(options, {
|
||||||
except : [],
|
except : [],
|
||||||
eval : false,
|
eval : false,
|
||||||
|
sort : false,
|
||||||
|
toplevel : false,
|
||||||
|
screw_ie8 : false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -371,12 +340,16 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
var p = tw.parent();
|
var p = tw.parent(), a = [];
|
||||||
node.variables.each(function(symbol){
|
node.variables.each(function(symbol){
|
||||||
if (options.except.indexOf(symbol.name) < 0) {
|
if (options.except.indexOf(symbol.name) < 0) {
|
||||||
to_mangle.push(symbol);
|
a.push(symbol);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (options.sort) a.sort(function(a, b){
|
||||||
|
return b.references.length - a.references.length;
|
||||||
|
});
|
||||||
|
to_mangle.push.apply(to_mangle, a);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Label) {
|
if (node instanceof AST_Label) {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Tree transformer helpers.
|
// Tree transformer helpers.
|
||||||
// XXX: eventually I should refactor the compressor to use this infrastructure.
|
|
||||||
|
|
||||||
function TreeTransformer(before, after) {
|
function TreeTransformer(before, after) {
|
||||||
TreeWalker.call(this);
|
TreeWalker.call(this);
|
||||||
@@ -160,6 +159,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
});
|
});
|
||||||
|
|
||||||
_(AST_VarDef, function(self, tw){
|
_(AST_VarDef, function(self, tw){
|
||||||
|
self.name = self.name.transform(tw);
|
||||||
if (self.value) self.value = self.value.transform(tw);
|
if (self.value) self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
15
lib/utils.js
15
lib/utils.js
@@ -245,6 +245,13 @@ function makePredicate(words) {
|
|||||||
return new Function("str", f);
|
return new Function("str", f);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function all(array, predicate) {
|
||||||
|
for (var i = array.length; --i >= 0;)
|
||||||
|
if (!predicate(array[i]))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
function Dictionary() {
|
function Dictionary() {
|
||||||
this._values = Object.create(null);
|
this._values = Object.create(null);
|
||||||
this._size = 0;
|
this._size = 0;
|
||||||
@@ -255,6 +262,14 @@ Dictionary.prototype = {
|
|||||||
this._values["$" + key] = val;
|
this._values["$" + key] = val;
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
add: function(key, val) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
this.get(key).push(val);
|
||||||
|
} else {
|
||||||
|
this.set(key, [ val ]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
get: function(key) { return this._values["$" + key] },
|
get: function(key) { return this._values["$" + key] },
|
||||||
del: function(key) {
|
del: function(key) {
|
||||||
if (this.has(key)) {
|
if (this.has(key)) {
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -3,20 +3,25 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"main": "tools/node.js",
|
"main": "tools/node.js",
|
||||||
"version": "2.2.1",
|
"version": "2.4.4",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
"email": "mihai.bazon@gmail.com",
|
"email": "mihai.bazon@gmail.com",
|
||||||
"web": "http://lisperator.net/"
|
"web": "http://lisperator.net/"
|
||||||
}],
|
}],
|
||||||
"repositories": [{
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mishoo/UglifyJS2.git"
|
"url": "https://github.com/mishoo/UglifyJS2.git"
|
||||||
}],
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"async" : "~0.2.6",
|
||||||
"source-map" : "~0.1.7",
|
"source-map" : "~0.1.7",
|
||||||
"optimist" : "~0.3.5"
|
"optimist" : "~0.3.5",
|
||||||
|
"uglify-to-browserify": "~1.0.0"
|
||||||
|
},
|
||||||
|
"browserify": {
|
||||||
|
"transform": [ "uglify-to-browserify" ]
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"uglifyjs" : "bin/uglifyjs"
|
"uglifyjs" : "bin/uglifyjs"
|
||||||
|
|||||||
74
test/compress/arrays.js
Normal file
74
test/compress/arrays.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
holes_and_undefined: {
|
||||||
|
input: {
|
||||||
|
w = [1,,];
|
||||||
|
x = [1, 2, undefined];
|
||||||
|
y = [1, , 2, ];
|
||||||
|
z = [1, undefined, 3];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
w=[1,,];
|
||||||
|
x=[1,2,void 0];
|
||||||
|
y=[1,,2];
|
||||||
|
z=[1,void 0,3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_join: {
|
||||||
|
options = {
|
||||||
|
unsafe : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = [ "foo", "bar", "baz" ].join("");
|
||||||
|
var a1 = [ "foo", "bar", "baz" ].join();
|
||||||
|
var b = [ "foo", 1, 2, 3, "bar" ].join("");
|
||||||
|
var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join("");
|
||||||
|
var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join("");
|
||||||
|
var c2 = [ 1, 2, "foo", "bar", baz() ].join("");
|
||||||
|
var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-");
|
||||||
|
var e = [].join(foo + bar);
|
||||||
|
var f = [].join("");
|
||||||
|
var g = [].join("foo");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foobarbaz";
|
||||||
|
var a1 = "foo,bar,baz";
|
||||||
|
var b = "foo123bar";
|
||||||
|
var c = boo() + "foo123bar" + bar();
|
||||||
|
var c1 = "" + boo() + bar() + "foo123bar" + bar();
|
||||||
|
var c2 = "12foobar" + baz();
|
||||||
|
var d = "foo-3bar-baz";
|
||||||
|
var e = [].join(foo + bar);
|
||||||
|
var f = "";
|
||||||
|
var g = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_join_2: {
|
||||||
|
options = {
|
||||||
|
unsafe : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = [ "foo", "bar", boo(), "baz", "x", "y" ].join("");
|
||||||
|
var b = [ "foo", "bar", boo(), "baz", "x", "y" ].join("-");
|
||||||
|
var c = [ "foo", "bar", boo(), "baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var d = [ "foo", "bar", boo(),
|
||||||
|
[ "foo", 1, 2, 3, "bar" ].join("+"),
|
||||||
|
"baz", "x", "y" ].join("-");
|
||||||
|
var e = [ "foo", "bar", boo(),
|
||||||
|
[ "foo", 1, 2, 3, "bar" ].join("+"),
|
||||||
|
"baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var f = [ "str", "str" + variable, "foo", "bar", "moo" + foo ].join("");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foobar" + boo() + "bazxy";
|
||||||
|
var b = [ "foo-bar", boo(), "baz-x-y" ].join("-");
|
||||||
|
var c = [ "foo", "bar", boo(), "baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var d = [ "foo-bar", boo(), "foo+1+2+3+bar-baz-x-y" ].join("-");
|
||||||
|
var e = [ "foo", "bar", boo(),
|
||||||
|
"foo+1+2+3+bar",
|
||||||
|
"baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var f = "strstr" + variable + "foobarmoo" + foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
test/compress/concat-strings.js
Normal file
22
test/compress/concat-strings.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
concat_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = "foo" + "bar" + x() + "moo" + "foo" + y() + "x" + "y" + "z" + q();
|
||||||
|
var b = "foo" + 1 + x() + 2 + "boo";
|
||||||
|
var c = 1 + x() + 2 + "boo";
|
||||||
|
|
||||||
|
// this CAN'T safely be shortened to 1 + x() + "5boo"
|
||||||
|
var d = 1 + x() + 2 + 3 + "boo";
|
||||||
|
|
||||||
|
var e = 1 + x() + 2 + "X" + 3 + "boo";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foobar" + x() + "moofoo" + y() + "xyz" + q();
|
||||||
|
var b = "foo1" + x() + "2boo";
|
||||||
|
var c = 1 + x() + 2 + "boo";
|
||||||
|
var d = 1 + x() + 2 + 3 + "boo";
|
||||||
|
var e = 1 + x() + 2 + "X3boo";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,3 +95,27 @@ unused_circular_references_3: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unused_keep_setter_arg: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
var x = {
|
||||||
|
_foo: null,
|
||||||
|
set foo(val) {
|
||||||
|
},
|
||||||
|
get foo() {
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x = {
|
||||||
|
_foo: null,
|
||||||
|
set foo(val) {
|
||||||
|
},
|
||||||
|
get foo() {
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
25
test/compress/issue-105.js
Normal file
25
test/compress/issue-105.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
typeof_eq_undefined: {
|
||||||
|
options = {
|
||||||
|
comparisons: true
|
||||||
|
};
|
||||||
|
input: { a = typeof b.c != "undefined" }
|
||||||
|
expect: { a = "undefined" != typeof b.c }
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_eq_undefined_unsafe: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
unsafe: true
|
||||||
|
};
|
||||||
|
input: { a = typeof b.c != "undefined" }
|
||||||
|
expect: { a = void 0 !== b.c }
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_eq_undefined_unsafe2: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
unsafe: true
|
||||||
|
};
|
||||||
|
input: { a = "undefined" != typeof b.c }
|
||||||
|
expect: { a = void 0 !== b.c }
|
||||||
|
}
|
||||||
24
test/compress/issue-126.js
Normal file
24
test/compress/issue-126.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
concatenate_rhs_strings: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
foo(bar() + 123 + "Hello" + "World");
|
||||||
|
foo(bar() + (123 + "Hello") + "World");
|
||||||
|
foo((bar() + 123) + "Hello" + "World");
|
||||||
|
foo(bar() + 123 + "Hello" + "World" + ("Foo" + "Bar"));
|
||||||
|
foo("Foo" + "Bar" + bar() + 123 + "Hello" + "World" + ("Foo" + "Bar"));
|
||||||
|
foo("Hello" + bar() + 123 + "World");
|
||||||
|
foo(bar() + 'Foo' + (10 + parseInt('10')));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo(bar() + 123 + "HelloWorld");
|
||||||
|
foo(bar() + "123HelloWorld");
|
||||||
|
foo((bar() + 123) + "HelloWorld");
|
||||||
|
foo(bar() + 123 + "HelloWorldFooBar");
|
||||||
|
foo("FooBar" + bar() + "123HelloWorldFooBar");
|
||||||
|
foo("Hello" + bar() + "123World");
|
||||||
|
foo(bar() + 'Foo' + (10 + parseInt('10')));
|
||||||
|
}
|
||||||
|
}
|
||||||
48
test/compress/issue-143.js
Normal file
48
test/compress/issue-143.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* There was an incorrect sort behaviour documented in issue #143:
|
||||||
|
* (x = f(…)) <= x → x >= (x = f(…))
|
||||||
|
*
|
||||||
|
* For example, let the equation be:
|
||||||
|
* (a = parseInt('100')) <= a
|
||||||
|
*
|
||||||
|
* If a was an integer and has the value of 99,
|
||||||
|
* (a = parseInt('100')) <= a → 100 <= 100 → true
|
||||||
|
*
|
||||||
|
* When transformed incorrectly:
|
||||||
|
* a >= (a = parseInt('100')) → 99 >= 100 → false
|
||||||
|
*/
|
||||||
|
|
||||||
|
tranformation_sort_order_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) == a }
|
||||||
|
expect: { (a = parseInt('100')) == a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_unequal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) != a }
|
||||||
|
expect: { (a = parseInt('100')) != a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_lesser_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) <= a }
|
||||||
|
expect: { (a = parseInt('100')) <= a }
|
||||||
|
}
|
||||||
|
tranformation_sort_order_greater_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) >= a }
|
||||||
|
expect: { (a = parseInt('100')) >= a }
|
||||||
|
}
|
||||||
11
test/compress/issue-267.js
Normal file
11
test/compress/issue-267.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
issue_267: {
|
||||||
|
options = { comparisons: true };
|
||||||
|
input: {
|
||||||
|
x = a % b / b * c * 2;
|
||||||
|
x = a % b * 2
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x = a % b / b * c * 2;
|
||||||
|
x = a % b * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
test/compress/issue-269.js
Normal file
66
test/compress/issue-269.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
issue_269_1: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
f(
|
||||||
|
String(x),
|
||||||
|
Number(x),
|
||||||
|
Boolean(x),
|
||||||
|
|
||||||
|
String(),
|
||||||
|
Number(),
|
||||||
|
Boolean()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f(
|
||||||
|
x + '', +x, !!x,
|
||||||
|
'', 0, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_269_dangers: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
f(
|
||||||
|
String(x, x),
|
||||||
|
Number(x, x),
|
||||||
|
Boolean(x, x)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f(String(x, x), Number(x, x), Boolean(x, x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_269_in_scope: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
var String, Number, Boolean;
|
||||||
|
f(
|
||||||
|
String(x),
|
||||||
|
Number(x, x),
|
||||||
|
Boolean(x)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var String, Number, Boolean;
|
||||||
|
f(String(x), Number(x, x), Boolean(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_concat: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
f(
|
||||||
|
String(x + 'str'),
|
||||||
|
String('str' + x)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f(
|
||||||
|
x + 'str',
|
||||||
|
'str' + x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
test/compress/issue-59.js
Normal file
30
test/compress/issue-59.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
keep_continue: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
while (a) {
|
||||||
|
if (b) {
|
||||||
|
switch (true) {
|
||||||
|
case c():
|
||||||
|
d();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
while (a) {
|
||||||
|
if (b) {
|
||||||
|
switch (true) {
|
||||||
|
case c():
|
||||||
|
d();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
test/compress/negate-iife.js
Normal file
76
test/compress/negate-iife.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
negate_iife_1: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ stuff() })();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ stuff() }();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_2: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return {} })().x = 10; // should not transform this one
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(){ return {} })().x = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_3: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_3: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
sequences: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
|
(function(){
|
||||||
|
console.log("something");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
||||||
|
console.log("something");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_4: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
sequences: true,
|
||||||
|
conditionals: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if ((function(){ return true })()) {
|
||||||
|
console.log(true);
|
||||||
|
} else {
|
||||||
|
console.log(false);
|
||||||
|
}
|
||||||
|
(function(){
|
||||||
|
console.log("something");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
||||||
|
console.log("something");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,9 +17,38 @@ dot_properties: {
|
|||||||
input: {
|
input: {
|
||||||
a["foo"] = "bar";
|
a["foo"] = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dot_properties_es5: {
|
||||||
|
options = {
|
||||||
|
properties: true,
|
||||||
|
screw_ie8: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a["foo"] = "bar";
|
||||||
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a.foo = "bar";
|
||||||
|
a.if = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,10 +101,12 @@ lift_sequences_1: {
|
|||||||
lift_sequences_2: {
|
lift_sequences_2: {
|
||||||
options = { sequences: true, evaluate: true };
|
options = { sequences: true, evaluate: true };
|
||||||
input: {
|
input: {
|
||||||
q = 1 + (foo(), bar(), 5) + 7 * (5 / (3 - (a(), (QW=ER), c(), 2))) - (x(), y(), 5);
|
foo.x = (foo = {}, 10);
|
||||||
|
bar = (bar = {}, 10);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
foo(), bar(), a(), QW = ER, c(), x(), y(), q = 36
|
foo.x = (foo = {}, 10),
|
||||||
|
bar = {}, bar = 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -208,3 +208,53 @@ constant_switch_9: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop_default_1: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_default_2: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz(); break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_default: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
something();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
25
test/compress/typeof.js
Normal file
25
test/compress/typeof.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
typeof_evaluation: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a = typeof 1;
|
||||||
|
b = typeof 'test';
|
||||||
|
c = typeof [];
|
||||||
|
d = typeof {};
|
||||||
|
e = typeof /./;
|
||||||
|
f = typeof false;
|
||||||
|
g = typeof function(){};
|
||||||
|
h = typeof undefined;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a='number';
|
||||||
|
b='string';
|
||||||
|
c=typeof[];
|
||||||
|
d=typeof{};
|
||||||
|
e=typeof/./;
|
||||||
|
f='boolean';
|
||||||
|
g='function';
|
||||||
|
h='undefined';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,15 @@ var assert = require("assert");
|
|||||||
var sys = require("util");
|
var sys = require("util");
|
||||||
|
|
||||||
var tests_dir = path.dirname(module.filename);
|
var tests_dir = path.dirname(module.filename);
|
||||||
|
var failures = 0;
|
||||||
|
var failed_files = {};
|
||||||
|
|
||||||
run_compress_tests();
|
run_compress_tests();
|
||||||
|
if (failures) {
|
||||||
|
sys.error("\n!!! Failed " + failures + " test cases.");
|
||||||
|
sys.error("!!! " + Object.keys(failed_files).join(", "));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
@@ -83,6 +90,8 @@ function run_compress_tests() {
|
|||||||
output: output,
|
output: output,
|
||||||
expected: expect
|
expected: expect
|
||||||
});
|
});
|
||||||
|
failures++;
|
||||||
|
failed_files[file] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var tests = parse_test(path.resolve(dir, file));
|
var tests = parse_test(path.resolve(dir, file));
|
||||||
|
|||||||
@@ -56,10 +56,15 @@ exports.minify = function(files, options) {
|
|||||||
inSourceMap : null,
|
inSourceMap : null,
|
||||||
fromString : false,
|
fromString : false,
|
||||||
warnings : false,
|
warnings : false,
|
||||||
|
mangle : {},
|
||||||
|
output : null,
|
||||||
|
compress : {}
|
||||||
});
|
});
|
||||||
if (typeof files == "string")
|
if (typeof files == "string")
|
||||||
files = [ files ];
|
files = [ files ];
|
||||||
|
|
||||||
|
UglifyJS.base54.reset();
|
||||||
|
|
||||||
// 1. parse
|
// 1. parse
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
@@ -73,33 +78,42 @@ exports.minify = function(files, options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 2. compress
|
// 2. compress
|
||||||
toplevel.figure_out_scope();
|
if (options.compress) {
|
||||||
var sq = UglifyJS.Compressor({
|
var compress = { warnings: options.warnings };
|
||||||
warnings: options.warnings,
|
UglifyJS.merge(compress, options.compress);
|
||||||
});
|
toplevel.figure_out_scope();
|
||||||
toplevel = toplevel.transform(sq);
|
var sq = UglifyJS.Compressor(compress);
|
||||||
|
toplevel = toplevel.transform(sq);
|
||||||
|
}
|
||||||
|
|
||||||
// 3. mangle
|
// 3. mangle
|
||||||
toplevel.figure_out_scope();
|
if (options.mangle) {
|
||||||
toplevel.compute_char_frequency();
|
toplevel.figure_out_scope();
|
||||||
toplevel.mangle_names();
|
toplevel.compute_char_frequency();
|
||||||
|
toplevel.mangle_names(options.mangle);
|
||||||
|
}
|
||||||
|
|
||||||
// 4. output
|
// 4. output
|
||||||
var map = null;
|
var inMap = options.inSourceMap;
|
||||||
var inMap = null;
|
var output = {};
|
||||||
if (options.inSourceMap) {
|
if (typeof options.inSourceMap == "string") {
|
||||||
inMap = fs.readFileSync(options.inSourceMap, "utf8");
|
inMap = fs.readFileSync(options.inSourceMap, "utf8");
|
||||||
}
|
}
|
||||||
if (options.outSourceMap) map = UglifyJS.SourceMap({
|
if (options.outSourceMap) {
|
||||||
file: options.outSourceMap,
|
output.source_map = UglifyJS.SourceMap({
|
||||||
orig: inMap,
|
file: options.outSourceMap,
|
||||||
root: options.sourceRoot
|
orig: inMap,
|
||||||
});
|
root: options.sourceRoot
|
||||||
var stream = UglifyJS.OutputStream({ source_map: map });
|
});
|
||||||
|
}
|
||||||
|
if (options.output) {
|
||||||
|
UglifyJS.merge(output, options.output);
|
||||||
|
}
|
||||||
|
var stream = UglifyJS.OutputStream(output);
|
||||||
toplevel.print(stream);
|
toplevel.print(stream);
|
||||||
return {
|
return {
|
||||||
code : stream + "",
|
code : stream + "",
|
||||||
map : map + ""
|
map : output.source_map + ""
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user