Compare commits

...

83 Commits

Author SHA1 Message Date
Mihai Bazon
d895c09c70 v2.6.0 2015-11-12 12:46:28 +02:00
Mihai Bazon
08623aa6a7 Fix output for "use asm" code from SpiderMonkey AST
(will only work properly if the SM tree contains "raw" properties for
Literal number nodes)
2015-11-12 12:18:25 +02:00
Mihai Bazon
c898a26117 Build label def/refs info when figuring out scope
Fix #862
2015-11-12 11:48:06 +02:00
Mihai Bazon
619adb0308 Replace util.error with console.log 2015-11-12 11:47:37 +02:00
Mihai Bazon
7691bebea5 Rework has_directive
It's now available during tree walking, i.e. walker.has_directive("use
asm"), rather than as part of the scope.  It's thus no longer necessary
to call `figure_out_scope` before codegen.  Added special bits in the
code generator to overcome the fact that it doesn't inherit from
TreeWalker.

Fix #861
2015-11-11 22:15:25 +02:00
Mihai Bazon
3c4346728e Merge pull request #854 from kzc/moz-regexp-2
Have mozilla AST RegExpLiteral parser use regex.pattern and regex.flags
2015-11-10 10:12:30 +02:00
Mihai Bazon
18d37ac761 Fix parsing invalid input
i.e. `x = 1.xe` — because parseFloat("1.xe") returns 1, this parsed as
`x = 1`.

Ref #857
2015-11-09 13:15:20 +02:00
Richard van Velzen
63d35f8f6d Prevent ReDoS by not using a regexp to verify floating point numbers
`parseFloat` will return `NaN` for invalid numbers anyway, which is the check used to throw the parse error.

Fixes #857
2015-11-09 11:28:27 +01:00
kzc
7dbe961b2d simplify mozilla AST RegExpLiteral token parse and handle corner cases of regex.pattern better 2015-11-02 13:10:37 -05:00
kzc
94c4daaf9e Have mozilla AST RegExpLiteral parser use regex.pattern and regex.flags rather than non-standard raw property. 2015-11-02 12:24:09 -05:00
kzc
37ee9de902 rename To_Moz_Literal to To_Moz_RegExp 2015-11-01 10:20:42 -05:00
kzc
83db98ad3b Fixed RegExp literal in mozilla AST generation/output and added a --dump-spidermonkey-ast flag 2015-11-01 01:02:52 -04:00
kzc
bd0ae6569f return undefined optimization no longer uses return_void_0 option 2015-10-29 08:19:12 +01:00
kzc
841a661071 more tests for return undefined optimization 2015-10-29 08:19:12 +01:00
kzc
7491d07666 optimize return undefined and return void 0 2015-10-29 08:19:12 +01:00
Richard van Velzen
335e349314 Allow specification beautify options in tests
Caught an error in #847 as well - `output` wasn't passed anywhere which led to an exception. `options` was available though.
2015-10-28 20:50:01 +01:00
Richard van Velzen
2a88d07b3a Stop building for io.js 2015-10-28 20:36:03 +01:00
Michael Ficarra
a887cde9f2 fixes #845: \v escaping should be restricted to "screw_ie8" mode 2015-10-27 09:05:21 -07:00
Fábio Santos
b5623b19d4 Fix #836 2015-10-20 19:48:56 +01:00
startswithaj
6b2861e086 Make_string was missing \v and wasnt reversing vertical tabs even though read_escaped_char coverts them 2015-10-15 17:42:16 +10:00
Damian Krzeminski
d5138f7467 add --pure-funcs option
it has the same effect as specifying `pure_funcs` in `--compressor`
option, however it's much easier to use

instead of:

    --compressor 'pure_func=["Math.floor","debug","console.logTime"]'

it's now possible:

    --compressor --pure-funcs Math.floor debug console.logTime

