Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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"
|
||||||
146
README.md
146
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.
|
||||||
|
|
||||||
@@ -45,55 +46,64 @@ 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
|
||||||
--source-map-url The path to the source map to be added in //@
|
source map. [string]
|
||||||
sourceMappingURL. Defaults to the value passed with
|
--source-map-url The path to the source map to be added in //#
|
||||||
--source-map. [string]
|
sourceMappingURL. Defaults to the value passed with
|
||||||
--in-source-map Input source map, useful if you're compressing JS that was
|
--source-map. [string]
|
||||||
generated from some other original code.
|
--in-source-map Input source map, useful if you're compressing JS that was
|
||||||
--screw-ie8 Pass this flag if you don't care about full compliance with
|
generated from some other original code.
|
||||||
Internet Explorer 6-8 quirks (by default UglifyJS will try
|
--screw-ie8 Pass this flag if you don't care about full compliance
|
||||||
to be IE-proof).
|
with Internet Explorer 6-8 quirks (by default UglifyJS
|
||||||
-p, --prefix Skip prefix for original filenames that appear in source
|
will try to be IE-proof). [boolean]
|
||||||
maps. For example -p 3 will drop 3 directories from file
|
--expr Parse a single expression, rather than a program (for
|
||||||
names and ensure they are relative paths.
|
parsing JSON) [boolean]
|
||||||
-o, --output Output file (default STDOUT).
|
-p, --prefix Skip prefix for original filenames that appear in source
|
||||||
-b, --beautify Beautify output/specify output options. [string]
|
maps. For example -p 3 will drop 3 directories from file
|
||||||
-m, --mangle Mangle names/pass mangler options. [string]
|
names and ensure they are relative paths. You can also
|
||||||
-r, --reserved Reserved names to exclude from mangling.
|
specify -p relative, which will make UglifyJS figure out
|
||||||
-c, --compress Enable compressor/pass compressor options. Pass options
|
itself the relative paths between original sources, the
|
||||||
like -c hoist_vars=false,if_return=false. Use -c with no
|
source map and the output file. [string]
|
||||||
argument to use the default compression options. [string]
|
-o, --output Output file (default STDOUT).
|
||||||
-d, --define Global definitions [string]
|
-b, --beautify Beautify output/specify output options. [string]
|
||||||
--comments Preserve copyright comments in the output. By default this
|
-m, --mangle Mangle names/pass mangler options. [string]
|
||||||
works like Google Closure, keeping JSDoc-style comments
|
-r, --reserved Reserved names to exclude from mangling.
|
||||||
that contain "@license" or "@preserve". You can optionally
|
-c, --compress Enable compressor/pass compressor options. Pass options
|
||||||
pass one of the following arguments to this flag:
|
like -c hoist_vars=false,if_return=false. Use -c with no
|
||||||
- "all" to keep all comments
|
argument to use the default compression options. [string]
|
||||||
- a valid JS regexp (needs to start with a slash) to keep
|
-d, --define Global definitions [string]
|
||||||
only comments that match.
|
-e, --enclose Embed everything in a big function, with a configurable
|
||||||
Note that currently not *all* comments can be kept when
|
parameter/argument list. [string]
|
||||||
compression is on, because of dead code removal or
|
--comments Preserve copyright comments in the output. By default this
|
||||||
cascading statements into sequences. [string]
|
works like Google Closure, keeping JSDoc-style comments
|
||||||
--stats Display operations run time on STDERR. [boolean]
|
that contain "@license" or "@preserve". You can optionally
|
||||||
--acorn Use Acorn for parsing. [boolean]
|
pass one of the following arguments to this flag:
|
||||||
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
|
- "all" to keep all comments
|
||||||
[boolean]
|
- a valid JS regexp (needs to start with a slash) to keep
|
||||||
--self Build itself (UglifyJS2) as a library (implies
|
only comments that match.
|
||||||
--wrap=UglifyJS --export-all) [boolean]
|
Note that currently not *all* comments can be kept when
|
||||||
--wrap Embed everything in a big function, making the “exports”
|
compression is on, because of dead code removal or
|
||||||
and “global” variables available. You need to pass an
|
cascading statements into sequences. [string]
|
||||||
argument to this option to specify the name that your
|
--stats Display operations run time on STDERR. [boolean]
|
||||||
module will take when included in, say, a browser.
|
--acorn Use Acorn for parsing. [boolean]
|
||||||
[string]
|
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
|
||||||
--export-all Only used when --wrap, this tells UglifyJS to add code to
|
[boolean]
|
||||||
automatically export all globals. [boolean]
|
--self Build itself (UglifyJS2) as a library (implies
|
||||||
--lint Display some scope warnings [boolean]
|
--wrap=UglifyJS --export-all) [boolean]
|
||||||
-v, --verbose Verbose [boolean]
|
--wrap Embed everything in a big function, making the “exports”
|
||||||
-V, --version Print version number and exit. [boolean]
|
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.
|
||||||
@@ -174,32 +184,67 @@ you can pass a comma-separated list of options. Options are in the form
|
|||||||
to set `true`; it's effectively a shortcut for `foo=true`).
|
to set `true`; it's effectively a shortcut for `foo=true`).
|
||||||
|
|
||||||
- `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
|
- `dead_code` -- remove unreachable code
|
||||||
|
|
||||||
- `drop_debugger` -- remove `debugger;` statements
|
- `drop_debugger` -- remove `debugger;` statements
|
||||||
|
|
||||||
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
|
- `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_funs` -- hoist function declarations
|
||||||
|
|
||||||
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
|
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
|
||||||
by default because it seems to increase the size of the output in general)
|
by default because it seems to increase the size of the output in general)
|
||||||
|
|
||||||
- `if_return` -- optimizations for if/return and if/continue
|
- `if_return` -- optimizations for if/return and if/continue
|
||||||
|
|
||||||
- `join_vars` -- join consecutive `var` statements
|
- `join_vars` -- join consecutive `var` statements
|
||||||
|
|
||||||
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
||||||
and `x = something(), x` into `x = something()`
|
and `x = something(), x` into `x = something()`
|
||||||
|
|
||||||
- `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
|
### The `unsafe` option
|
||||||
|
|
||||||
It enables some transformations that *might* break code logic in certain
|
It enables some transformations that *might* break code logic in certain
|
||||||
@@ -276,9 +321,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.
|
||||||
|
|||||||
69
bin/uglifyjs
69
bin/uglifyjs
@@ -7,6 +7,7 @@ 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 async = require("async");
|
||||||
var acorn;
|
var acorn;
|
||||||
var ARGS = optimist
|
var ARGS = optimist
|
||||||
@@ -20,11 +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("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("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.")
|
||||||
@@ -76,6 +80,9 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.string("e")
|
.string("e")
|
||||||
.string("comments")
|
.string("comments")
|
||||||
.string("wrap")
|
.string("wrap")
|
||||||
|
.string("p")
|
||||||
|
|
||||||
|
.boolean("expr")
|
||||||
.boolean("screw-ie8")
|
.boolean("screw-ie8")
|
||||||
.boolean("export-all")
|
.boolean("export-all")
|
||||||
.boolean("self")
|
.boolean("self")
|
||||||
@@ -122,11 +129,6 @@ if (ARGS.d) {
|
|||||||
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ARGS.screw_ie8) {
|
|
||||||
if (COMPRESS) COMPRESS.screw_ie8 = true;
|
|
||||||
if (MANGLE) MANGLE.screw_ie8 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ARGS.r) {
|
if (ARGS.r) {
|
||||||
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||||
}
|
}
|
||||||
@@ -135,6 +137,12 @@ var OUTPUT_OPTIONS = {
|
|||||||
beautify: BEAUTIFY ? true : false
|
beautify: BEAUTIFY ? true : false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -196,9 +204,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;
|
||||||
@@ -220,11 +229,18 @@ try {
|
|||||||
async.eachLimit(files, 1, function (file, cb) {
|
async.eachLimit(files, 1, function (file, cb) {
|
||||||
read_whole_file(file, function (err, code) {
|
read_whole_file(file, function (err, code) {
|
||||||
if (err) {
|
if (err) {
|
||||||
sys.error("ERROR: can't read file: " + filename);
|
sys.error("ERROR: can't read file: " + file);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
if (ARGS.p != null) {
|
if (ARGS.p != null) {
|
||||||
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
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(){
|
time_it("parse", function(){
|
||||||
if (ARGS.spidermonkey) {
|
if (ARGS.spidermonkey) {
|
||||||
@@ -241,10 +257,21 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TOPLEVEL = UglifyJS.parse(code, {
|
try {
|
||||||
filename: file,
|
TOPLEVEL = UglifyJS.parse(code, {
|
||||||
toplevel: TOPLEVEL
|
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();
|
cb();
|
||||||
@@ -260,11 +287,12 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
|
|
||||||
if (ARGS.enclose) {
|
if (ARGS.enclose) {
|
||||||
var arg_parameter_list = ARGS.enclose;
|
var arg_parameter_list = ARGS.enclose;
|
||||||
|
if (arg_parameter_list === true) {
|
||||||
if (!(arg_parameter_list instanceof Array)) {
|
arg_parameter_list = [];
|
||||||
|
}
|
||||||
|
else if (!(arg_parameter_list instanceof Array)) {
|
||||||
arg_parameter_list = [arg_parameter_list];
|
arg_parameter_list = [arg_parameter_list];
|
||||||
}
|
}
|
||||||
|
|
||||||
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
|
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +333,12 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
|
|
||||||
if (SOURCE_MAP) {
|
if (SOURCE_MAP) {
|
||||||
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
||||||
output += "\n/*\n//@ sourceMappingURL=" + (ARGS.source_map_url || ARGS.source_map) + "\n*/";
|
var source_map_url = ARGS.source_map_url || (
|
||||||
|
P_RELATIVE
|
||||||
|
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
|
||||||
|
: ARGS.source_map
|
||||||
|
);
|
||||||
|
output += "\n//# sourceMappingURL=" + source_map_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OUTPUT_FILE) {
|
if (OUTPUT_FILE) {
|
||||||
|
|||||||
41
lib/ast.js
41
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",
|
||||||
@@ -821,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);
|
||||||
|
|
||||||
@@ -945,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];
|
||||||
@@ -965,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
459
lib/compress.js
459
lib/compress.js
@@ -66,6 +66,9 @@ 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,
|
screw_ie8 : false,
|
||||||
|
|
||||||
warnings : true,
|
warnings : true,
|
||||||
@@ -89,15 +92,7 @@ merge(Compressor.prototype, {
|
|||||||
descend(node, this);
|
descend(node, this);
|
||||||
node = node.optimize(this);
|
node = node.optimize(this);
|
||||||
if (node instanceof AST_Scope) {
|
if (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;
|
|
||||||
}
|
}
|
||||||
node._squeezed = true;
|
node._squeezed = true;
|
||||||
return node;
|
return node;
|
||||||
@@ -214,6 +209,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) {
|
||||||
@@ -318,7 +318,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);
|
||||||
@@ -340,7 +340,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();
|
||||||
@@ -379,7 +379,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);
|
||||||
@@ -497,6 +497,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) {
|
||||||
@@ -590,14 +624,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 ];
|
||||||
@@ -611,10 +645,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
|
||||||
@@ -622,69 +658,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":
|
case "typeof":
|
||||||
// Function would be evaluated to an array and so typeof would
|
// Function would be evaluated to an array and so typeof would
|
||||||
// incorrectly return 'object'. Hence making is a special case.
|
// incorrectly return 'object'. Hence making is a special case.
|
||||||
if (e instanceof AST_Function) return typeof function(){};
|
if (e instanceof AST_Function) return typeof function(){};
|
||||||
|
|
||||||
e = ev(e);
|
e = ev(e, compressor);
|
||||||
|
|
||||||
// typeof <RegExp> returns "object" or "function" on different platforms
|
// typeof <RegExp> returns "object" or "function" on different platforms
|
||||||
// so cannot evaluate reliably
|
// so cannot evaluate reliably
|
||||||
if (e instanceof RegExp) throw def;
|
if (e instanceof RegExp) throw def;
|
||||||
|
|
||||||
return typeof e;
|
return typeof e;
|
||||||
case "void": return void ev(e);
|
case "void": return void ev(e, compressor);
|
||||||
case "~": return ~ev(e);
|
case "~": return ~ev(e, compressor);
|
||||||
case "-":
|
case "-":
|
||||||
e = ev(e);
|
e = ev(e, compressor);
|
||||||
if (e === 0) throw def;
|
if (e === 0) throw def;
|
||||||
return -e;
|
return -e;
|
||||||
case "+": return +ev(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 && d.init) return ev(d.init);
|
if (d && d.constant && d.init) return ev(d.init, compressor);
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
@@ -760,70 +796,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);
|
||||||
@@ -907,7 +951,7 @@ merge(Compressor.prototype, {
|
|||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
if (def.value) {
|
if (def.value) {
|
||||||
initializations.add(def.name.name, def.value);
|
initializations.add(def.name.name, def.value);
|
||||||
if (def.value.has_side_effects()) {
|
if (def.value.has_side_effects(compressor)) {
|
||||||
def.value.walk(tw);
|
def.value.walk(tw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -948,7 +992,7 @@ merge(Compressor.prototype, {
|
|||||||
// pass 3: we should drop declarations not in_use
|
// pass 3: we should drop declarations not in_use
|
||||||
var tt = new TreeTransformer(
|
var tt = new TreeTransformer(
|
||||||
function before(node, descend, in_list) {
|
function before(node, descend, in_list) {
|
||||||
if (node instanceof AST_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()) {
|
||||||
@@ -984,7 +1028,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;
|
||||||
@@ -1186,7 +1230,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);
|
||||||
}
|
}
|
||||||
@@ -1589,6 +1633,56 @@ merge(Compressor.prototype, {
|
|||||||
operator: "+",
|
operator: "+",
|
||||||
right: make_node(AST_String, self, { value: "" })
|
right: make_node(AST_String, self, { value: "" })
|
||||||
});
|
});
|
||||||
|
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) {
|
||||||
@@ -1598,15 +1692,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){
|
||||||
@@ -1629,7 +1770,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).
|
||||||
@@ -1644,12 +1785,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;
|
||||||
}
|
}
|
||||||
@@ -1711,7 +1852,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (this.right instanceof AST_Seq
|
if (this.right instanceof AST_Seq
|
||||||
&& !(this.operator == "||" || this.operator == "&&")
|
&& !(this.operator == "||" || this.operator == "&&")
|
||||||
&& !this.left.has_side_effects()) {
|
&& !this.left.has_side_effects(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();
|
||||||
@@ -1726,14 +1867,15 @@ merge(Compressor.prototype, {
|
|||||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor){
|
OPT(AST_Binary, function(self, compressor){
|
||||||
function reverse(op, force) {
|
var reverse = compressor.has_directive("use asm") ? noop
|
||||||
if (force || !(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)) {
|
||||||
@@ -1742,6 +1884,32 @@ merge(Compressor.prototype, {
|
|||||||
// result. hence, force switch.
|
// result. hence, force switch.
|
||||||
reverse(null, true);
|
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);
|
||||||
if (compressor.option("comparisons")) switch (self.operator) {
|
if (compressor.option("comparisons")) switch (self.operator) {
|
||||||
@@ -1807,11 +1975,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) {
|
||||||
@@ -1831,7 +1994,62 @@ 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 = 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self.evaluate(compressor)[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_SymbolRef, function(self, compressor){
|
OPT(AST_SymbolRef, function(self, compressor){
|
||||||
@@ -1965,8 +2183,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 (compressor.option("screw_ie8") && RESERVED_WORDS(prop)
|
if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
|
||||||
|| !(RESERVED_WORDS(prop)) && 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
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ 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,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
@@ -95,7 +95,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;
|
||||||
});
|
});
|
||||||
@@ -350,18 +350,17 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||||
var self = this, generator = self._codegen;
|
var self = this, generator = self._codegen;
|
||||||
stream.push_node(self);
|
function doit() {
|
||||||
if (force_parens || 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_comments(stream);
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
generator(self, stream);
|
generator(self, stream);
|
||||||
}
|
}
|
||||||
|
stream.push_node(self);
|
||||||
|
if (force_parens || self.needs_parens(stream)) {
|
||||||
|
stream.with_parens(doit);
|
||||||
|
} else {
|
||||||
|
doit();
|
||||||
|
}
|
||||||
stream.pop_node();
|
stream.pop_node();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -400,7 +399,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();
|
||||||
}
|
}
|
||||||
@@ -757,7 +756,7 @@ function OutputStream(options) {
|
|||||||
if (!self.body)
|
if (!self.body)
|
||||||
return output.force_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
|
||||||
@@ -990,7 +989,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){
|
||||||
@@ -1012,6 +1022,11 @@ function OutputStream(options) {
|
|||||||
a.forEach(function(exp, i){
|
a.forEach(function(exp, i){
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
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();
|
||||||
});
|
});
|
||||||
@@ -1039,10 +1054,10 @@ function OutputStream(options) {
|
|||||||
&& +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);
|
||||||
@@ -1119,7 +1134,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 ) ||
|
||||||
|
|||||||
119
lib/parse.js
119
lib/parse.js
@@ -168,7 +168,10 @@ function is_identifier_char(ch) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function is_identifier_string(str){
|
function is_identifier_string(str){
|
||||||
for (var i = str.length; --i >= 0;) {
|
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)))
|
if (!is_identifier_char(str.charAt(i)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -207,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, ''),
|
||||||
@@ -239,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;
|
||||||
@@ -251,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,
|
||||||
@@ -376,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);
|
||||||
@@ -386,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;
|
||||||
@@ -400,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() {
|
||||||
@@ -465,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("/");
|
||||||
};
|
};
|
||||||
@@ -488,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)
|
||||||
@@ -510,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);
|
||||||
@@ -585,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,
|
||||||
@@ -684,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();
|
||||||
@@ -817,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 });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -825,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_() {
|
||||||
@@ -1275,6 +1326,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();
|
||||||
@@ -1338,15 +1390,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) {
|
||||||
@@ -1390,6 +1435,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 = [];
|
||||||
|
|||||||
41
lib/scope.js
41
lib/scope.js
@@ -82,18 +82,14 @@ 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) {
|
||||||
node.init_scope_vars(nesting);
|
node.init_scope_vars(nesting);
|
||||||
var save_scope = node.parent_scope = scope;
|
var save_scope = node.parent_scope = scope;
|
||||||
var save_labels = labels;
|
|
||||||
++nesting;
|
++nesting;
|
||||||
scope = node;
|
scope = node;
|
||||||
labels = new Dictionary();
|
|
||||||
descend();
|
descend();
|
||||||
labels = save_labels;
|
|
||||||
scope = save_scope;
|
scope = save_scope;
|
||||||
--nesting;
|
--nesting;
|
||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
@@ -108,22 +104,9 @@ 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_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);
|
||||||
}
|
}
|
||||||
@@ -150,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);
|
||||||
|
|
||||||
@@ -173,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);
|
||||||
@@ -187,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;
|
||||||
@@ -194,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 {
|
||||||
@@ -240,14 +211,6 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
this.frame = this.scope.nesting - def.scope.nesting;
|
this.frame = this.scope.nesting - def.scope.nesting;
|
||||||
});
|
});
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -3,21 +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.3.1",
|
"version": "2.4.1",
|
||||||
"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",
|
"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"
|
||||||
|
|||||||
@@ -1,12 +1,74 @@
|
|||||||
holes_and_undefined: {
|
holes_and_undefined: {
|
||||||
input: {
|
input: {
|
||||||
|
w = [1,,];
|
||||||
x = [1, 2, undefined];
|
x = [1, 2, undefined];
|
||||||
y = [1, , 2, ];
|
y = [1, , 2, ];
|
||||||
z = [1, undefined, 3];
|
z = [1, undefined, 3];
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
w=[1,,];
|
||||||
x=[1,2,void 0];
|
x=[1,2,void 0];
|
||||||
y=[1,,2];
|
y=[1,,2];
|
||||||
z=[1,void 0,3];
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
22
test/compress/issue-126.js
Normal file
22
test/compress/issue-126.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo(bar() + 123 + "HelloWorld");
|
||||||
|
foo(bar() + "123HelloWorld");
|
||||||
|
foo((bar() + 123) + "HelloWorld");
|
||||||
|
foo(bar() + 123 + "HelloWorldFooBar");
|
||||||
|
foo("FooBar" + bar() + "123HelloWorldFooBar");
|
||||||
|
foo("Hello" + bar() + "123World");
|
||||||
|
}
|
||||||
|
}
|
||||||
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");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,12 +19,16 @@ dot_properties: {
|
|||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
a["*"] = "asterisk";
|
a["*"] = "asterisk";
|
||||||
a["\u0EB3"] = "unicode";
|
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["*"] = "asterisk";
|
||||||
a.\u0EB3 = "unicode";
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,11 +42,13 @@ dot_properties_es5: {
|
|||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
a["*"] = "asterisk";
|
a["*"] = "asterisk";
|
||||||
a["\u0EB3"] = "unicode";
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a.if = "if";
|
a.if = "if";
|
||||||
a["*"] = "asterisk";
|
a["*"] = "asterisk";
|
||||||
a.\u0EB3 = "unicode";
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ exports.minify = function(files, options) {
|
|||||||
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){
|
||||||
|
|||||||
Reference in New Issue
Block a user