fixes #684
2015-10-13 21:24:14 -04:00
Damian Krzeminski
eac67b2816 upgrade yargs 3.5.4 -> 3.10.0
we need a version with better support for 'array' params
see: https://github.com/bcoe/yargs/pull/164
2015-10-13 21:01:36 -04:00
Mihai Bazon
ce10072824 Merge pull request #829 from kzc/html_comment_ops
Fix other operator output producing <!-- or -->
2015-10-13 09:59:40 +03:00
kzc
dff54a6552 Fix other operator output related to <!-- or --> 2015-10-13 01:17:10 -04:00
Mihai Bazon
1940fb682c Fix tests 2015-10-12 10:27:00 +03:00
Mihai Bazon
17eef5a3c2 Only encode <!-- and --> in strings when inline_script 2015-10-12 10:21:22 +03:00
kzc
9f1f21b810 Output -- > instead of --> in expressions. Escape <!-- and --> within string literals. 2015-10-12 10:19:17 +03:00
Mihai Bazon
a8e67d157e v2.5.0 2015-10-11 18:24:38 +03:00
kzc
e870c7db45 have minify() call figure_out_scope() if needed to produce well formed "use asm" code 2015-10-07 16:31:57 -04:00
kzc
6500f8c52c get rid of SCOPE_IS_NEEDED as it was always true 2015-10-07 15:33:24 -04:00
kzc
4d2f7d83af Fix handling of "use asm" when no command line flags are passed to uglifyjs. SCOPE_IS_NEEDED is unconditionally true now. Refactored floating point literal parsing to be more in keeping with the AST class design. 2015-10-07 13:10:53 -04:00
SpainTrain
99945fcd04 Pin dependencies with npm shrinkwrap
* Use `npm run shrinkwrap` to create a shrinkwrap file with all dependencies pinned
* Update dependency `source-map` to latest (Closes #738)
2015-10-07 13:52:49 +02:00
kzc
0d952ae43d add asm.js test 2015-10-07 10:00:28 +02:00
kzc
593677d2ff Add proper support for "use asm"; blocks. Disable -c optimization within "use asm"; sections and preserve floating point literals in their original form. Non-asm.js sections are optimized as before. Asm.js sections can still be mangled and minified of whitespace. No special command line flags are required. 2015-10-07 10:00:28 +02:00
Anthony Van de Gejuchte
c69294c449 Implement shebang support 2015-10-06 22:35:45 +02:00
Mihai Bazon
2a06c7758e Merge pull request #808 from avdg/travis
Add node 4.x in Travis
2015-09-24 19:27:54 +03:00
Anthony Van de Gejuchte
7ee1ec91a2 Add node 4.x in Travis 2015-09-24 17:41:52 +02:00
Mihai Bazon
233fb62bd8 Disable node 0.8 in Travis 2015-09-24 18:26:23 +03:00
Mihai Bazon
6637c267a5 Fix mozilla-ast after module loading changes
Need to explicitly qualify stuff now, since it's not evaluated in some
global scope.

Ref #636
2015-09-24 18:13:21 +03:00
Mihai Bazon
99233c44cc No longer use vm to load code.
Improves performance 2x on node > 0.10.

Ref #636
2015-09-24 17:58:51 +03:00
Mihai Bazon
33528002b4 Fix wrap_commonjs to include code first
(code could have directives, i.e. "use strict")
2015-09-24 17:58:51 +03:00
Kyle Mitchell
20542a37a8 use a valid SPDX license identifier 2015-09-14 19:44:49 +02:00
Ville Lautanala
5fd12451f9 Control keeping function arguments with a single option 2015-09-14 19:38:45 +02:00
Richard van Velzen
ba939ccd6c Merge pull request #786 from istr/anonymous-source-map
Allow for anonymous map generation using string type check
2015-09-06 17:06:14 +02:00
Ingo Struck
3a5f354846 allow for anonymous map generation using string type check 2015-08-27 19:38:33 +02:00
Richard van Velzen
fcde6109b0 Fix bad parsing of new new x()() constructs
Fixes #739
2015-08-27 12:29:36 +03:00
Richard van Velzen
e3bd223dac Don't change sequences that influence lexical binding in calls
Fixes #782
2015-08-25 10:53:35 +02:00
Richard van Velzen
6c8db6eae1 Merge pull request #767 from vjeux/208
[Fix] --define replaces SymbolRefs in LHS of assignments
2015-08-10 20:29:37 +02:00
Christopher Chedeau
3ff0b9e0c9 [Fix] --define replaces SymbolRefs in LHS of assignments
See #208 for context
2015-08-10 11:22:36 -07:00
Richard van Velzen
464a942a95 Merge pull request #736 from AlbertoGP/master
fromString option, use index from argument array for filename instead of "?"
2015-08-07 14:12:41 +02:00
Richard van Velzen
d7a4a4a462 Merge pull request #729 from DrewML/keep_fnames_docs
Add keep_fnames compressor option to README.md
2015-08-07 14:11:50 +02:00
Richard van Velzen
759b3f7d6d Fix mangling of property names which overwrite unmangleable properties
Fixes #747.
2015-08-05 21:18:39 +02:00
Richard van Velzen
958b6c2e57 Merge pull request #753 from Surgo/master
Support wrap and exportAll options for node.js tools.
2015-08-05 21:17:42 +02:00
Mihai Bazon
ab15d676d7 Merge pull request #757 from rvanvelzen/semicolon-fix
Fix semicolon printing when restricting max line length
2015-07-30 17:25:13 +03:00
Richard van Velzen
66761d7ecf Fix semicolon printing when restricting max line length
Fixes #755
2015-07-30 16:13:32 +02:00
Richard van Velzen
3afad58a93 Revert "Fix semicolon printing when restricting max line length"
This reverts commit 170e8b519e.
2015-07-30 15:57:18 +02:00
Richard van Velzen
170e8b519e Fix semicolon printing when restricting max line length
Fixes #755
2015-07-29 17:57:18 +02:00
Richard van Velzen
f8684f418a Replace util.puts in run-tests with console.log
See d2dda34b2a
2015-07-29 15:24:45 +02:00
XhmikosR
881bda7f59 Make node.js 0.8 the minimum supported version.
Node.js 0.4 and 0.6 are ancient; things don't work there.
Update Travis CI configuration accordingly.

Note, that the npm update in Travis is needed for 0.8 only at the moment.
2015-07-29 15:21:01 +02:00
Chris Cowan
9854deb626 Re-use the caught exception's error message in the parse error call. 2015-07-29 15:06:52 +02:00
Chris Cowan
d6814050dd Give a good error message if an invalid regular expression is found. 2015-07-29 15:05:59 +02:00
thorn0
252fc65558 Advanced way to specify if a function call might have side effects. #400 2015-07-29 14:36:42 +02:00
Kosei Kitahara
8108c7ffaf Support wrap and exportAll options. 2015-07-28 21:36:22 +09:00
Mihai Bazon
ba9936a572 v2.4.24 2015-07-22 16:58:09 +03:00
Mihai Bazon
905b601178 Don't attempt to negate non-boolean AST_Binary
Fix #751
2015-07-22 16:55:55 +03:00
Mihai Bazon
63fb2d5a44 Merge pull request #735 from kzc/master
optimizations for && and || where left side is constant expression
2015-07-20 09:58:01 +03:00
Mihai Bazon
85a5fc0aeb Don't drop parens in a * (b * c). Close #744 2015-06-30 10:10:29 +03:00
Alberto González Palomo
4fba3e0b80 fromString option, use index from argument array for filename instead of "?"
The index allows the caller to map things like parse errors back to the
code chunk where they appeared.
2015-06-15 18:03:06 +02:00
kzc
9d398d999c spacing 2015-06-14 17:45:19 -04:00
kzc
f47b2b52a5 operator && and || optimization: add "else" before "if" as intended 2015-06-14 17:44:26 -04:00
kzc
fedb6191a1 optimizations for && and || where left side is constant expression 2015-06-11 23:22:38 -04:00
Mihai Bazon
5bf617ebde Merge pull request #733 from jcxplorer/add-mangle-regex-option
Add --mangle-regex option
2015-06-09 16:33:21 +03:00
Joao Carlos
0b82e1cd5b Change --mangle-regex to accept a full regex 2015-06-09 15:14:41 +03:00
Joao Carlos
9aef34a816 Show descriptive error when --mangle-regex is invalid 2015-06-09 14:31:49 +03:00
Joao Carlos
0ac6918a41 Add --mangle-regex option 2015-06-09 14:16:50 +03:00
Andrew Levine
65ee5af78c Add keep_fnames compressor option to README.md 2015-06-02 15:32:10 -05:00
Mihai Bazon
c6fa291571 v2.4.23 2015-05-20 17:48:30 +03:00
Mihai Bazon
bce4307e9e Treat \uFEFF as whitespace.
Fix #714
2015-05-20 16:17:46 +03:00
Mihai Bazon
96ad94ab41 v2.4.22 2015-05-18 13:58:25 +03:00
Mihai Bazon
a5b60217ce Fix compressing conditionals
Only transform foo() ? EXP(x) : EXP(y) into EXP(foo() ? x : y) if EXP has no
side effects.

Fix #710
2015-05-18 13:56:04 +03:00
Mihai Bazon
44fd6694eb fix again reserved props 2015-05-13 22:03:00 +03:00
Mihai Bazon
e48db3a8b6 Make reserved names take priority over the name cache 2015-05-07 15:01:16 +03:00
Mihai Bazon
e637bdaf4e Only drop the BOM when it's the first character.
Close #704
2015-05-05 10:11:38 +03:00
29 changed files with 1218 additions and 229 deletions

View File

@@ -1,10 +1,9 @@
language: node_js language: node_js
before_install: "npm install -g npm" before_install: "npm install -g npm"
node_js: node_js:
- "iojs"
- "0.12" - "0.12"
- "0.11"
- "0.10" - "0.10"
- "4"
matrix: matrix:
fast_finish: true fast_finish: true
sudo: false sudo: false

View File

@@ -132,7 +132,10 @@ The available options are:
--reserve-domprops Make (most?) DOM properties reserved for --reserve-domprops Make (most?) DOM properties reserved for
--mangle-props --mangle-props
--mangle-props Mangle property names --mangle-props Mangle property names
--mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings --name-cache File to hold mangled names mappings
--pure-funcs List of functions that can be safely removed if
their return value is not used [array]
``` ```
Specify `--output` (`-o`) to declare the output file. Otherwise the output Specify `--output` (`-o`) to declare the output file. Otherwise the output
@@ -251,6 +254,10 @@ A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass cover most standard JS and DOM properties defined in various browsers. Pass
`--reserve-domprops` to read that in. `--reserve-domprops` to read that in.
You can also use a regular expression to define which property names should be
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
that start with an underscore.
When you compress multiple files using this option, in order for them to When you compress multiple files using this option, in order for them to
work together in the end we need to ensure somehow that one property gets work together in the end we need to ensure somehow that one property gets
mangled to the same name in all of them. For this, pass `--name-cache mangled to the same name in all of them. For this, pass `--name-cache
@@ -345,6 +352,11 @@ to set `true`; it's effectively a shortcut for `foo=true`).
compressor from discarding unused function arguments. You need this compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`. for code which relies on `Function.length`.
- `keep_fnames` -- default `false`. Pass `true` to prevent the
compressor from mangling/discarding function names. Useful for code relying on
`Function.prototype.name`.
### 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

View File

@@ -70,7 +70,10 @@ You need to pass an argument to this option to specify the name that your module
.describe("reserved-file", "File containing reserved names") .describe("reserved-file", "File containing reserved names")
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props") .describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
.describe("mangle-props", "Mangle property names") .describe("mangle-props", "Mangle property names")
.describe("mangle-regex", "Only mangle property names matching the regex")
.describe("name-cache", "File to hold mangled names mappings") .describe("name-cache", "File to hold mangled names mappings")
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
.alias("p", "prefix") .alias("p", "prefix")
.alias("o", "output") .alias("o", "output")
@@ -103,6 +106,7 @@ You need to pass an argument to this option to specify the name that your module
.string("prefix") .string("prefix")
.string("name-cache") .string("name-cache")
.array("reserved-file") .array("reserved-file")
.array("pure-funcs")
.boolean("expr") .boolean("expr")
.boolean("source-map-include-sources") .boolean("source-map-include-sources")
@@ -114,6 +118,7 @@ You need to pass an argument to this option to specify the name that your module
.boolean("stats") .boolean("stats")
.boolean("acorn") .boolean("acorn")
.boolean("spidermonkey") .boolean("spidermonkey")
.boolean("dump-spidermonkey-ast")
.boolean("lint") .boolean("lint")
.boolean("V") .boolean("V")
.boolean("version") .boolean("version")
@@ -174,6 +179,10 @@ if (ARGS.d) {
if (COMPRESS) COMPRESS.global_defs = getOptions("d"); if (COMPRESS) COMPRESS.global_defs = getOptions("d");
} }
if (ARGS.pure_funcs) {
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
}
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*/);
} }
@@ -191,6 +200,15 @@ function writeNameCache(key, cache) {
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache); return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
} }
function extractRegex(str) {
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
var regex_pos = str.lastIndexOf("/");
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
} else {
throw new Error("Invalid regular expression: " + str);
}
}
if (ARGS.quotes === true) { if (ARGS.quotes === true) {
ARGS.quotes = 3; ARGS.quotes = 3;
} }
@@ -217,9 +235,8 @@ if (BEAUTIFY)
if (ARGS.comments != null) { if (ARGS.comments != null) {
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) { if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
var regex_pos = ARGS.comments.lastIndexOf("/");
try { try {
OUTPUT_OPTIONS.comments = new RegExp(ARGS.comments.substr(1, regex_pos - 1), ARGS.comments.substr(regex_pos + 1)); OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
} catch (e) { } catch (e) {
print_error("ERROR: Invalid --comments: " + e.message); print_error("ERROR: Invalid --comments: " + e.message);
} }
@@ -245,7 +262,6 @@ if (ARGS.self) {
} }
files = UglifyJS.FILES; files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS"; if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
ARGS.export_all = true;
} }
var ORIG_MAP = ARGS.in_source_map; var ORIG_MAP = ARGS.in_source_map;
@@ -375,15 +391,25 @@ async.eachLimit(files, 1, function (file, cb) {
if (ARGS.mangle_props || ARGS.name_cache) (function(){ if (ARGS.mangle_props || ARGS.name_cache) (function(){
var reserved = RESERVED ? RESERVED.props : null; var reserved = RESERVED ? RESERVED.props : null;
var cache = readNameCache("props"); var cache = readNameCache("props");
var regex;
try {
regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
} catch (e) {
print_error("ERROR: Invalid --mangle-regex: " + e.message);
process.exit(1);
}
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, { TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
reserved : reserved, reserved : reserved,
cache : cache, cache : cache,
only_cache : !ARGS.mangle_props only_cache : !ARGS.mangle_props,
regex : regex
}); });
writeNameCache("props", cache); writeNameCache("props", cache);
})(); })();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint; var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars"); var TL_CACHE = readNameCache("vars");
if (SCOPE_IS_NEEDED) { if (SCOPE_IS_NEEDED) {
@@ -425,6 +451,9 @@ async.eachLimit(files, 1, function (file, cb) {
} }
} }
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
time_it("generate", function(){ time_it("generate", function(){
TOPLEVEL.print(output); TOPLEVEL.print(output);
}); });
@@ -446,6 +475,7 @@ async.eachLimit(files, 1, function (file, cb) {
} else { } else {
print(output); print(output);
} }
}
if (ARGS.stats) { if (ARGS.stats) {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", { print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {

View File

@@ -81,10 +81,11 @@ function DEFNODE(type, props, methods, base) {
ctor.DEFMETHOD = function(name, method) { ctor.DEFMETHOD = function(name, method) {
this.prototype[name] = method; this.prototype[name] = method;
}; };
exports["AST_" + type] = ctor;
return ctor; return ctor;
}; };
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", { var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
}, null); }, null);
var AST_Node = DEFNODE("Node", "start end", { var AST_Node = DEFNODE("Node", "start end", {
@@ -329,12 +330,11 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
} }
})); }));
} }
var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))"; var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))";
wrapped_tl = parse(wrapped_tl); wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_SimpleStatement) { if (node instanceof AST_Directive) {
node = node.body; switch (node.value) {
if (node instanceof AST_String) switch (node.getValue()) {
case "$ORIG": case "$ORIG":
return MAP.splice(self.body); return MAP.splice(self.body);
case "$EXPORTS": case "$EXPORTS":
@@ -864,10 +864,11 @@ var AST_String = DEFNODE("String", "value quote", {
} }
}, AST_Constant); }, AST_Constant);
var AST_Number = DEFNODE("Number", "value", { var AST_Number = DEFNODE("Number", "value literal", {
$documentation: "A number literal", $documentation: "A number literal",
$propdoc: { $propdoc: {
value: "[number] the numeric value" value: "[number] the numeric value",
literal: "[string] numeric value as string (optional)"
} }
}, AST_Constant); }, AST_Constant);
@@ -926,27 +927,36 @@ var AST_True = DEFNODE("True", null, {
function TreeWalker(callback) { function TreeWalker(callback) {
this.visit = callback; this.visit = callback;
this.stack = []; this.stack = [];
this.directives = Object.create(null);
}; };
TreeWalker.prototype = { TreeWalker.prototype = {
_visit: function(node, descend) { _visit: function(node, descend) {
this.stack.push(node); this.push(node);
var ret = this.visit(node, descend ? function(){ var ret = this.visit(node, descend ? function(){
descend.call(node); descend.call(node);
} : noop); } : noop);
if (!ret && descend) { if (!ret && descend) {
descend.call(node); descend.call(node);
} }
this.stack.pop(); this.pop(node);
return ret; return ret;
}, },
parent: function(n) { parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)]; return this.stack[this.stack.length - 2 - (n || 0)];
}, },
push: function (node) { push: function (node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) {
this.directives[node.value] = this.directives[node.value] ? "up" : true;
}
this.stack.push(node); this.stack.push(node);
}, },
pop: function() { pop: function(node) {
return this.stack.pop(); this.stack.pop();
if (node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
}, },
self: function() { self: function() {
return this.stack[this.stack.length - 1]; return this.stack[this.stack.length - 1];
@@ -959,7 +969,16 @@ TreeWalker.prototype = {
} }
}, },
has_directive: function(type) { has_directive: function(type) {
return this.find_parent(AST_Scope).has_directive(type); var dir = this.directives[type];
if (dir) return dir;
var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope) {
for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i];
if (!(st instanceof AST_Directive)) break;
if (st.value == type) return true;
}
}
}, },
in_boolean_context: function() { in_boolean_context: function() {
var stack = this.stack; var stack = this.stack;

View File

@@ -61,7 +61,7 @@ function Compressor(options, false_by_default) {
loops : !false_by_default, loops : !false_by_default,
unused : !false_by_default, unused : !false_by_default,
hoist_funs : !false_by_default, hoist_funs : !false_by_default,
keep_fargs : false, keep_fargs : true,
keep_fnames : false, keep_fnames : false,
hoist_vars : false, hoist_vars : false,
if_return : !false_by_default, if_return : !false_by_default,
@@ -111,6 +111,7 @@ merge(Compressor.prototype, {
node.DEFMETHOD("optimize", function(compressor){ node.DEFMETHOD("optimize", function(compressor){
var self = this; var self = this;
if (self._optimized) return self; if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor); var opt = optimizer(self, compressor);
opt._optimized = true; opt._optimized = true;
if (opt === self) return opt; if (opt === self) return opt;
@@ -896,6 +897,7 @@ merge(Compressor.prototype, {
def(AST_Call, function(compressor){ def(AST_Call, function(compressor){
var pure = compressor.option("pure_funcs"); var pure = compressor.option("pure_funcs");
if (!pure) return true; if (!pure) return true;
if (typeof pure == "function") return pure(this);
return pure.indexOf(this.expression.print_to_string()) < 0; return pure.indexOf(this.expression.print_to_string()) < 0;
}); });
@@ -989,7 +991,7 @@ merge(Compressor.prototype, {
/* -----[ optimizers ]----- */ /* -----[ optimizers ]----- */
OPT(AST_Directive, function(self, compressor){ OPT(AST_Directive, function(self, compressor){
if (self.scope.has_directive(self.value) !== self.scope) { if (compressor.has_directive(self.value) === "up") {
return make_node(AST_EmptyStatement, self); return make_node(AST_EmptyStatement, self);
} }
return self; return self;
@@ -1025,6 +1027,7 @@ merge(Compressor.prototype, {
AST_Scope.DEFMETHOD("drop_unused", function(compressor){ AST_Scope.DEFMETHOD("drop_unused", function(compressor){
var self = this; var self = this;
if (compressor.has_directive("use asm")) return self;
if (compressor.option("unused") if (compressor.option("unused")
&& !(self instanceof AST_Toplevel) && !(self instanceof AST_Toplevel)
&& !self.uses_eval && !self.uses_eval
@@ -1086,7 +1089,7 @@ merge(Compressor.prototype, {
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 && !(node instanceof AST_Accessor)) { if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
if (compressor.option("unsafe") && !compressor.option("keep_fargs")) { if (!compressor.option("keep_fargs")) {
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()) {
@@ -1204,9 +1207,10 @@ merge(Compressor.prototype, {
}); });
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
var self = this;
if (compressor.has_directive("use asm")) return self;
var hoist_funs = compressor.option("hoist_funs"); var hoist_funs = compressor.option("hoist_funs");
var hoist_vars = compressor.option("hoist_vars"); var hoist_vars = compressor.option("hoist_vars");
var self = this;
if (hoist_funs || hoist_vars) { if (hoist_funs || hoist_vars) {
var dirs = []; var dirs = [];
var hoisted = []; var hoisted = [];
@@ -1909,15 +1913,11 @@ merge(Compressor.prototype, {
if (!compressor.option("side_effects")) if (!compressor.option("side_effects"))
return self; return self;
if (!self.car.has_side_effects(compressor)) { if (!self.car.has_side_effects(compressor)) {
// we shouldn't compress (1,eval)(something) to // we shouldn't compress (1,func)(something) to
// eval(something) because that changes the meaning of // func(something) because that changes the meaning of
// eval (becomes lexical instead of global). // the func (becomes lexical instead of global).
var p; var p = compressor.parent();
if (!(self.cdr instanceof AST_SymbolRef if (!(p instanceof AST_Call && p.expression === self)) {
&& self.cdr.name == "eval"
&& self.cdr.undeclared()
&& (p = compressor.parent()) instanceof AST_Call
&& p.expression === self)) {
return self.cdr; return self.cdr;
} }
} }
@@ -2031,15 +2031,14 @@ merge(Compressor.prototype, {
var commutativeOperators = makePredicate("== === != !== * & | ^"); var commutativeOperators = makePredicate("== === != !== * & | ^");
OPT(AST_Binary, function(self, compressor){ OPT(AST_Binary, function(self, compressor){
var reverse = compressor.has_directive("use asm") ? noop function reverse(op, force) {
: function(op, force) {
if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) { if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
if (op) self.operator = op; if (op) self.operator = op;
var tmp = self.left; var tmp = self.left;
self.left = self.right; self.left = self.right;
self.right = tmp; 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)) {
@@ -2104,6 +2103,34 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (compressor.option("conditionals")) {
if (self.operator == "&&") {
var ll = self.left.evaluate(compressor);
if (ll.length > 1) {
if (ll[1]) {
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
var rr = self.right.evaluate(compressor);
return rr[0];
} else {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return ll[0];
}
}
}
else if (self.operator == "||") {
var ll = self.left.evaluate(compressor);
if (ll.length > 1) {
if (ll[1]) {
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return ll[0];
} else {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
var rr = self.right.evaluate(compressor);
return rr[0];
}
}
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) { if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
case "&&": case "&&":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
@@ -2155,7 +2182,7 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (compressor.option("comparisons")) { if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary) if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) { || compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, { var negated = make_node(AST_UnaryPrefix, self, {
@@ -2230,10 +2257,11 @@ merge(Compressor.prototype, {
} }
} }
} }
// x * (y * z) ==> x * y * z // x && (y && z) ==> x && y && z
// x || (y || z) ==> x || y || z
if (self.right instanceof AST_Binary if (self.right instanceof AST_Binary
&& self.right.operator == self.operator && self.right.operator == self.operator
&& (self.operator == "*" || self.operator == "&&" || self.operator == "||")) && (self.operator == "&&" || self.operator == "||"))
{ {
self.left = make_node(AST_Binary, self.left, { self.left = make_node(AST_Binary, self.left, {
operator : self.operator, operator : self.operator,
@@ -2247,7 +2275,15 @@ merge(Compressor.prototype, {
}); });
OPT(AST_SymbolRef, function(self, compressor){ OPT(AST_SymbolRef, function(self, compressor){
if (self.undeclared()) { function isLHS(symbol, parent) {
return (
parent instanceof AST_Binary &&
parent.operator === '=' &&
parent.left === symbol
);
}
if (self.undeclared() && !isLHS(self, compressor.parent())) {
var defines = compressor.option("global_defs"); var defines = compressor.option("global_defs");
if (defines && defines.hasOwnProperty(self.name)) { if (defines && defines.hasOwnProperty(self.name)) {
return make_node_from_constant(compressor, defines[self.name], self); return make_node_from_constant(compressor, defines[self.name], self);
@@ -2356,6 +2392,7 @@ merge(Compressor.prototype, {
if (consequent instanceof AST_Call if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE && alternative.TYPE === consequent.TYPE
&& consequent.args.length == alternative.args.length && consequent.args.length == alternative.args.length
&& !consequent.expression.has_side_effects(compressor)
&& consequent.expression.equivalent_to(alternative.expression)) { && consequent.expression.equivalent_to(alternative.expression)) {
if (consequent.args.length == 0) { if (consequent.args.length == 0) {
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
@@ -2482,4 +2519,11 @@ merge(Compressor.prototype, {
OPT(AST_Object, literals_in_boolean_context); OPT(AST_Object, literals_in_boolean_context);
OPT(AST_RegExp, literals_in_boolean_context); OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){
if (self.value instanceof AST_Undefined) {
self.value = null;
}
return self;
});
})(); })();

View File

@@ -146,7 +146,14 @@
case "boolean": case "boolean":
return new (val ? AST_True : AST_False)(args); return new (val ? AST_True : AST_False)(args);
default: default:
args.value = val; var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags).toString();
} else {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
}
return new AST_RegExp(args); return new AST_RegExp(args);
} }
}, },
@@ -334,6 +341,19 @@
}; };
}); });
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var value = M.value;
return {
type: "Literal",
value: value,
raw: value.toString(),
regex: {
pattern: value.source,
flags: value.toString().match(/[gimuy]*$/)[0]
}
};
});
def_to_moz(AST_Constant, function To_Moz_Literal(M) { def_to_moz(AST_Constant, function To_Moz_Literal(M) {
var value = M.value; var value = M.value;
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) { if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
@@ -343,13 +363,15 @@
prefix: true, prefix: true,
argument: { argument: {
type: "Literal", type: "Literal",
value: -value value: -value,
raw: M.start.raw
} }
}; };
} }
return { return {
type: "Literal", type: "Literal",
value: value value: value,
raw: M.start.raw
}; };
}); });
@@ -369,6 +391,12 @@
/* -----[ tools ]----- */ /* -----[ tools ]----- */
function raw_token(moznode) {
if (moznode.type == "Literal") {
return moznode.raw != null ? moznode.raw : moznode.value + "";
}
}
function my_start_token(moznode) { function my_start_token(moznode) {
var loc = moznode.loc, start = loc && loc.start; var loc = moznode.loc, start = loc && loc.start;
var range = moznode.range; var range = moznode.range;
@@ -379,7 +407,8 @@
pos : range ? range[0] : moznode.start, pos : range ? range[0] : moznode.start,
endline : start && start.line, endline : start && start.line,
endcol : start && start.column, endcol : start && start.column,
endpos : range ? range[0] : moznode.start endpos : range ? range[0] : moznode.start,
raw : raw_token(moznode),
}); });
}; };
@@ -393,13 +422,14 @@
pos : range ? range[1] : moznode.end, pos : range ? range[1] : moznode.end,
endline : end && end.line, endline : end && end.line,
endcol : end && end.column, endcol : end && end.column,
endpos : range ? range[1] : moznode.end endpos : range ? range[1] : moznode.end,
raw : raw_token(moznode),
}); });
}; };
function map(moztype, mytype, propmap) { function map(moztype, mytype, propmap) {
var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
moz_to_me += "return new " + mytype.name + "({\n" + moz_to_me += "return new U2." + mytype.name + "({\n" +
"start: my_start_token(M),\n" + "start: my_start_token(M),\n" +
"end: my_end_token(M)"; "end: my_end_token(M)";
@@ -442,8 +472,8 @@
//me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
//console.log(moz_to_me); //console.log(moz_to_me);
moz_to_me = new Function("my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
my_start_token, my_end_token, from_moz exports, my_start_token, my_end_token, from_moz
); );
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")( me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
to_moz, to_moz_block to_moz, to_moz_block

View File

@@ -60,6 +60,7 @@ function OutputStream(options) {
bracketize : false, bracketize : false,
semicolons : true, semicolons : true,
comments : false, comments : false,
shebang : true,
preserve_line : false, preserve_line : false,
screw_ie8 : false, screw_ie8 : false,
preamble : null, preamble : null,
@@ -87,13 +88,14 @@ function OutputStream(options) {
function make_string(str, quote) { function make_string(str, quote) {
var dq = 0, sq = 0; var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){ str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
switch (s) { switch (s) {
case "\\": return "\\\\"; case "\\": return "\\\\";
case "\b": return "\\b"; case "\b": return "\\b";
case "\f": return "\\f"; case "\f": return "\\f";
case "\n": return "\\n"; case "\n": return "\\n";
case "\r": return "\\r"; case "\r": return "\\r";
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
case "\u2028": return "\\u2028"; case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029"; case "\u2029": return "\\u2029";
case '"': ++dq; return '"'; case '"': ++dq; return '"';
@@ -124,8 +126,11 @@ function OutputStream(options) {
function encode_string(str, quote) { function encode_string(str, quote) {
var ret = make_string(str, quote); var ret = make_string(str, quote);
if (options.inline_script) if (options.inline_script) {
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
return ret; return ret;
}; };
@@ -161,6 +166,8 @@ function OutputStream(options) {
str = String(str); str = String(str);
var ch = str.charAt(0); var ch = str.charAt(0);
if (might_need_semicolon) { if (might_need_semicolon) {
might_need_semicolon = false;
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) { if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
if (options.semicolons || requireSemicolonChars(ch)) { if (options.semicolons || requireSemicolonChars(ch)) {
OUTPUT += ";"; OUTPUT += ";";
@@ -171,11 +178,17 @@ function OutputStream(options) {
current_pos++; current_pos++;
current_line++; current_line++;
current_col = 0; current_col = 0;
if (/^\s+$/.test(str)) {
// reset the semicolon flag, since we didn't print one
// now and might still have to later
might_need_semicolon = true;
} }
}
if (!options.beautify) if (!options.beautify)
might_need_space = false; might_need_space = false;
} }
might_need_semicolon = false;
} }
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
@@ -369,8 +382,13 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator); nodetype.DEFMETHOD("_codegen", generator);
}; };
var use_asm = false;
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, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm") {
use_asm = true;
}
function doit() { function doit() {
self.add_comments(stream); self.add_comments(stream);
self.add_source_map(stream); self.add_source_map(stream);
@@ -383,6 +401,9 @@ function OutputStream(options) {
doit(); doit();
} }
stream.pop_node(); stream.pop_node();
if (self instanceof AST_Lambda) {
use_asm = prev_use_asm;
}
}); });
AST_Node.DEFMETHOD("print_to_string", function(options){ AST_Node.DEFMETHOD("print_to_string", function(options){
@@ -395,7 +416,6 @@ function OutputStream(options) {
AST_Node.DEFMETHOD("add_comments", function(output){ AST_Node.DEFMETHOD("add_comments", function(output){
var c = output.option("comments"), self = this; var c = output.option("comments"), self = this;
if (c) {
var start = self.start; var start = self.start;
if (start && !start._comments_dumped) { if (start && !start._comments_dumped) {
start._comments_dumped = true; start._comments_dumped = true;
@@ -418,13 +438,17 @@ function OutputStream(options) {
})); }));
} }
if (c.test) { if (!c) {
comments = comments.filter(function(comment) {
return comment.type == "comment5";
});
} else if (c.test) {
comments = comments.filter(function(comment){ comments = comments.filter(function(comment){
return c.test(comment.value); return c.test(comment.value) || comment.type == "comment5";
}); });
} else if (typeof c == "function") { } else if (typeof c == "function") {
comments = comments.filter(function(comment){ comments = comments.filter(function(comment){
return c(self, comment); return c(self, comment) || comment.type == "comment5";
}); });
} }
@@ -450,8 +474,11 @@ function OutputStream(options) {
output.space(); output.space();
} }
} }
}); else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) {
output.print("#!" + c.value + "\n");
output.indent();
} }
});
} }
}); });
@@ -1030,16 +1057,24 @@ function OutputStream(options) {
output.print(self.operator); output.print(self.operator);
}); });
DEFPRINT(AST_Binary, function(self, output){ DEFPRINT(AST_Binary, function(self, output){
var op = self.operator;
self.left.print(output); self.left.print(output);
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
&& self.left instanceof AST_UnaryPostfix
&& self.left.operator == "--") {
// space is mandatory to avoid outputting -->
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space(); output.space();
output.print(self.operator); }
if (self.operator == "<" output.print(op);
if ((op == "<" || op == "<<")
&& self.right instanceof AST_UnaryPrefix && self.right instanceof AST_UnaryPrefix
&& self.right.operator == "!" && self.right.operator == "!"
&& self.right.expression instanceof AST_UnaryPrefix && self.right.expression instanceof AST_UnaryPrefix
&& self.right.expression.operator == "--") { && self.right.expression.operator == "--") {
// space is mandatory to avoid outputting <!-- // space is mandatory to avoid outputting <!--
// http://javascript.spec.whatwg.org/#comment-syntax
output.print(" "); output.print(" ");
} else { } else {
// the space is optional depending on "beautify" // the space is optional depending on "beautify"
@@ -1143,7 +1178,11 @@ function OutputStream(options) {
output.print_string(self.getValue(), self.quote); output.print_string(self.getValue(), self.quote);
}); });
DEFPRINT(AST_Number, function(self, output){ DEFPRINT(AST_Number, function(self, output){
if (use_asm && self.start.raw != null) {
output.print(self.start.raw);
} else {
output.print(make_num(self.getValue())); output.print(make_num(self.getValue()));
}
}); });
function regexp_safe_literal(code) { function regexp_safe_literal(code) {
@@ -1319,6 +1358,12 @@ function OutputStream(options) {
DEFMAP(AST_Finally, basic_sourcemap_gen); DEFMAP(AST_Finally, basic_sourcemap_gen);
DEFMAP(AST_Definitions, basic_sourcemap_gen); DEFMAP(AST_Definitions, basic_sourcemap_gen);
DEFMAP(AST_Constant, basic_sourcemap_gen); DEFMAP(AST_Constant, basic_sourcemap_gen);
DEFMAP(AST_ObjectSetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectGetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectProperty, function(self, output){ DEFMAP(AST_ObjectProperty, function(self, output){
output.add_mapping(self.start, self.key); output.add_mapping(self.start, self.key);
}); });

View File

@@ -59,7 +59,6 @@ var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
var RE_OCT_NUMBER = /^0[0-7]+$/; var RE_OCT_NUMBER = /^0[0-7]+$/;
var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
var OPERATORS = makePredicate([ var OPERATORS = makePredicate([
"in", "in",
@@ -108,7 +107,7 @@ var OPERATORS = makePredicate([
"||" "||"
]); ]);
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000")); var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:")); var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
@@ -182,8 +181,9 @@ function parse_js_number(num) {
return parseInt(num.substr(2), 16); return parseInt(num.substr(2), 16);
} else if (RE_OCT_NUMBER.test(num)) { } else if (RE_OCT_NUMBER.test(num)) {
return parseInt(num.substr(1), 8); return parseInt(num.substr(1), 8);
} else if (RE_DEC_NUMBER.test(num)) { } else {
return parseFloat(num); var val = parseFloat(num);
if (val == num) return val;
} }
}; };
@@ -210,10 +210,10 @@ function is_token(token, type, val) {
var EX_EOF = {}; var EX_EOF = {};
function tokenizer($TEXT, filename, html5_comments) { function tokenizer($TEXT, filename, html5_comments, shebang) {
var S = { var S = {
text : $TEXT.replace(/\uFEFF/g, ''), text : $TEXT,
filename : filename, filename : filename,
pos : 0, pos : 0,
tokpos : 0, tokpos : 0,
@@ -285,6 +285,9 @@ function tokenizer($TEXT, filename, html5_comments) {
nlb : S.newline_before, nlb : S.newline_before,
file : filename file : filename
}; };
if (/^(?:num|string|regexp)$/i.test(type)) {
ret.raw = $TEXT.substring(ret.pos, ret.endpos);
}
if (!is_comment) { if (!is_comment) {
ret.comments_before = S.comments_before; ret.comments_before = S.comments_before;
S.comments_before = []; S.comments_before = [];
@@ -480,7 +483,11 @@ function tokenizer($TEXT, filename, html5_comments) {
regexp += ch; regexp += ch;
} }
var mods = read_name(); var mods = read_name();
try {
return token("regexp", new RegExp(regexp, mods)); return token("regexp", new RegExp(regexp, mods));
} catch(e) {
parse_error(e.message);
}
}); });
function read_operator(prefix) { function read_operator(prefix) {
@@ -564,6 +571,13 @@ function tokenizer($TEXT, filename, html5_comments) {
if (PUNC_CHARS(ch)) return token("punc", next()); if (PUNC_CHARS(ch)) return token("punc", next());
if (OPERATOR_CHARS(ch)) return read_operator(); if (OPERATOR_CHARS(ch)) return read_operator();
if (code == 92 || is_identifier_start(code)) return read_word(); if (code == 92 || is_identifier_start(code)) return read_word();
if (shebang) {
if (S.pos == 0 && looking_at("#!")) {
forward(2);
return skip_line_comment("comment5");
}
}
parse_error("Unexpected character '" + ch + "'"); parse_error("Unexpected character '" + ch + "'");
}; };
@@ -633,12 +647,13 @@ function parse($TEXT, options) {
expression : false, expression : false,
html5_comments : true, html5_comments : true,
bare_returns : false, bare_returns : false,
shebang : true,
}); });
var S = { var S = {
input : (typeof $TEXT == "string" input : (typeof $TEXT == "string"
? tokenizer($TEXT, options.filename, ? tokenizer($TEXT, options.filename,
options.html5_comments) options.html5_comments, options.shebang)
: $TEXT), : $TEXT),
token : null, token : null,
prev : null, prev : null,
@@ -1110,7 +1125,7 @@ function parse($TEXT, options) {
}); });
}; };
var new_ = function() { var new_ = function(allow_calls) {
var start = S.token; var start = S.token;
expect_token("operator", "new"); expect_token("operator", "new");
var newexp = expr_atom(false), args; var newexp = expr_atom(false), args;
@@ -1125,7 +1140,7 @@ function parse($TEXT, options) {
expression : newexp, expression : newexp,
args : args, args : args,
end : prev() end : prev()
}), true); }), allow_calls);
}; };
function as_atom_node() { function as_atom_node() {
@@ -1169,7 +1184,7 @@ function parse($TEXT, options) {
var expr_atom = function(allow_calls) { var expr_atom = function(allow_calls) {
if (is("operator", "new")) { if (is("operator", "new")) {
return new_(); return new_(allow_calls);
} }
var start = S.token; var start = S.token;
if (is("punc")) { if (is("punc")) {

View File

@@ -64,7 +64,8 @@ function mangle_properties(ast, options) {
options = defaults(options, { options = defaults(options, {
reserved : null, reserved : null,
cache : null, cache : null,
only_cache : false only_cache : false,
regex : null
}); });
var reserved = options.reserved; var reserved = options.reserved;
@@ -79,7 +80,10 @@ function mangle_properties(ast, options) {
}; };
} }
var regex = options.regex;
var names_to_mangle = []; var names_to_mangle = [];
var unmangleable = [];
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){ ast.walk(new TreeWalker(function(node){
@@ -105,21 +109,15 @@ function mangle_properties(ast, options) {
// step 2: transform the tree, renaming properties // step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){ return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
if (should_mangle(node.key)) {
node.key = mangle(node.key); node.key = mangle(node.key);
} }
}
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
// setter or getter // setter or getter
if (should_mangle(node.key.name)) {
node.key.name = mangle(node.key.name); node.key.name = mangle(node.key.name);
} }
}
else if (node instanceof AST_Dot) { else if (node instanceof AST_Dot) {
if (should_mangle(node.property)) {
node.property = mangle(node.property); node.property = mangle(node.property);
} }
}
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
node.property = mangleStrings(node.property); node.property = mangleStrings(node.property);
} }
@@ -140,15 +138,18 @@ function mangle_properties(ast, options) {
// only function declarations after this line // only function declarations after this line
function can_mangle(name) { function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) { if (options.only_cache) {
return cache.props.has(name); return cache.props.has(name);
} }
if (reserved.indexOf(name) >= 0) return false;
if (/^[0-9.]+$/.test(name)) return false; if (/^[0-9.]+$/.test(name)) return false;
return true; return true;
} }
function should_mangle(name) { function should_mangle(name) {
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name) return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0; || names_to_mangle.indexOf(name) >= 0;
} }
@@ -156,9 +157,17 @@ function mangle_properties(ast, options) {
function add(name) { function add(name) {
if (can_mangle(name)) if (can_mangle(name))
push_uniq(names_to_mangle, name); push_uniq(names_to_mangle, name);
if (!should_mangle(name)) {
push_uniq(unmangleable, name);
}
} }
function mangle(name) { function mangle(name) {
if (!should_mangle(name)) {
return name;
}
var mangled = cache.props.get(name); var mangled = cache.props.get(name);
if (!mangled) { if (!mangled) {
do { do {
@@ -201,10 +210,8 @@ function mangle_properties(ast, options) {
node.cdr = mangleStrings(node.cdr); node.cdr = mangleStrings(node.cdr);
} }
else if (node instanceof AST_String) { else if (node instanceof AST_String) {
if (should_mangle(node.value)) {
node.value = mangle(node.value); node.value = mangle(node.value);
} }
}
else if (node instanceof AST_Conditional) { else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent); node.consequent = mangleStrings(node.consequent);
node.alternative = mangleStrings(node.alternative); node.alternative = mangleStrings(node.alternative);

View File

@@ -92,6 +92,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// 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 defun = null; var defun = null;
var nesting = 0; var nesting = 0;
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
@@ -108,16 +109,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
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_defun = defun; var save_defun = defun;
var save_labels = labels;
defun = scope = node; defun = scope = node;
labels = new Dictionary();
++nesting; descend(); --nesting; ++nesting; descend(); --nesting;
scope = save_scope; scope = save_scope;
defun = save_defun; defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Directive) { if (node instanceof AST_LabeledStatement) {
node.scope = scope; var l = node.label;
push_uniq(scope.directives, node.value); if (labels.has(l.name)) {
return true; 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_With) { if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) for (var s = scope; s; s = s.parent_scope)
@@ -127,6 +136,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) { if (node instanceof AST_Symbol) {
node.scope = scope; node.scope = scope;
} }
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) { if (node instanceof AST_SymbolLambda) {
defun.def_function(node); defun.def_function(node);
} }
@@ -148,6 +161,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
(options.screw_ie8 ? scope : defun) (options.screw_ie8 ? scope : defun)
.def_variable(node); .def_variable(node);
} }
else 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);
@@ -162,6 +184,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
func = prev_func; func = prev_func;
return true; return true;
} }
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
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);
@@ -198,7 +224,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}); });
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
@@ -209,10 +234,6 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.nesting = nesting; // the nesting level of this scope (0 means toplevel) this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
}); });
AST_Scope.DEFMETHOD("strict", function(){
return this.has_directive("use strict");
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false; this.uses_arguments = false;
@@ -236,11 +257,6 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name)); || (this.parent_scope && this.parent_scope.find_variable(name));
}); });
AST_Scope.DEFMETHOD("has_directive", function(value){
return this.parent_scope && this.parent_scope.has_directive(value)
|| (this.directives.indexOf(value) >= 0 ? this : null);
});
AST_Scope.DEFMETHOD("def_function", function(symbol){ AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol)); this.functions.set(symbol.name, this.def_variable(symbol));
}); });

View File

@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
if (y !== undefined) x = y; if (y !== undefined) x = y;
} }
} }
tw.pop(); tw.pop(this);
return x; return x;
}); });
}; };

128
npm-shrinkwrap.json generated Normal file
View File

@@ -0,0 +1,128 @@
{
"name": "uglify-js",
"version": "2.4.24",
"dependencies": {
"abbrev": {
"version": "1.0.7",
"from": "abbrev@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz"
},
"amdefine": {
"version": "1.0.0",
"from": "amdefine@>=0.0.4",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
},
"async": {
"version": "0.2.10",
"from": "async@>=0.2.6 <0.3.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
},
"camelcase": {
"version": "1.2.1",
"from": "camelcase@>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
},
"decamelize": {
"version": "1.0.0",
"from": "decamelize@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz"
},
"deep-is": {
"version": "0.1.3",
"from": "deep-is@>=0.1.2 <0.2.0",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz"
},
"esprima": {
"version": "1.1.1",
"from": "esprima@>=1.1.1 <1.2.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz"
},
"estraverse": {
"version": "1.5.1",
"from": "estraverse@>=1.5.1 <1.6.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz"
},
"esutils": {
"version": "1.0.0",
"from": "esutils@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz"
},
"fast-levenshtein": {
"version": "1.0.7",
"from": "fast-levenshtein@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz"
},
"levn": {
"version": "0.2.5",
"from": "levn@>=0.2.5 <0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz"
},
"nopt": {
"version": "2.1.2",
"from": "nopt@>=2.1.2 <2.2.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz"
},
"optionator": {
"version": "0.5.0",
"from": "optionator@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz"
},
"prelude-ls": {
"version": "1.1.2",
"from": "prelude-ls@>=1.1.1 <1.2.0",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
},
"reflect": {
"version": "0.1.3",
"from": "git://github.com/zaach/reflect.js.git",
"resolved": "git://github.com/zaach/reflect.js.git#286bcd79661c96ecc404357d3c0e35fdb54a6967"
},
"source-map": {
"version": "0.5.1",
"from": "source-map@>=0.5.1 <0.6.0",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz"
},
"type-check": {
"version": "0.3.1",
"from": "type-check@>=0.3.1 <0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz"
},
"uglify-js": {
"version": "2.4.24",
"from": "git://github.com/mishoo/UglifyJS2.git",
"resolved": "git://github.com/mishoo/UglifyJS2.git#2a06c7758e24a64740473c8031eafbb7fefa213f",
"dependencies": {
"source-map": {
"version": "0.1.34",
"from": "source-map@0.1.34",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz"
}
}
},
"uglify-to-browserify": {
"version": "1.0.2",
"from": "uglify-to-browserify@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
},
"window-size": {
"version": "0.1.0",
"from": "window-size@0.1.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
},
"wordwrap": {
"version": "0.0.2",
"from": "wordwrap@0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
},
"yargs": {
"version": "3.10.0",
"from": "yargs@>=3.10.0 <3.11.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz"
},
"zeparser": {
"version": "0.0.7",
"from": "git://github.com/qfox/ZeParser.git",
"resolved": "git://github.com/qfox/ZeParser.git#c99240c5ba7054c467733800ff38265958a2dda9"
}
}
}

View File

@@ -3,10 +3,10 @@
"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",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD", "license": "BSD-2-Clause",
"version": "2.4.21", "version": "2.6.0",
"engines": { "engines": {
"node": ">=0.4.0" "node": ">=0.8.0"
}, },
"maintainers": [ "maintainers": [
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)" "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
@@ -30,9 +30,9 @@
], ],
"dependencies": { "dependencies": {
"async": "~0.2.6", "async": "~0.2.6",
"source-map": "0.1.34", "source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0", "uglify-to-browserify": "~1.0.0",
"yargs": "~3.5.4" "yargs": "~3.10.0"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~0.6.0", "acorn": "~0.6.0",
@@ -46,6 +46,7 @@
] ]
}, },
"scripts": { "scripts": {
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
} }
} }

106
test/compress/asm.js Normal file
View File

@@ -0,0 +1,106 @@
asm_mixed: {
options = {
sequences : true,
properties : true,
dead_code : true,
drop_debugger : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
keep_fargs : true,
keep_fnames : false,
hoist_vars : true,
if_return : true,
join_vars : true,
cascade : true,
side_effects : true,
negate_iife : true
};
input: {
// adapted from http://asmjs.org/spec/latest/
function asm_GeometricMean(stdlib, foreign, buffer) {
"use asm";
var exp = stdlib.Math.exp;
var log = stdlib.Math.log;
var values = new stdlib.Float64Array(buffer);
function logSum(start, end) {
start = start|0;
end = end|0;
var sum = 0.0, p = 0, q = 0;
// asm.js forces byte addressing of the heap by requiring shifting by 3
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
sum = sum + +log(values[p>>3]);
}
return +sum;
}
function geometricMean(start, end) {
start = start|0;
end = end|0;
return +exp(+logSum(start, end) / +((end - start)|0));
}
return { geometricMean: geometricMean };
}
function no_asm_GeometricMean(stdlib, foreign, buffer) {
var exp = stdlib.Math.exp;
var log = stdlib.Math.log;
var values = new stdlib.Float64Array(buffer);
function logSum(start, end) {
start = start|0;
end = end|0;
var sum = 0.0, p = 0, q = 0;
// asm.js forces byte addressing of the heap by requiring shifting by 3
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
sum = sum + +log(values[p>>3]);
}
return +sum;
}
function geometricMean(start, end) {
start = start|0;
end = end|0;
return +exp(+logSum(start, end) / +((end - start)|0));
}
return { geometricMean: geometricMean };
}
}
expect: {
function asm_GeometricMean(stdlib, foreign, buffer) {
"use asm";
var exp = stdlib.Math.exp;
var log = stdlib.Math.log;
var values = new stdlib.Float64Array(buffer);
function logSum(start, end) {
start = start | 0;
end = end | 0;
var sum = 0.0, p = 0, q = 0;
for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0) {
sum = sum + +log(values[p >> 3]);
}
return +sum;
}
function geometricMean(start, end) {
start = start | 0;
end = end | 0;
return +exp(+logSum(start, end) / +(end - start | 0));
}
return { geometricMean: geometricMean };
}
function no_asm_GeometricMean(stdlib, foreign, buffer) {
function logSum(start, end) {
start = 0 | start, end = 0 | end;
var sum = 0, p = 0, q = 0;
for (p = start << 3, q = end << 3; (0 | q) > (0 | p); p = p + 8 | 0) sum += +log(values[p >> 3]);
return +sum;
}
function geometricMean(start, end) {
return start = 0 | start, end = 0 | end, +exp(+logSum(start, end) / +(end - start | 0));
}
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
return { geometricMean: geometricMean };
}
}
}

View File

@@ -153,6 +153,7 @@ cond_1: {
conditionals: true conditionals: true
}; };
input: { input: {
var do_something; // if undeclared it's assumed to have side-effects
if (some_condition()) { if (some_condition()) {
do_something(x); do_something(x);
} else { } else {
@@ -160,6 +161,7 @@ cond_1: {
} }
} }
expect: { expect: {
var do_something;
do_something(some_condition() ? x : y); do_something(some_condition() ? x : y);
} }
} }
@@ -169,7 +171,7 @@ cond_2: {
conditionals: true conditionals: true
}; };
input: { input: {
var x; var x, FooBar;
if (some_condition()) { if (some_condition()) {
x = new FooBar(1); x = new FooBar(1);
} else { } else {
@@ -177,7 +179,7 @@ cond_2: {
} }
} }
expect: { expect: {
var x; var x, FooBar;
x = new FooBar(some_condition() ? 1 : 2); x = new FooBar(some_condition() ? 1 : 2);
} }
} }
@@ -187,6 +189,7 @@ cond_3: {
conditionals: true conditionals: true
}; };
input: { input: {
var FooBar;
if (some_condition()) { if (some_condition()) {
new FooBar(1); new FooBar(1);
} else { } else {
@@ -194,6 +197,7 @@ cond_3: {
} }
} }
expect: { expect: {
var FooBar;
some_condition() ? new FooBar(1) : FooBar(2); some_condition() ? new FooBar(1) : FooBar(2);
} }
} }
@@ -203,6 +207,7 @@ cond_4: {
conditionals: true conditionals: true
}; };
input: { input: {
var do_something;
if (some_condition()) { if (some_condition()) {
do_something(); do_something();
} else { } else {
@@ -210,6 +215,7 @@ cond_4: {
} }
} }
expect: { expect: {
var do_something;
some_condition(), do_something(); some_condition(), do_something();
} }
} }
@@ -378,3 +384,163 @@ cond_8: {
a = condition ? 1 : 0; a = condition ? 1 : 0;
} }
} }
conditional_and: {
options = {
conditionals: true,
evaluate : true
};
input: {
var a;
// compress these
a = true && condition;
a = 1 && console.log("a");
a = 2 * 3 && 2 * condition;
a = 5 == 5 && condition + 3;
a = "string" && 4 - condition;
a = 5 + "" && condition / 5;
a = -4.5 && 6 << condition;
a = 6 && 7;
a = false && condition;
a = NaN && console.log("b");
a = 0 && console.log("c");
a = undefined && 2 * condition;
a = null && condition + 3;
a = 2 * 3 - 6 && 4 - condition;
a = 10 == 7 && condition / 5;
a = !"string" && 6 % condition;
a = 0 && 7;
// don't compress these
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && undefined;
a = condition + 3 && null;
}
expect: {
var a;
a = condition;
a = console.log("a");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 << condition;
a = 7;
a = false;
a = NaN;
a = 0;
a = void 0;
a = null;
a = 0;
a = false;
a = false;
a = 0;
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && void 0;
a = condition + 3 && null;
}
}
conditional_or: {
options = {
conditionals: true,
evaluate : true
};
input: {
var a;
// compress these
a = true || condition;
a = 1 || console.log("a");
a = 2 * 3 || 2 * condition;
a = 5 == 5 || condition + 3;
a = "string" || 4 - condition;
a = 5 + "" || condition / 5;
a = -4.5 || 6 << condition;
a = 6 || 7;
a = false || condition;
a = 0 || console.log("b");
a = NaN || console.log("c");
a = undefined || 2 * condition;
a = null || condition + 3;
a = 2 * 3 - 6 || 4 - condition;
a = 10 == 7 || condition / 5;
a = !"string" || 6 % condition;
a = null || 7;
a = console.log(undefined && condition || null);
a = console.log(undefined || condition && null);
// don't compress these
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || undefined;
a = condition + 3 || null;
}
expect: {
var a;
a = true;
a = 1;
a = 6;
a = true;
a = "string";
a = "5";
a = -4.5;
a = 6;
a = condition;
a = console.log("b");
a = console.log("c");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 % condition;
a = 7;
a = console.log(null);
a = console.log(condition && null);
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || void 0;
a = condition + 3 || null;
}
}

View File

@@ -1,5 +1,5 @@
unused_funarg_1: { unused_funarg_1: {
options = { unused: true, unsafe: true }; options = { unused: true, keep_fargs: false };
input: { input: {
function f(a, b, c, d, e) { function f(a, b, c, d, e) {
return a + b; return a + b;
@@ -13,7 +13,7 @@ unused_funarg_1: {
} }
unused_funarg_2: { unused_funarg_2: {
options = { unused: true, unsafe: true }; options = { unused: true, keep_fargs: false };
input: { input: {
function f(a, b, c, d, e) { function f(a, b, c, d, e) {
return a + c; return a + c;
@@ -173,7 +173,7 @@ keep_fnames: {
} }
expect: { expect: {
function foo() { function foo() {
return function bar() {}; return function bar(baz) {};
} }
} }
} }

View File

@@ -0,0 +1,71 @@
html_comment_in_expression: {
input: {
function f(a, b, x, y) { return a < !--b && x-- > y; }
}
expect_exact: "function f(a,b,x,y){return a< !--b&&x-- >y}";
}
html_comment_in_less_than: {
input: {
function f(a, b) { return a < !--b; }
}
expect_exact: "function f(a,b){return a< !--b}";
}
html_comment_in_left_shift: {
input: {
function f(a, b) { return a << !--b; }
}
expect_exact: "function f(a,b){return a<< !--b}";
}
html_comment_in_right_shift: {
input: {
function f(a, b) { return a-- >> b; }
}
expect_exact: "function f(a,b){return a-- >>b}";
}
html_comment_in_zero_fill_right_shift: {
input: {
function f(a, b) { return a-- >>> b; }
}
expect_exact: "function f(a,b){return a-- >>>b}";
}
html_comment_in_greater_than: {
input: {
function f(a, b) { return a-- > b; }
}
expect_exact: "function f(a,b){return a-- >b}";
}
html_comment_in_greater_than_or_equal: {
input: {
function f(a, b) { return a-- >= b; }
}
expect_exact: "function f(a,b){return a-- >=b}";
}
html_comment_in_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>= b; }
}
expect_exact: "function f(a,b){return a-- >>=b}";
}
html_comment_in_zero_fill_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>>= b; }
}
expect_exact: "function f(a,b){return a-- >>>=b}";
}
html_comment_in_string_literal: {
input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
}
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
}

View File

@@ -0,0 +1,11 @@
do_not_update_lhs: {
options = { global_defs: { DEBUG: false } };
input: { DEBUG = false; }
expect: { DEBUG = false; }
}
do_update_rhs: {
options = { global_defs: { DEBUG: false } };
input: { MY_DEBUG = DEBUG; }
expect: { MY_DEBUG = false; }
}

View File

@@ -0,0 +1,37 @@
dont_reuse_prop: {
mangle_props = {
regex: /asd/
};
input: {
var obj = {};
obj.a = 123;
obj.asd = 256;
console.log(obj.a);
}
expect: {
var obj = {};
obj.a = 123;
obj.b = 256;
console.log(obj.a);
}
}
unmangleable_props_should_always_be_reserved: {
mangle_props = {
regex: /asd/
};
input: {
var obj = {};
obj.asd = 256;
obj.a = 123;
console.log(obj.a);
}
expect: {
var obj = {};
obj.b = 256;
obj.a = 123;
console.log(obj.a);
}
}

View File

@@ -0,0 +1,29 @@
negate_booleans_1: {
options = {
comparisons: true
};
input: {
var a = !a || !b || !c || !d || !e || !f;
}
expect: {
var a = !(a && b && c && d && e && f);
}
}
negate_booleans_2: {
options = {
comparisons: true
};
input: {
var match = !x && // should not touch this one
(!z || c) &&
(!k || d) &&
the_stuff();
}
expect: {
var match = !x &&
(!z || c) &&
(!k || d) &&
the_stuff();
}
}

View File

@@ -0,0 +1,23 @@
remove_redundant_sequence_items: {
options = { side_effects: true };
input: {
(0, 1, logThis)();
(0, 1, _decorators.logThis)();
}
expect: {
(0, logThis)();
(0, _decorators.logThis)();
}
}
dont_remove_lexical_binding_sequence: {
options = { side_effects: true };
input: {
(0, logThis)();
(0, _decorators.logThis)();
}
expect: {
(0, logThis)();
(0, _decorators.logThis)();
}
}

12
test/compress/new.js Normal file
View File

@@ -0,0 +1,12 @@
new_statement: {
input: {
new x(1);
new x(1)(2);
new x(1)(2)(3);
new new x(1);
new new x(1)(2);
new (new x(1))(2);
(new new x(1))(2);
}
expect_exact: "new x(1);new x(1)(2);new x(1)(2)(3);new new x(1);new new x(1)(2);new new x(1)(2);(new new x(1))(2);"
}

View File

@@ -0,0 +1,124 @@
return_undefined: {
options = {
sequences : false,
if_return : true,
evaluate : true,
dead_code : true,
conditionals : true,
comparisons : true,
booleans : true,
unused : true,
side_effects : true,
properties : true,
drop_debugger : true,
loops : true,
hoist_funs : true,
keep_fargs : true,
keep_fnames : false,
hoist_vars : true,
join_vars : true,
cascade : true,
negate_iife : true
};
input: {
function f0() {
}
function f1() {
return undefined;
}
function f2() {
return void 0;
}
function f3() {
return void 123;
}
function f4() {
return;
}
function f5(a, b) {
console.log(a, b);
baz(a);
return;
}
function f6(a, b) {
console.log(a, b);
if (a) {
foo(b);
baz(a);
return a + b;
}
return undefined;
}
function f7(a, b) {
console.log(a, b);
if (a) {
foo(b);
baz(a);
return void 0;
}
return a + b;
}
function f8(a, b) {
foo(a);
bar(b);
return void 0;
}
function f9(a, b) {
foo(a);
bar(b);
return undefined;
}
function f10() {
return false;
}
function f11() {
return null;
}
function f12() {
return 0;
}
}
expect: {
function f0() {}
function f1() {}
function f2() {}
function f3() {}
function f4() {}
function f5(a, b) {
console.log(a, b);
baz(a);
}
function f6(a, b) {
console.log(a, b);
if (a) {
foo(b);
baz(a);
return a + b;
}
}
function f7(a, b) {
console.log(a, b);
if (!a)
return a + b;
foo(b);
baz(a);
}
function f8(a, b) {
foo(a);
bar(b);
}
function f9(a, b) {
foo(a);
bar(b);
}
function f10() {
return !1;
}
function f11() {
return null;
}
function f12() {
return 0;
}
}
}

View File

@@ -0,0 +1,18 @@
do_screw: {
options = { screw_ie8: true };
beautify = {
screw_ie8: true,
ascii_only: true
};
input: f("\v");
expect_exact: 'f("\\v");';
}
dont_screw: {
options = { screw_ie8: false };
beautify = { screw_ie8: false, ascii_only: true };
input: f("\v");
expect_exact: 'f("\\x0B");';
}

View File

@@ -4,7 +4,6 @@ var U = require("../tools/node");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
var sys = require("util");
var tests_dir = path.dirname(module.filename); var tests_dir = path.dirname(module.filename);
var failures = 0; var failures = 0;
@@ -12,11 +11,14 @@ var failed_files = {};
run_compress_tests(); run_compress_tests();
if (failures) { if (failures) {
sys.error("\n!!! Failed " + failures + " test cases."); console.error("\n!!! Failed " + failures + " test cases.");
sys.error("!!! " + Object.keys(failed_files).join(", ")); console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1); process.exit(1);
} }
var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests();
var run_ast_conversion_tests = require("./mozilla-ast"); var run_ast_conversion_tests = require("./mozilla-ast");
run_ast_conversion_tests({ run_ast_conversion_tests({
@@ -31,7 +33,7 @@ function tmpl() {
function log() { function log() {
var txt = tmpl.apply(this, arguments); var txt = tmpl.apply(this, arguments);
sys.puts(txt); console.log("%s", txt);
} }
function log_directory(dir) { function log_directory(dir) {
@@ -84,17 +86,21 @@ function run_compress_tests() {
warnings: false warnings: false
}); });
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output_options = test.beautify || {};
var expect; var expect;
if (test.expect) { if (test.expect) {
expect = make_code(as_toplevel(test.expect), false); expect = make_code(as_toplevel(test.expect), output_options);
} else { } else {
expect = test.expect_exact; expect = test.expect_exact;
} }
var input = as_toplevel(test.input); var input = as_toplevel(test.input);
var input_code = make_code(test.input); var input_code = make_code(test.input, { beautify: true });
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var output = input.transform(cmp); var output = input.transform(cmp);
output.figure_out_scope(); output.figure_out_scope();
output = make_code(output, false); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
input: input_code, input: input_code,
@@ -138,7 +144,7 @@ function parse_test(file) {
file: file, file: file,
line: node.start.line, line: node.start.line,
col: node.start.col, col: node.start.col,
code: make_code(node, false) code: make_code(node, { beautify: false })
})); }));
} }
@@ -185,15 +191,15 @@ function parse_test(file) {
}; };
} }
function make_code(ast, beautify) { function make_code(ast, options) {
if (arguments.length == 1) beautify = true; options.inline_script = true;
var stream = U.OutputStream({ beautify: beautify }); var stream = U.OutputStream(options);
ast.print(stream); ast.print(stream);
return stream.get(); return stream.get();
} }
function evaluate(code) { function evaluate(code) {
if (code instanceof U.AST_Node) if (code instanceof U.AST_Node)
code = make_code(code); code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")(); return new Function("return(" + code + ")")();
} }

40
test/sourcemaps.js Normal file
View File

@@ -0,0 +1,40 @@
var UglifyJS = require("..");
var ok = require("assert");
module.exports = function () {
console.log("--- Sourcemaps tests");
var basic = source_map([
'var x = 1 + 1;'
].join('\n'));
ok.equal(basic.version, 3);
ok.deepEqual(basic.names, ['x']);
var issue836 = source_map([
"({",
" get enabled() {",
" return 3;",
" },",
" set enabled(x) {",
" ;",
" }",
"});",
].join("\n"));
ok.deepEqual(issue836.names, ['enabled', 'x']);
}
function source_map(js) {
var source_map = UglifyJS.SourceMap();
var stream = UglifyJS.OutputStream({ source_map: source_map });
var parsed = UglifyJS.parse(js);
parsed.print(stream);
return JSON.parse(source_map.toString());
}
// Run standalone
if (module.parent === null) {
module.exports();
}

17
tools/exports.js Normal file
View File

@@ -0,0 +1,17 @@
exports["Compressor"] = Compressor;
exports["DefaultsError"] = DefaultsError;
exports["Dictionary"] = Dictionary;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["MAP"] = MAP;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeTransformer"] = TreeTransformer;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["merge"] = merge;
exports["parse"] = parse;
exports["push_uniq"] = push_uniq;
exports["string_template"] = string_template;
exports["is_identifier"] = is_identifier;

View File

@@ -1,26 +1,5 @@
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var vm = require("vm");
var UglifyJS = vm.createContext({
console : console,
process : process,
Buffer : Buffer,
MOZ_SourceMap : require("source-map")
});
function load_global(file) {
file = path.resolve(path.dirname(module.filename), file);
try {
var code = fs.readFileSync(file, "utf8");
return vm.runInContext(code, UglifyJS, file);
} catch(ex) {
// XXX: in case of a syntax error, the message is kinda
// useless. (no location information).
console.log("ERROR in file: " + file + " / " + ex);
process.exit(1);
}
};
var FILES = exports.FILES = [ var FILES = exports.FILES = [
"../lib/utils.js", "../lib/utils.js",
@@ -32,24 +11,25 @@ var FILES = exports.FILES = [
"../lib/compress.js", "../lib/compress.js",
"../lib/sourcemap.js", "../lib/sourcemap.js",
"../lib/mozilla-ast.js", "../lib/mozilla-ast.js",
"../lib/propmangle.js" "../lib/propmangle.js",
"./exports.js",
].map(function(file){ ].map(function(file){
return fs.realpathSync(path.join(path.dirname(__filename), file)); return fs.realpathSync(path.join(path.dirname(__filename), file));
}); });
FILES.forEach(load_global); var UglifyJS = exports;
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(
require("source-map"),
UglifyJS
);
UglifyJS.AST_Node.warn_function = function(txt) { UglifyJS.AST_Node.warn_function = function(txt) {
console.error("WARN: %s", txt); console.error("WARN: %s", txt);
}; };
// XXX: perhaps we shouldn't export everything but heck, I'm lazy.
for (var i in UglifyJS) {
if (UglifyJS.hasOwnProperty(i)) {
exports[i] = UglifyJS[i];
}
}
exports.minify = function(files, options) { exports.minify = function(files, options) {
options = UglifyJS.defaults(options, { options = UglifyJS.defaults(options, {
spidermonkey : false, spidermonkey : false,
@@ -73,17 +53,20 @@ exports.minify = function(files, options) {
} else { } else {
if (typeof files == "string") if (typeof files == "string")
files = [ files ]; files = [ files ];
files.forEach(function(file){ files.forEach(function(file, i){
var code = options.fromString var code = options.fromString
? file ? file
: fs.readFileSync(file, "utf8"); : fs.readFileSync(file, "utf8");
sourcesContent[file] = code; sourcesContent[file] = code;
toplevel = UglifyJS.parse(code, { toplevel = UglifyJS.parse(code, {
filename: options.fromString ? "?" : file, filename: options.fromString ? i : file,
toplevel: toplevel toplevel: toplevel
}); });
}); });
} }
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
}
// 2. compress // 2. compress
if (options.compress) { if (options.compress) {
@@ -128,7 +111,7 @@ exports.minify = function(files, options) {
var stream = UglifyJS.OutputStream(output); var stream = UglifyJS.OutputStream(output);
toplevel.print(stream); toplevel.print(stream);
if(options.outSourceMap){ if (options.outSourceMap && "string" === typeof options.outSourceMap) {
stream += "\n//# sourceMappingURL=" + options.outSourceMap; stream += "\n//# sourceMappingURL=" + options.outSourceMap;
} }