Compare commits

...

131 Commits

Author SHA1 Message Date
Mihai Bazon
294861ba96 v2.6.2 2016-02-22 21:39:14 +02:00
kzc
11b0efdf84 boolean_expression ? true : false --> boolean_expression 2016-02-22 17:59:36 +01:00
kzc
5486b68850 Take operator || precendence into account for AST_If optimization.
Fixes #979.
2016-02-21 12:05:02 -05:00
Richard van Velzen
bdd8e34f63 Allow --no-* options to disable their respective parameter
Fixes #974 and #972
2016-02-17 20:04:45 +01:00
alexlamsl
6547437725 preserve ThisBinding for side_effects 2016-02-17 19:34:01 +01:00
Richard van Velzen
9662228f6a Don't compress (0, eval)() to eval() 2016-02-16 19:00:48 +01:00
alexlamsl
31a9b05c96 Preserve ThisBinding in conditionals & collapse_vars
Fixes #973
2016-02-16 18:47:49 +01:00
Richard van Velzen
63b01fe8f9 Merge pull request #948 from kzc/collapse_vars_doc
collapse_vars: document the compress option in README
2016-02-11 22:13:30 +01:00
sergeyv
7a4ed9d200 Revert "using the original sourcemap as the base"
This reverts commit ad18689d92.

Reason for revert: introduce issue #882

Currently, generated sourcemap contains copy of all existing mappings and adds new mappings from uglified code to original one.
However, previous mapping are no longer valid and shouldn't be added.
2016-02-10 10:19:39 +01:00
Richard van Velzen
d5c651a5e5 Allow cli options to be specified in separate definitions
Fix for #963. This allows stuff like `--define a=1 --define b=1` besides only `--define a=1,b=1`
2016-02-10 10:14:46 +01:00
Martii
cdba43cfa4 Create and map bare-returns into new parse property name 2016-02-08 10:45:42 +01:00
Boris Letocha
a123e232b9 Fixes #951 missing export for SymbolDef 2016-01-31 21:41:38 +01:00
Mihai Bazon
601780acc1 Merge pull request #949 from kzc/collapse_vars_conditions
collapse_vars: fix if/else and ternary operator side effects
2016-01-29 18:05:39 +02:00
kzc
7c3fee9e31 collapse_vars: avoid replacement across AST_Case nodes to be on safe side even though no issues seen. 2016-01-29 10:35:07 -05:00
kzc
929de2b0de collapse_vars: fix if/else and ternary operator side effects 2016-01-28 12:17:06 -05:00
kzc
12e6ad326c collapse_vars: small change to README 2016-01-28 11:04:30 -05:00
kzc
00c8d1d241 collapse_vars: document option in README 2016-01-28 11:01:17 -05:00
kzc
af2472d85e collapse_vars: fix bug in repeated var defs of same name 2016-01-28 16:48:50 +01:00
Bryan Rayner
3eb9101918 Add mangleProperties documentation to README
Add additional documentation to mangleProperties.
2016-01-27 14:24:32 -06:00
kzc
0a38a688f9 fix bug in collapse_vars for right side of "||" and "&&" 2016-01-27 14:18:46 -05:00
kzc
f4c2ea37bf Collapse single use var definitions
Fix #721
2016-01-27 11:48:15 +02:00
Mihai Bazon
915f907186 Add start/end in the arguments definition
(keeps my https://github.com/mishoo/jsinfo.el working)
2016-01-27 11:36:03 +02:00
Jeremy Marzka
799509e145 Added a mangle properties option 2016-01-26 22:10:08 +01:00
Richard van Velzen
b5a7197ae5 Merge pull request #928 from STRML/constPragma
Mark vars with /** @const */ pragma as consts so they can be eliminated.
2016-01-20 19:04:36 +01:00
Samuel Reed
1b703349cf Tighten up @const regex. 2016-01-20 11:35:45 -06:00
Samuel Reed
4a7179ff91 Simplify by skipping extra tree walk. 2016-01-20 11:03:41 -06:00
Samuel Reed
f97da4294a Use TreeWalker for more accurate @const results and update tests 2016-01-20 10:54:00 -06:00
Samuel Reed
918c17bd88 Update README for /** @const */ 2016-01-19 13:24:36 -06:00
Samuel Reed
8b71c6559b Mark vars with /** @const */ pragma as consts so they can be eliminated.
Fixes older browser support for consts and allows more flexibility
in dead code removal.
2016-01-19 13:23:02 -06:00
Anthony Van de Gejuchte
26641f3fb2 Allow operator names as getters/setters
Fixes #919

Fix provided by @kzc
2016-01-19 19:28:51 +01:00
Anthony Van de Gejuchte
ebe118dc79 Add keywords to package.json
Should hopefully bump up on the results of the npm site when searching `uglify`
2016-01-19 19:26:55 +01:00
Anthony Van de Gejuchte
70e5b6f15b Add some tests for comment-filters through api
Also never bother comment options to filter comment5/shebang comments
as they have their custom filter.
2016-01-19 19:14:19 +01:00
Richard van Velzen
57e0fafd5c Merge pull request #918 from avdg/fix-arguments-handling
Never mangle arguments and keep them in their scope
2016-01-18 18:35:48 +01:00
Anthony Van de Gejuchte
8439c8ba98 Make arguments test slightly more strict 2016-01-15 00:04:05 +01:00
Anthony Van de Gejuchte
5c4e470d43 Add scope test for arguments 2016-01-14 22:32:46 +01:00
Anthony Van de Gejuchte
6605d15783 Never mangle arguments and keep them in their scope
Fixes #892

Helped-by: kzc
2016-01-14 19:45:52 +01:00
Richard van Velzen
ac8db977b9 Merge pull request #905 from avdg/unit-tests
Add unit tests
2016-01-14 08:54:40 +01:00
Anthony Van de Gejuchte
88b77ddaa9 Add test case for line continuation 2016-01-13 00:34:56 +01:00
Mihai Bazon
fe4e9f9d97 Fix hoisting the var in ForIn
Close #913
2016-01-05 13:56:52 +02:00
Anthony Van de Gejuchte
8c6af09ae0 Add mocha tests 2015-12-27 22:38:20 +01:00
Anthony Van de Gejuchte
6f3e35bb3f Fix ch that could contain other newline characters 2015-12-27 22:24:37 +01:00
Anthony Van de Gejuchte
174404c0f3 Do not allow newlines in string literals 2015-12-26 15:08:37 +01:00
Richard van Velzen
60c4030a4d Merge pull request #874 from kzc/fix-conditionals
#873 Fix `conditionals` optimizations with default compress options
2015-12-26 14:28:33 +01:00
Richard van Velzen
ac810dc07a Merge pull request #896 from avdg/do-while-semicolon
Semicolon after do...while statement is optional
2015-12-26 14:26:22 +01:00
Anthony Van de Gejuchte
0cabedc526 Disable loop optimization for parse-only tests 2015-12-18 19:20:56 +01:00
Anthony Van de Gejuchte
5cd26c005b Add tests 2015-12-18 14:39:48 +01:00
Anthony Van de Gejuchte
bd99b00413 Semicolon after do...while statement is optional 2015-12-17 23:02:35 +01:00
Richard van Velzen
9e2f9f7910 Merge pull request #879 from ReadmeCritic/master
Update README URLs based on HTTP redirects
2015-12-07 19:04:56 +01:00
ReadmeCritic
e87c77ed41 Update README URLs based on HTTP redirects 2015-11-27 08:46:55 -08:00
kzc
774bda13cd #873 Fix conditionals optimizations with default compress options 2015-11-24 13:27:50 -05:00
Mihai Bazon
15b5f70338 v2.6.1 2015-11-16 12:10:47 +02:00
Mihai Bazon
7f48d5b33c Fix endless loop
Close #866
2015-11-16 12:08:24 +02:00
Mihai Bazon
b6968b6bd2 Limit max iterations for tighten_body
Ref #866
2015-11-16 12:08:24 +02:00
Richard van Velzen
08b80302eb Merge pull request #864 from plievone/patch-1
Fix docs for keep_fargs
2015-11-14 12:04:49 +01:00
plievone
645626ebe8 Fix docs for keep_fargs
Compression options `keep_fargs` and `unsafe` were decoupled in v.2.5.0 (commit 5fd1245), so document actual keep_fargs default.
2015-11-14 11:38:00 +02:00
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
44 changed files with 3737 additions and 297 deletions

View File

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

View File

@@ -125,14 +125,19 @@ The available options are:
--noerr Don't throw an error for unknown options in -c,
-b or -m.
--bare-returns Allow return outside of functions. Useful when
minifying CommonJS modules.
minifying CommonJS modules and Userscripts that
may be anonymous function wrapped (IIFE) by the
.user.js engine `caller`.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--reserved-file File containing reserved names
--reserve-domprops Make (most?) DOM properties reserved for
--mangle-props
--mangle-props Mangle property names
--mangle-regex Only mangle property names matching the regex
--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
@@ -251,6 +256,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
`--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
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
@@ -316,6 +325,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
definitions when possible.
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
@@ -341,10 +353,15 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `drop_console` -- default `false`. Pass `true` to discard calls to
`console.*` functions.
- `keep_fargs` -- default `false`. Pass `true` to prevent the
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
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
It enables some transformations that *might* break code logic in certain
@@ -360,7 +377,6 @@ when this flag is on:
- `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
- discards unused function arguments (affects `function.length`)
### Conditional compilation
@@ -384,6 +400,8 @@ separate file and include it into the build. For example you can have a
```javascript
const DEBUG = false;
const PRODUCTION = true;
// Alternative for environments that don't support `const`
/** @const */ var STAGING = false;
// etc.
```
@@ -393,8 +411,8 @@ and build your code like this:
UglifyJS will notice the constants and, since they cannot be altered, it
will evaluate references to them to the value itself and drop unreachable
code as usual. The possible downside of this approach is that the build
will contain the `const` declarations.
code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments, use `/** @const */ var`.
<a name="codegen-options"></a>
## Beautifier options
@@ -613,6 +631,9 @@ Other options:
- `mangle` — pass `false` to skip mangling names.
- `mangleProperties` (default `false`) — pass an object to specify custom
mangle property options.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options][codegen]. The defaults are optimized
for best compression.
@@ -620,6 +641,13 @@ Other options:
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options][compressor].
- `parse` (default {}) — pass an object if you wish to specify some
additional [parser options][parser]. (not all options available... see below)
##### mangleProperties options
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mange-regex` CLI arguments option)
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
@@ -638,6 +666,9 @@ properties are available:
- `strict` — disable automatic semicolon insertion and support for trailing
comma in arrays and objects
- `bare_returns` — Allow return outside of functions. (maps to the
`--bare-returns` CLI arguments option and available to `minify` `parse`
other options object)
- `filename` — the name of the file where this code is coming from
- `toplevel` — a `toplevel` node (as returned by a previous invocation of
`parse`)
@@ -772,8 +803,9 @@ The `source_map_options` (optional) can contain the following properties:
came from. It can be simply a string in JSON, or a JSON object containing
the original source map.
[acorn]: https://github.com/marijnh/acorn
[acorn]: https://github.com/ternjs/acorn
[source-map]: https://github.com/mozilla/source-map
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
[codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress
[parser]: http://lisperator.net/uglifyjs/parser

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("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
.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("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("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("name-cache")
.array("reserved-file")
.array("pure-funcs")
.boolean("expr")
.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("acorn")
.boolean("spidermonkey")
.boolean("dump-spidermonkey-ast")
.boolean("lint")
.boolean("V")
.boolean("version")
@@ -174,6 +179,10 @@ if (ARGS.d) {
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
}
if (ARGS.pure_funcs) {
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
}
if (ARGS.r) {
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);
}
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) {
ARGS.quotes = 3;
}
@@ -217,9 +235,8 @@ if (BEAUTIFY)
if (ARGS.comments != null) {
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
var regex_pos = ARGS.comments.lastIndexOf("/");
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) {
print_error("ERROR: Invalid --comments: " + e.message);
}
@@ -245,7 +262,6 @@ if (ARGS.self) {
}
files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
ARGS.export_all = true;
}
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(){
var reserved = RESERVED ? RESERVED.props : null;
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, {
reserved : reserved,
cache : cache,
only_cache : !ARGS.mangle_props
only_cache : !ARGS.mangle_props,
regex : regex
});
writeNameCache("props", cache);
})();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars");
if (SCOPE_IS_NEEDED) {
@@ -425,26 +451,30 @@ async.eachLimit(files, 1, function (file, cb) {
}
}
time_it("generate", function(){
TOPLEVEL.print(output);
});
output = output.get();
if (SOURCE_MAP) {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
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) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
print(output);
time_it("generate", function(){
TOPLEVEL.print(output);
});
output = output.get();
if (SOURCE_MAP) {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
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) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else {
print(output);
}
}
if (ARGS.stats) {
@@ -469,17 +499,19 @@ function normalize(o) {
}
}
function getOptions(x, constants) {
x = ARGS[x];
if (x == null) return null;
function getOptions(flag, constants) {
var x = ARGS[flag];
if (x == null || x === false) return null;
var ret = {};
if (x !== "") {
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
var ast;
try {
ast = UglifyJS.parse(x, { expression: true });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments in: " + x);
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}
}
@@ -499,7 +531,7 @@ function getOptions(x, constants) {
return true; // no descend
}
print_error(node.TYPE)
print_error("Error parsing arguments in: " + x);
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}));
}

View File

@@ -81,10 +81,11 @@ function DEFNODE(type, props, methods, base) {
ctor.DEFMETHOD = function(name, method) {
this.prototype[name] = method;
};
exports["AST_" + type] = 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);
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 = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_SimpleStatement) {
node = node.body;
if (node instanceof AST_String) switch (node.getValue()) {
if (node instanceof AST_Directive) {
switch (node.value) {
case "$ORIG":
return MAP.splice(self.body);
case "$EXPORTS":
@@ -864,10 +864,11 @@ var AST_String = DEFNODE("String", "value quote", {
}
}, AST_Constant);
var AST_Number = DEFNODE("Number", "value", {
var AST_Number = DEFNODE("Number", "value literal", {
$documentation: "A number literal",
$propdoc: {
value: "[number] the numeric value"
value: "[number] the numeric value",
literal: "[string] numeric value as string (optional)"
}
}, AST_Constant);
@@ -926,27 +927,36 @@ var AST_True = DEFNODE("True", null, {
function TreeWalker(callback) {
this.visit = callback;
this.stack = [];
this.directives = Object.create(null);
};
TreeWalker.prototype = {
_visit: function(node, descend) {
this.stack.push(node);
this.push(node);
var ret = this.visit(node, descend ? function(){
descend.call(node);
} : noop);
if (!ret && descend) {
descend.call(node);
}
this.stack.pop();
this.pop(node);
return ret;
},
parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)];
},
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);
},
pop: function() {
return this.stack.pop();
pop: function(node) {
this.stack.pop();
if (node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
},
self: function() {
return this.stack[this.stack.length - 1];
@@ -959,7 +969,16 @@ TreeWalker.prototype = {
}
},
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() {
var stack = this.stack;

View File

@@ -61,11 +61,12 @@ function Compressor(options, false_by_default) {
loops : !false_by_default,
unused : !false_by_default,
hoist_funs : !false_by_default,
keep_fargs : false,
keep_fargs : true,
keep_fnames : false,
hoist_vars : false,
if_return : !false_by_default,
join_vars : !false_by_default,
collapse_vars : false,
cascade : !false_by_default,
side_effects : !false_by_default,
pure_getters : false,
@@ -111,6 +112,7 @@ merge(Compressor.prototype, {
node.DEFMETHOD("optimize", function(compressor){
var self = this;
if (self._optimized) return self;
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
if (opt === self) return opt;
@@ -174,6 +176,23 @@ merge(Compressor.prototype, {
}
};
// we shouldn't compress (1,func)(something) to
// func(something) because that changes the meaning of
// the func (becomes lexical instead of global).
function maintain_this_binding(parent, orig, val) {
if (parent instanceof AST_Call && parent.expression === orig) {
if (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name === "eval") {
return make_node(AST_Seq, orig, {
car: make_node(AST_Number, orig, {
value: 0
}),
cdr: val
});
}
}
return val;
}
function as_statement_array(thing) {
if (thing === null) return [];
if (thing instanceof AST_BlockStatement) return thing.body;
@@ -198,7 +217,7 @@ merge(Compressor.prototype, {
};
function tighten_body(statements, compressor) {
var CHANGED;
var CHANGED, max_iter = 10;
do {
CHANGED = false;
if (compressor.option("angular")) {
@@ -217,7 +236,10 @@ merge(Compressor.prototype, {
if (compressor.option("join_vars")) {
statements = join_consecutive_vars(statements, compressor);
}
} while (CHANGED);
if (compressor.option("collapse_vars")) {
statements = collapse_single_use_vars(statements, compressor);
}
} while (CHANGED && max_iter-- > 0);
if (compressor.option("negate_iife")) {
negate_iifes(statements, compressor);
@@ -225,6 +247,163 @@ merge(Compressor.prototype, {
return statements;
function collapse_single_use_vars(statements, compressor) {
// Iterate statements backwards looking for a statement with a var/const
// declaration immediately preceding it. Grab the rightmost var definition
// and if it has exactly one reference then attempt to replace its reference
// in the statement with the var value and then erase the var definition.
var self = compressor.self();
var var_defs_removed = false;
for (var stat_index = statements.length; --stat_index >= 0;) {
var stat = statements[stat_index];
if (stat instanceof AST_Definitions) continue;
// Process child blocks of statement if present.
[stat, stat.body, stat.alternative, stat.bcatch, stat.bfinally].forEach(function(node) {
node && node.body && collapse_single_use_vars(node.body, compressor);
});
// The variable definition must precede a statement.
if (stat_index <= 0) break;
var prev_stat_index = stat_index - 1;
var prev_stat = statements[prev_stat_index];
if (!(prev_stat instanceof AST_Definitions)) continue;
var var_defs = prev_stat.definitions;
if (var_defs == null) continue;
var var_names_seen = {};
var side_effects_encountered = false;
var lvalues_encountered = false;
var lvalues = {};
// Scan variable definitions from right to left.
for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) {
// Obtain var declaration and var name with basic sanity check.
var var_decl = var_defs[var_defs_index];
if (var_decl.value == null) break;
var var_name = var_decl.name.name;
if (!var_name || !var_name.length) break;
// Bail if we've seen a var definition of same name before.
if (var_name in var_names_seen) break;
var_names_seen[var_name] = true;
// Only interested in cases with just one reference to the variable.
var def = self.find_variable && self.find_variable(var_name);
if (!def || !def.references || def.references.length !== 1 || var_name == "arguments") {
side_effects_encountered = true;
continue;
}
var ref = def.references[0];
// Don't replace ref if eval() or with statement in scope.
if (ref.scope.uses_eval || ref.scope.uses_with) break;
// Constant single use vars can be replaced in any scope.
if (var_decl.value.is_constant(compressor)) {
var ctt = new TreeTransformer(function(node) {
if (node === ref)
return replace_var(node, ctt.parent(), true);
});
stat.transform(ctt);
continue;
}
// Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue;
// Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) {
side_effects_encountered |= var_decl.value.has_side_effects(compressor);
continue;
}
// Detect lvalues in var value.
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
lvalues[node.name] = lvalues_encountered = true;
}
});
var_decl.value.walk(tw);
// Replace the non-constant single use var in statement if side effect free.
var unwind = false;
var tt = new TreeTransformer(
function preorder(node) {
if (unwind) return node;
var parent = tt.parent();
if (node instanceof AST_Lambda
|| node instanceof AST_Try
|| node instanceof AST_With
|| node instanceof AST_Case
|| node instanceof AST_IterationStatement
|| (parent instanceof AST_If && node !== parent.condition)
|| (parent instanceof AST_Conditional && node !== parent.condition)
|| (parent instanceof AST_Binary
&& (parent.operator == "&&" || parent.operator == "||")
&& node === parent.right)
|| (parent instanceof AST_Switch && node !== parent.expression)) {
return side_effects_encountered = unwind = true, node;
}
},
function postorder(node) {
if (unwind) return node;
if (node === ref)
return unwind = true, replace_var(node, tt.parent(), false);
if (side_effects_encountered |= node.has_side_effects(compressor))
return unwind = true, node;
if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
side_effects_encountered = true;
return unwind = true, node;
}
}
);
stat.transform(tt);
}
}
// Remove extraneous empty statments in block after removing var definitions.
// Leave at least one statement in `statements`.
if (var_defs_removed) for (var i = statements.length; --i >= 0;) {
if (statements.length > 1 && statements[i] instanceof AST_EmptyStatement)
statements.splice(i, 1);
}
return statements;
function is_lvalue(node, parent) {
return node instanceof AST_SymbolRef && (
(parent instanceof AST_Assign && node === parent.left)
|| (parent instanceof AST_Unary && parent.expression === node
&& (parent.operator == "++" || parent.operator == "--")));
}
function replace_var(node, parent, is_constant) {
if (is_lvalue(node, parent)) return node;
// Remove var definition and return its value to the TreeTransformer to replace.
var value = maintain_this_binding(parent, node, var_decl.value);
var_decl.value = null;
var_defs.splice(var_defs_index, 1);
if (var_defs.length === 0) {
statements[prev_stat_index] = make_node(AST_EmptyStatement, self);
var_defs_removed = true;
}
// Further optimize statement after substitution.
stat.walk(new TreeWalker(function(node){
delete node._squeezed;
delete node._optimized;
}));
compressor.warn("Replacing " + (is_constant ? "constant" : "variable") +
" " + var_name + " [{file}:{line},{col}]", node.start);
CHANGED = true;
return value;
}
}
function process_for_angular(statements) {
function has_inject(comment) {
return /@ngInject/.test(comment.value);
@@ -383,7 +562,12 @@ merge(Compressor.prototype, {
continue loop;
}
//---
if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
// XXX: what was the intention of this case?
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
if (compressor.option("sequences")
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
&& (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
CHANGED = true;
ret.push(make_node(AST_Return, ret[0], {
@@ -720,6 +904,32 @@ merge(Compressor.prototype, {
return [ this ];
}
});
AST_Node.DEFMETHOD("is_constant", function(compressor){
// Accomodate when compress option evaluate=false
// as well as the common constant expressions !0 and !1
return this instanceof AST_Constant
|| (this instanceof AST_UnaryPrefix && this.operator == "!"
&& this.expression instanceof AST_Constant)
|| this.evaluate(compressor).length > 1;
});
// Obtain the constant value of an expression already known to be constant.
// Result only valid iff this.is_constant(compressor) is true.
AST_Node.DEFMETHOD("constant_value", function(compressor){
// Accomodate when option evaluate=false.
if (this instanceof AST_Constant) return this.value;
// Accomodate the common constant expressions !0 and !1 when option evaluate=false.
if (this instanceof AST_UnaryPrefix
&& this.operator == "!"
&& this.expression instanceof AST_Constant) {
return !this.expression.value;
}
var result = this.evaluate(compressor)
if (result.length > 1) {
return result[1];
}
// should never be reached
return undefined;
});
def(AST_Statement, function(){
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
});
@@ -896,6 +1106,7 @@ merge(Compressor.prototype, {
def(AST_Call, function(compressor){
var pure = compressor.option("pure_funcs");
if (!pure) return true;
if (typeof pure == "function") return pure(this);
return pure.indexOf(this.expression.print_to_string()) < 0;
});
@@ -989,7 +1200,7 @@ merge(Compressor.prototype, {
/* -----[ optimizers ]----- */
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 self;
@@ -1025,6 +1236,7 @@ merge(Compressor.prototype, {
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
var self = this;
if (compressor.has_directive("use asm")) return self;
if (compressor.option("unused")
&& !(self instanceof AST_Toplevel)
&& !self.uses_eval
@@ -1086,7 +1298,7 @@ merge(Compressor.prototype, {
var tt = new TreeTransformer(
function before(node, descend, in_list) {
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;) {
var sym = a[i];
if (sym.unreferenced()) {
@@ -1204,9 +1416,10 @@ merge(Compressor.prototype, {
});
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_vars = compressor.option("hoist_vars");
var self = this;
if (hoist_funs || hoist_vars) {
var dirs = [];
var hoisted = [];
@@ -1241,7 +1454,10 @@ merge(Compressor.prototype, {
var seq = node.to_assignments();
var p = tt.parent();
if (p instanceof AST_ForIn && p.init === node) {
if (seq == null) return node.definitions[0].name;
if (seq == null) {
var def = node.definitions[0].name;
return make_node(AST_SymbolRef, def, def);
}
return seq;
}
if (p instanceof AST_For && p.init === node) {
@@ -1472,9 +1688,13 @@ merge(Compressor.prototype, {
}
if (is_empty(self.alternative)) self.alternative = null;
var negated = self.condition.negate(compressor);
var negated_is_best = best_of(self.condition, negated) === negated;
var self_condition_length = self.condition.print_to_string().length;
var negated_length = negated.print_to_string().length;
var negated_is_best = negated_length < self_condition_length;
if (self.alternative && negated_is_best) {
negated_is_best = false; // because we already do the switch here.
// no need to swap values of self_condition_length and negated_length
// here because they are only used in an equality comparison later on.
self.condition = negated;
var tmp = self.body;
self.body = self.alternative || make_node(AST_EmptyStatement);
@@ -1496,6 +1716,13 @@ merge(Compressor.prototype, {
}).transform(compressor);
}
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
if (self_condition_length === negated_length && !negated_is_best
&& self.condition instanceof AST_Binary && self.condition.operator == "||") {
// although the code length of self.condition and negated are the same,
// negated does not require additional surrounding parentheses.
// see https://github.com/mishoo/UglifyJS2/issues/979
negated_is_best = true;
}
if (negated_is_best) return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
operator : "||",
@@ -1909,17 +2136,7 @@ merge(Compressor.prototype, {
if (!compressor.option("side_effects"))
return self;
if (!self.car.has_side_effects(compressor)) {
// we shouldn't compress (1,eval)(something) to
// eval(something) because that changes the meaning of
// eval (becomes lexical instead of global).
var p;
if (!(self.cdr instanceof AST_SymbolRef
&& self.cdr.name == "eval"
&& self.cdr.undeclared()
&& (p = compressor.parent()) instanceof AST_Call
&& p.expression === self)) {
return self.cdr;
}
return maintain_this_binding(compressor.parent(), self, self.cdr);
}
if (compressor.option("cascade")) {
if (self.car instanceof AST_Assign
@@ -2031,15 +2248,14 @@ merge(Compressor.prototype, {
var commutativeOperators = makePredicate("== === != !== * & | ^");
OPT(AST_Binary, function(self, compressor){
var reverse = compressor.has_directive("use asm") ? noop
: function(op, force) {
if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
if (op) self.operator = op;
var tmp = self.left;
self.left = self.right;
self.right = tmp;
}
};
function reverse(op, force) {
if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
if (op) self.operator = op;
var tmp = self.left;
self.left = self.right;
self.right = tmp;
}
}
if (commutativeOperators(self.operator)) {
if (self.right instanceof AST_Constant
&& !(self.left instanceof AST_Constant)) {
@@ -2104,6 +2320,32 @@ merge(Compressor.prototype, {
}
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);
return maintain_this_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
} else {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, 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 maintain_this_binding(compressor.parent(), self, ll[0]);
} else {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
}
}
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
case "&&":
var ll = self.left.evaluate(compressor);
@@ -2155,7 +2397,7 @@ merge(Compressor.prototype, {
}
break;
}
if (compressor.option("comparisons")) {
if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, {
@@ -2230,10 +2472,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
&& self.right.operator == self.operator
&& (self.operator == "*" || self.operator == "&&" || self.operator == "||"))
&& (self.operator == "&&" || self.operator == "||"))
{
self.left = make_node(AST_Binary, self.left, {
operator : self.operator,
@@ -2247,7 +2490,15 @@ merge(Compressor.prototype, {
});
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");
if (defines && defines.hasOwnProperty(self.name)) {
return make_node_from_constant(compressor, defines[self.name], self);
@@ -2315,10 +2566,10 @@ merge(Compressor.prototype, {
if (cond.length > 1) {
if (cond[1]) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return self.consequent;
return maintain_this_binding(compressor.parent(), self, self.consequent);
} else {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return self.alternative;
return maintain_this_binding(compressor.parent(), self, self.alternative);
}
}
var negated = cond[0].negate(compressor);
@@ -2386,32 +2637,52 @@ merge(Compressor.prototype, {
alternative: alternative
});
}
// x=y?1:1 --> x=1
if (consequent instanceof AST_Constant
&& alternative instanceof AST_Constant
// y?1:1 --> 1
if (consequent.is_constant(compressor)
&& alternative.is_constant(compressor)
&& consequent.equivalent_to(alternative)) {
var consequent_value = consequent.constant_value();
if (self.condition.has_side_effects(compressor)) {
return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent.value, self)]);
return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent_value, self)]);
} else {
return make_node_from_constant(compressor, consequent.value, self);
return make_node_from_constant(compressor, consequent_value, self);
}
}
// x=y?true:false --> x=!!y
if (consequent instanceof AST_True
&& alternative instanceof AST_False) {
// y?true:false --> !!y
if (is_true(consequent) && is_false(alternative)) {
if (self.condition.is_boolean()) {
// boolean_expression ? true : false --> boolean_expression
return self.condition;
}
self.condition = self.condition.negate(compressor);
return make_node(AST_UnaryPrefix, self.condition, {
operator: "!",
expression: self.condition
});
}
// x=y?false:true --> x=!y
if (consequent instanceof AST_False
&& alternative instanceof AST_True) {
// y?false:true --> !y
if (is_false(consequent) && is_true(alternative)) {
return self.condition.negate(compressor)
}
return self;
// AST_True or !0
function is_true(node) {
return node instanceof AST_True
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !node.expression.value);
}
// AST_False or !1
function is_false(node) {
return node instanceof AST_False
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !!node.expression.value);
}
});
OPT(AST_Boolean, function(self, compressor){
@@ -2483,4 +2754,11 @@ merge(Compressor.prototype, {
OPT(AST_Object, 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":
return new (val ? AST_True : AST_False)(args);
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);
}
},
@@ -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) {
var value = M.value;
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
@@ -343,13 +363,15 @@
prefix: true,
argument: {
type: "Literal",
value: -value
value: -value,
raw: M.start.raw
}
};
}
return {
type: "Literal",
value: value
value: value,
raw: M.start.raw
};
});
@@ -369,6 +391,12 @@
/* -----[ tools ]----- */
function raw_token(moznode) {
if (moznode.type == "Literal") {
return moznode.raw != null ? moznode.raw : moznode.value + "";
}
}
function my_start_token(moznode) {
var loc = moznode.loc, start = loc && loc.start;
var range = moznode.range;
@@ -379,7 +407,8 @@
pos : range ? range[0] : moznode.start,
endline : start && start.line,
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,
endline : end && end.line,
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) {
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" +
"end: my_end_token(M)";
@@ -442,8 +472,8 @@
//me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
//console.log(moz_to_me);
moz_to_me = new Function("my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
my_start_token, my_end_token, from_moz
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
exports, my_start_token, my_end_token, from_moz
);
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
to_moz, to_moz_block

View File

@@ -60,6 +60,7 @@ function OutputStream(options) {
bracketize : false,
semicolons : true,
comments : false,
shebang : true,
preserve_line : false,
screw_ie8 : false,
preamble : null,
@@ -87,13 +88,14 @@ function OutputStream(options) {
function make_string(str, quote) {
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) {
case "\\": return "\\\\";
case "\b": return "\\b";
case "\f": return "\\f";
case "\n": return "\\n";
case "\r": return "\\r";
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
case '"': ++dq; return '"';
@@ -124,8 +126,11 @@ function OutputStream(options) {
function encode_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(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
return ret;
};
@@ -161,6 +166,8 @@ function OutputStream(options) {
str = String(str);
var ch = str.charAt(0);
if (might_need_semicolon) {
might_need_semicolon = false;
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
if (options.semicolons || requireSemicolonChars(ch)) {
OUTPUT += ";";
@@ -171,11 +178,17 @@ function OutputStream(options) {
current_pos++;
current_line++;
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)
might_need_space = false;
}
might_need_semicolon = false;
}
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
@@ -369,8 +382,13 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
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() {
self.add_comments(stream);
self.add_source_map(stream);
@@ -383,6 +401,9 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Lambda) {
use_asm = prev_use_asm;
}
});
AST_Node.DEFMETHOD("print_to_string", function(options){
@@ -395,63 +416,69 @@ function OutputStream(options) {
AST_Node.DEFMETHOD("add_comments", function(output){
var c = output.option("comments"), self = this;
if (c) {
var start = self.start;
if (start && !start._comments_dumped) {
start._comments_dumped = true;
var comments = start.comments_before || [];
var start = self.start;
if (start && !start._comments_dumped) {
start._comments_dumped = true;
var comments = start.comments_before || [];
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
// and https://github.com/mishoo/UglifyJS2/issues/372
if (self instanceof AST_Exit && self.value) {
self.value.walk(new TreeWalker(function(node){
if (node.start && node.start.comments_before) {
comments = comments.concat(node.start.comments_before);
node.start.comments_before = [];
}
if (node instanceof AST_Function ||
node instanceof AST_Array ||
node instanceof AST_Object)
{
return true; // don't go inside.
}
}));
}
if (c.test) {
comments = comments.filter(function(comment){
return c.test(comment.value);
});
} else if (typeof c == "function") {
comments = comments.filter(function(comment){
return c(self, comment);
});
}
// Keep single line comments after nlb, after nlb
if (!output.option("beautify") && comments.length > 0 &&
/comment[134]/.test(comments[0].type) &&
output.col() !== 0 && comments[0].nlb)
{
output.print("\n");
}
comments.forEach(function(c){
if (/comment[134]/.test(c.type)) {
output.print("//" + c.value + "\n");
output.indent();
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
// and https://github.com/mishoo/UglifyJS2/issues/372
if (self instanceof AST_Exit && self.value) {
self.value.walk(new TreeWalker(function(node){
if (node.start && node.start.comments_before) {
comments = comments.concat(node.start.comments_before);
node.start.comments_before = [];
}
else if (c.type == "comment2") {
output.print("/*" + c.value + "*/");
if (start.nlb) {
output.print("\n");
output.indent();
} else {
output.space();
}
if (node instanceof AST_Function ||
node instanceof AST_Array ||
node instanceof AST_Object)
{
return true; // don't go inside.
}
}));
}
if (!c) {
comments = comments.filter(function(comment) {
return comment.type == "comment5";
});
} else if (c.test) {
comments = comments.filter(function(comment){
return comment.type == "comment5" || c.test(comment.value);
});
} else if (typeof c == "function") {
comments = comments.filter(function(comment){
return comment.type == "comment5" || c(self, comment);
});
}
// Keep single line comments after nlb, after nlb
if (!output.option("beautify") && comments.length > 0 &&
/comment[134]/.test(comments[0].type) &&
output.col() !== 0 && comments[0].nlb)
{
output.print("\n");
}
comments.forEach(function(c){
if (/comment[134]/.test(c.type)) {
output.print("//" + c.value + "\n");
output.indent();
}
else if (c.type == "comment2") {
output.print("/*" + c.value + "*/");
if (start.nlb) {
output.print("\n");
output.indent();
} else {
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);
});
DEFPRINT(AST_Binary, function(self, output){
var op = self.operator;
self.left.print(output);
output.space();
output.print(self.operator);
if (self.operator == "<"
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.print(op);
if ((op == "<" || op == "<<")
&& 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"
@@ -1143,7 +1178,11 @@ function OutputStream(options) {
output.print_string(self.getValue(), self.quote);
});
DEFPRINT(AST_Number, function(self, output){
output.print(make_num(self.getValue()));
if (use_asm && self.start.raw != null) {
output.print(self.start.raw);
} else {
output.print(make_num(self.getValue()));
}
});
function regexp_safe_literal(code) {
@@ -1319,6 +1358,12 @@ function OutputStream(options) {
DEFMAP(AST_Finally, basic_sourcemap_gen);
DEFMAP(AST_Definitions, 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){
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_OCT_NUMBER = /^0[0-7]+$/;
var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
var OPERATORS = makePredicate([
"in",
@@ -182,8 +181,9 @@ function parse_js_number(num) {
return parseInt(num.substr(2), 16);
} else if (RE_OCT_NUMBER.test(num)) {
return parseInt(num.substr(1), 8);
} else if (RE_DEC_NUMBER.test(num)) {
return parseFloat(num);
} else {
var val = parseFloat(num);
if (val == num) return val;
}
};
@@ -210,7 +210,7 @@ function is_token(token, type, val) {
var EX_EOF = {};
function tokenizer($TEXT, filename, html5_comments) {
function tokenizer($TEXT, filename, html5_comments, shebang) {
var S = {
text : $TEXT,
@@ -285,6 +285,9 @@ function tokenizer($TEXT, filename, html5_comments) {
nlb : S.newline_before,
file : filename
};
if (/^(?:num|string|regexp)$/i.test(type)) {
ret.raw = $TEXT.substring(ret.pos, ret.endpos);
}
if (!is_comment) {
ret.comments_before = S.comments_before;
S.comments_before = [];
@@ -396,6 +399,7 @@ function tokenizer($TEXT, filename, html5_comments) {
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
else ch = read_escaped_char(true);
}
else if ("\r\n\u2028\u2029".indexOf(ch) >= 0) parse_error("Unterminated string constant");
else if (ch == quote) break;
ret += ch;
}
@@ -480,7 +484,11 @@ function tokenizer($TEXT, filename, html5_comments) {
regexp += ch;
}
var mods = read_name();
return token("regexp", new RegExp(regexp, mods));
try {
return token("regexp", new RegExp(regexp, mods));
} catch(e) {
parse_error(e.message);
}
});
function read_operator(prefix) {
@@ -564,6 +572,13 @@ function tokenizer($TEXT, filename, html5_comments) {
if (PUNC_CHARS(ch)) return token("punc", next());
if (OPERATOR_CHARS(ch)) return read_operator();
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 + "'");
};
@@ -633,12 +648,13 @@ function parse($TEXT, options) {
expression : false,
html5_comments : true,
bare_returns : false,
shebang : true,
});
var S = {
input : (typeof $TEXT == "string"
? tokenizer($TEXT, options.filename,
options.html5_comments)
options.html5_comments, options.shebang)
: $TEXT),
token : null,
prev : null,
@@ -709,9 +725,9 @@ function parse($TEXT, options) {
);
};
function semicolon() {
function semicolon(optional) {
if (is("punc", ";")) next();
else if (!can_insert_semicolon()) unexpected();
else if (!optional && !can_insert_semicolon()) unexpected();
};
function parenthesised() {
@@ -799,7 +815,7 @@ function parse($TEXT, options) {
case "do":
return new AST_Do({
body : in_loop(statement),
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
});
case "while":
@@ -1110,7 +1126,7 @@ function parse($TEXT, options) {
});
};
var new_ = function() {
var new_ = function(allow_calls) {
var start = S.token;
expect_token("operator", "new");
var newexp = expr_atom(false), args;
@@ -1125,7 +1141,7 @@ function parse($TEXT, options) {
expression : newexp,
args : args,
end : prev()
}), true);
}), allow_calls);
};
function as_atom_node() {
@@ -1162,6 +1178,13 @@ function parse($TEXT, options) {
break;
}
break;
case "operator":
if (!is_identifier_string(tok.value)) {
throw new JS_Parse_Error("Invalid getter/setter name: " + tok.value,
tok.file, tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
}
next();
return ret;
@@ -1169,7 +1192,7 @@ function parse($TEXT, options) {
var expr_atom = function(allow_calls) {
if (is("operator", "new")) {
return new_();
return new_(allow_calls);
}
var start = S.token;
if (is("punc")) {

View File

@@ -64,7 +64,8 @@ function mangle_properties(ast, options) {
options = defaults(options, {
reserved : null,
cache : null,
only_cache : false
only_cache : false,
regex : null
});
var reserved = options.reserved;
@@ -79,7 +80,10 @@ function mangle_properties(ast, options) {
};
}
var regex = options.regex;
var names_to_mangle = [];
var unmangleable = [];
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
@@ -105,20 +109,14 @@ function mangle_properties(ast, options) {
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
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) {
// 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) {
if (should_mangle(node.property)) {
node.property = mangle(node.property);
}
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
@@ -140,6 +138,7 @@ function mangle_properties(ast, options) {
// only function declarations after this line
function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.props.has(name);
@@ -149,6 +148,7 @@ function mangle_properties(ast, options) {
}
function should_mangle(name) {
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0;
@@ -157,9 +157,17 @@ function mangle_properties(ast, options) {
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
if (!should_mangle(name)) {
push_uniq(unmangleable, name);
}
}
function mangle(name) {
if (!should_mangle(name)) {
return name;
}
var mangled = cache.props.get(name);
if (!mangled) {
do {
@@ -202,9 +210,7 @@ function mangle_properties(ast, options) {
node.cdr = mangleStrings(node.cdr);
}
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) {
node.consequent = mangleStrings(node.consequent);

View File

@@ -92,7 +92,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var last_var_had_const_pragma = false;
var nesting = 0;
var tw = new TreeWalker(function(node, descend){
if (options.screw_ie8 && node instanceof AST_Catch) {
@@ -108,16 +110,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.init_scope_vars(nesting);
var save_scope = node.parent_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
++nesting; descend(); --nesting;
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Directive) {
node.scope = scope;
push_uniq(scope.directives, node.value);
return true;
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_With) {
for (var s = scope; s; s = s.parent_scope)
@@ -127,6 +137,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
}
@@ -138,16 +152,28 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// later.
(node.scope = defun.parent_scope).def_function(node);
}
else if (node instanceof AST_Var) {
last_var_had_const_pragma = node.has_const_pragma();
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
var def = defun.def_variable(node);
def.constant = node instanceof AST_SymbolConst;
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
(options.screw_ie8 ? scope : defun)
.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);
@@ -162,8 +188,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
func = prev_func;
return true;
}
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
}
if (node instanceof AST_SymbolRef) {
var name = node.name;
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true;
}
}
var sym = node.scope.find_variable(name);
if (!sym) {
var g;
@@ -176,10 +211,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
globals.set(name, g);
}
node.thedef = g;
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
s.uses_eval = true;
}
if (func && name == "arguments") {
func.uses_arguments = true;
}
@@ -198,7 +229,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
});
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.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
@@ -209,13 +239,13 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
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_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end });
var def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
});
AST_SymbolRef.DEFMETHOD("reference", function() {
@@ -236,11 +266,6 @@ AST_Scope.DEFMETHOD("find_variable", function(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){
this.functions.set(symbol.name, this.def_variable(symbol));
});
@@ -337,6 +362,12 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Var.DEFMETHOD("has_const_pragma", function() {
var comments_before = this.start && this.start.comments_before;
var lastComment = comments_before && comments_before[comments_before.length - 1];
return lastComment && /@const\b/.test(lastComment.value);
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
@@ -350,6 +381,10 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of

View File

@@ -53,16 +53,11 @@ function SourceMap(options) {
orig_line_diff : 0,
dest_line_diff : 0,
});
var generator = new MOZ_SourceMap.SourceMapGenerator({
file : options.file,
sourceRoot : options.root
});
var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
var generator;
if (orig_map) {
generator = MOZ_SourceMap.SourceMapGenerator.fromSourceMap(orig_map);
} else {
generator = new MOZ_SourceMap.SourceMapGenerator({
file : options.file,
sourceRoot : options.root
});
}
function add(source, gen_line, gen_col, orig_line, orig_col, name) {
if (orig_map) {
var info = orig_map.originalPositionFor({
@@ -83,7 +78,7 @@ function SourceMap(options) {
source : source,
name : name
});
}
};
return {
add : add,
get : function() { return generator },

View File

@@ -64,13 +64,13 @@ TreeTransformer.prototype = new TreeWalker;
x = this;
descend(x, tw);
} else {
tw.stack[tw.stack.length - 1] = x = this.clone();
tw.stack[tw.stack.length - 1] = x = this;
descend(x, tw);
y = tw.after(x, in_list);
if (y !== undefined) x = y;
}
}
tw.pop();
tw.pop(this);
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",
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD",
"version": "2.4.23",
"license": "BSD-2-Clause",
"version": "2.6.2",
"engines": {
"node": ">=0.4.0"
"node": ">=0.8.0"
},
"maintainers": [
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
@@ -30,15 +30,16 @@
],
"dependencies": {
"async": "~0.2.6",
"source-map": "0.1.34",
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.5.4"
"yargs": "~3.10.0"
},
"devDependencies": {
"acorn": "~0.6.0",
"escodegen": "~1.3.3",
"esfuzz": "~0.3.1",
"estraverse": "~1.5.1"
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"browserify": {
"transform": [
@@ -46,6 +47,8 @@
]
},
"scripts": {
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
"test": "node test/run-tests.js"
}
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
}

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 };
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -332,55 +332,483 @@ cond_7_1: {
cond_8: {
options = {
conditionals: true,
evaluate : true
evaluate : true,
booleans : false
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
// don't compress these
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = condition ? 1 : false;
a = condition ? 0 : true;
a = condition ? 1 : 0;
}
}
cond_8b: {
options = {
conditionals: true,
evaluate : true,
booleans : true
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = condition ? 1 : !1;
a = condition ? 0 : !0;
a = condition ? 1 : 0;
}
}
cond_8c: {
options = {
conditionals: true,
evaluate : false,
booleans : false
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = condition() ? !0 : !-3.5;
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = condition ? 1 : false;
a = condition ? 0 : true;
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;
}
}
trivial_boolean_ternary_expressions : {
options = {
conditionals: true,
evaluate : true,
booleans : true
};
input: {
f('foo' in m ? true : false);
f('foo' in m ? false : true);
f(g ? true : false);
f(foo() ? true : false);
f("bar" ? true : false);
f(5 ? true : false);
f(5.7 ? true : false);
f(x - y ? true : false);
f(x == y ? true : false);
f(x === y ? !0 : !1);
f(x < y ? !0 : false);
f(x <= y ? true : false);
f(x > y ? true : !1);
f(x >= y ? !0 : !1);
f(g ? false : true);
f(foo() ? false : true);
f("bar" ? false : true);
f(5 ? false : true);
f(5.7 ? false : true);
f(x - y ? false : true);
f(x == y ? !1 : !0);
f(x === y ? false : true);
f(x < y ? false : true);
f(x <= y ? false : !0);
f(x > y ? !1 : true);
f(x >= y ? !1 : !0);
}
expect: {
f('foo' in m);
f(!('foo' in m));
f(!!g);
f(!!foo());
f(!0);
f(!0);
f(!0);
f(!!(x - y));
f(x == y);
f(x === y);
f(x < y);
f(x <= y);
f(x > y);
f(x >= y);
f(!g);
f(!foo());
f(!1);
f(!1);
f(!1);
f(!(x - y));
f(x != y);
f(x !== y);
f(!(x < y));
f(!(x <= y));
f(!(x > y));
f(!(x >= y));
}
}

View File

@@ -87,3 +87,120 @@ dead_code_constant_boolean_should_warn_more: {
var moo;
}
}
dead_code_const_declaration: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused;
const CONST_FOO = false;
if (CONST_FOO) {
console.log("unreachable");
var moo;
function bar() {}
}
}
expect: {
var unused;
const CONST_FOO = !1;
var moo;
function bar() {}
}
}
dead_code_const_annotation: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused;
/** @const */ var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
var moo;
function bar() {}
}
}
dead_code_const_annotation_regex: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused;
// @constraint this shouldn't be a constant
var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("reachable");
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
CONST_FOO_ANN && console.log('reachable');
}
}
dead_code_const_annotation_complex_scope: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused_var;
/** @const */ var test = 'test';
// @const
var CONST_FOO_ANN = false;
var unused_var_2;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
if (test === 'test') {
var beef = 'good';
/** @const */ var meat = 'beef';
var pork = 'bad';
if (meat === 'pork') {
console.log('also unreachable');
} else if (pork === 'good') {
console.log('reached, not const');
}
}
}
expect: {
var unused_var;
var test = 'test';
var CONST_FOO_ANN = !1;
var unused_var_2;
var moo;
function bar() {}
var beef = 'good';
var meat = 'beef';
var pork = 'bad';
'good' === pork && console.log('reached, not const');
}
}

View File

@@ -1,5 +1,5 @@
unused_funarg_1: {
options = { unused: true, unsafe: true };
options = { unused: true, keep_fargs: false };
input: {
function f(a, b, c, d, e) {
return a + b;
@@ -13,7 +13,7 @@ unused_funarg_1: {
}
unused_funarg_2: {
options = { unused: true, unsafe: true };
options = { unused: true, keep_fargs: false };
input: {
function f(a, b, c, d, e) {
return a + c;
@@ -173,7 +173,7 @@ keep_fnames: {
}
expect: {
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

@@ -9,3 +9,50 @@ keep_name_of_setter: {
input: { a = { set foo () {} } }
expect: { a = { set foo () {} } }
}
setter_with_operator_keys: {
input: {
var tokenCodes = {
get instanceof(){
return test0;
},
set instanceof(value){
test0 = value;
},
set typeof(value){
test1 = value;
},
get typeof(){
return test1;
},
set else(value){
test2 = value;
},
get else(){
return test2;
}
};
}
expect: {
var tokenCodes = {
get instanceof(){
return test0;
},
set instanceof(value){
test0 = value;
},
set typeof(value){
test1 = value;
},
get typeof(){
return test1;
},
set else(value){
test2 = value;
},
get else(){
return test2;
}
};
}
}

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,27 @@
remove_redundant_sequence_items: {
options = { side_effects: true };
input: {
(0, 1, eval)();
(0, 1, logThis)();
(0, 1, _decorators.logThis)();
}
expect: {
(0, eval)();
logThis();
(0, _decorators.logThis)();
}
}
dont_remove_this_binding_sequence: {
options = { side_effects: true };
input: {
(0, eval)();
(0, logThis)();
(0, _decorators.logThis)();
}
expect: {
(0, eval)();
logThis();
(0, _decorators.logThis)();
}
}

View File

@@ -0,0 +1,32 @@
dont_mangle_arguments: {
mangle = {
};
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 : false
};
input: {
(function(){
var arguments = arguments, not_arguments = 9;
console.log(not_arguments, arguments);
})(5,6,7);
}
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
}

View File

@@ -0,0 +1,20 @@
keep_var_for_in: {
options = {
hoist_vars: true,
unused: true
};
input: {
(function(obj){
var foo = 5;
for (var i in obj)
return foo;
})();
}
expect: {
(function(obj){
var i, foo = 5;
for (i in obj)
return foo;
})();
}
}

View File

@@ -0,0 +1,96 @@
this_binding_conditionals: {
options = {
conditionals: true,
evaluate : true
};
input: {
(1 && a)();
(0 || a)();
(0 || 1 && a)();
(1 ? a : 0)();
(1 && a.b)();
(0 || a.b)();
(0 || 1 && a.b)();
(1 ? a.b : 0)();
(1 && a[b])();
(0 || a[b])();
(0 || 1 && a[b])();
(1 ? a[b] : 0)();
(1 && eval)();
(0 || eval)();
(0 || 1 && eval)();
(1 ? eval : 0)();
}
expect: {
a();
a();
a();
a();
(0, a.b)();
(0, a.b)();
(0, a.b)();
(0, a.b)();
(0, a[b])();
(0, a[b])();
(0, a[b])();
(0, a[b])();
(0, eval)();
(0, eval)();
(0, eval)();
(0, eval)();
}
}
this_binding_collapse_vars: {
options = {
collapse_vars: true,
};
input: {
var c = a; c();
var d = a.b; d();
var e = eval; e();
}
expect: {
a();
(0, a.b)();
(0, eval)();
}
}
this_binding_side_effects: {
options = {
side_effects : true
};
input: {
(function (foo) {
(0, foo)();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
(function (foo) {
var eval = console;
(0, foo)();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
}
expect: {
(function (foo) {
foo();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
(function (foo) {
var eval = console;
foo();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
}
}

View File

@@ -0,0 +1,88 @@
eval_collapse_vars: {
options = {
collapse_vars:true, sequences:false, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
};
input: {
function f1() {
var e = 7;
var s = "abcdef";
var i = 2;
var eval = console.log.bind(console);
var x = s.charAt(i++);
var y = s.charAt(i++);
var z = s.charAt(i++);
eval(x, y, z, e);
}
function p1() { var a = foo(), b = bar(), eval = baz(); return a + b + eval; }
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
}
expect: {
function f1() {
var e = 7,
s = "abcdef",
i = 2,
eval = console.log.bind(console),
x = s.charAt(i++),
y = s.charAt(i++),
z = s.charAt(i++);
eval(x, y, z, e);
}
function p1() { return foo() + bar() + baz(); }
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
}
}
eval_unused: {
options = { unused: true, keep_fargs: false };
input: {
function f1(a, eval, c, d, e) {
return a('c') + eval;
}
function f2(a, b, c, d, e) {
return a + eval('c');
}
function f3(a, eval, c, d, e) {
return a + eval('c');
}
}
expect: {
function f1(a, eval) {
return a('c') + eval;
}
function f2(a, b, c, d, e) {
return a + eval('c');
}
function f3(a, eval, c, d, e) {
return a + eval('c');
}
}
}
eval_mangle: {
mangle = {
};
input: {
function f1(a, eval, c, d, e) {
return a('c') + eval;
}
function f2(a, b, c, d, e) {
return a + eval('c');
}
function f3(a, eval, c, d, e) {
return a + eval('c');
}
}
expect_exact: 'function f1(n,c,e,a,o){return n("c")+c}function f2(a,b,c,d,e){return a+eval("c")}function f3(a,eval,c,d,e){return a+eval("c")}'
}

View File

@@ -0,0 +1,89 @@
issue979_reported: {
options = {
sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1() {
if (a == 1 || b == 2) {
foo();
}
}
function f2() {
if (!(a == 1 || b == 2)) {
}
else {
foo();
}
}
}
expect: {
function f1() {
1!=a&&2!=b||foo();
}
function f2() {
1!=a&&2!=b||foo();
}
}
}
issue979_test_negated_is_best: {
options = {
sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f3() {
if (a == 1 | b == 2) {
foo();
}
}
function f4() {
if (!(a == 1 | b == 2)) {
}
else {
foo();
}
}
function f5() {
if (a == 1 && b == 2) {
foo();
}
}
function f6() {
if (!(a == 1 && b == 2)) {
}
else {
foo();
}
}
function f7() {
if (a == 1 || b == 2) {
foo();
}
else {
return bar();
}
}
}
expect: {
function f3() {
1==a|2==b&&foo();
}
function f4() {
1==a|2==b&&foo();
}
function f5() {
1==a&&2==b&&foo();
}
function f6() {
1!=a||2!=b||foo();
}
function f7() {
return 1!=a&&2!=b?bar():void foo();
}
}
}

View File

@@ -121,3 +121,27 @@ drop_if_else_break_4: {
for (; bar() && (x(), y(), foo());) baz(), z(), k();
}
}
parse_do_while_with_semicolon: {
options = { loops: false };
input: {
do {
x();
} while (false);y()
}
expect: {
do x(); while (false);y();
}
}
parse_do_while_without_semicolon: {
options = { loops: false };
input: {
do {
x();
} while (false)y()
}
expect: {
do x(); while (false);y();
}
}

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");';
}

29
test/mocha.js Normal file
View File

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

22
test/mocha/arguments.js Normal file
View File

@@ -0,0 +1,22 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("arguments", function() {
it("Should known that arguments in functions are local scoped", function() {
var ast = UglifyJS.parse("var arguments; var f = function() {arguments.length}");
ast.figure_out_scope();
// Test scope of `var arguments`
assert.strictEqual(ast.find_variable("arguments").global, true);
// Select arguments symbol in function
var symbol = ast.body[1].definitions[0].value.find_variable("arguments");
assert.strictEqual(symbol.global, false);
assert.strictEqual(symbol.scope, ast. // From ast
body[1]. // Select 2nd statement (equals to `var f ...`)
definitions[0]. // First definition of selected statement
value // Select function as scope
);
});
});

View File

@@ -0,0 +1,45 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("comment filters", function() {
it("Should be able to filter comments by passing regex", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
});
it("Should be able to filter comments by passing a function", function() {
var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars.");
var f = function(node, comment) {
return comment.value.length === 8;
};
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
});
it("Should be able to get the comment and comment type when using a function", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
var f = function(node, comment) {
return comment.type == "comment1" || comment.type == "comment3";
};
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6\n");
});
it("Should be able to filter comments by passing a boolean", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: false}), "");
});
it("Should never be able to filter comment5 (shebangs)", function() {
var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/");
var f = function(node, comment) {
assert.strictEqual(comment.type === "comment5", false);
return true;
};
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/\n");
});
});

View File

@@ -0,0 +1,89 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("Getters and setters", function() {
it("Should not accept operator symbols as getter/setter name", function() {
var illegalOperators = [
"++",
"--",
"+",
"-",
"!",
"~",
"&",
"|",
"^",
"*",
"/",
"%",
">>",
"<<",
">>>",
"<",
">",
"<=",
">=",
"==",
"===",
"!=",
"!==",
"?",
"=",
"+=",
"-=",
"/=",
"*=",
"%=",
">>=",
"<<=",
">>>=",
"|=",
"^=",
"&=",
"&&",
"||"
];
var generator = function() {
var results = [];
for (var i in illegalOperators) {
results.push({
code: "var obj = { get " + illegalOperators[i] + "() { return test; }};",
operator: illegalOperators[i],
method: "get"
});
results.push({
code: "var obj = { set " + illegalOperators[i] + "(value) { test = value}};",
operator: illegalOperators[i],
method: "set"
});
}
return results;
};
var testCase = function(data) {
return function() {
UglifyJS.parse(data.code);
};
};
var fail = function(data) {
return function (e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Invalid getter/setter name: " + data.operator;
};
};
var errorMessage = function(data) {
return "Expected but didn't get a syntax error while parsing following line:\n" + data.code;
};
var tests = generator();
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
assert.throws(testCase(test), fail(test), errorMessage(test));
}
});
});

View File

@@ -0,0 +1,34 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("String literals", function() {
it("Should throw syntax error if a string literal contains a newline", function() {
var inputs = [
"'\n'",
"'\r'",
'"\r\n"',
"'\u2028'",
'"\u2029"'
];
var test = function(input) {
return function() {
var ast = UglifyJS.parse(input);
};
};
var error = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Unterminated string constant";
};
for (var input in inputs) {
assert.throws(test(inputs[input]), error);
}
});
it("Should not throw syntax error if a string has a line continuation", function() {
var output = UglifyJS.parse('var a = "a\\\nb";').print_to_string();
assert.equal(output, 'var a="ab";');
});
});

View File

@@ -100,4 +100,4 @@ module.exports = function(options) {
}
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n");
};
};

View File

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

18
tools/exports.js Normal file
View File

@@ -0,0 +1,18 @@
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;
exports["SymbolDef"] = SymbolDef;

View File

@@ -1,26 +1,5 @@
var path = require("path");
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 = [
"../lib/utils.js",
@@ -32,35 +11,39 @@ var FILES = exports.FILES = [
"../lib/compress.js",
"../lib/sourcemap.js",
"../lib/mozilla-ast.js",
"../lib/propmangle.js"
"../lib/propmangle.js",
"./exports.js",
].map(function(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) {
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) {
options = UglifyJS.defaults(options, {
spidermonkey : false,
outSourceMap : null,
sourceRoot : null,
inSourceMap : null,
fromString : false,
warnings : false,
mangle : {},
output : null,
compress : {}
spidermonkey : false,
outSourceMap : null,
sourceRoot : null,
inSourceMap : null,
fromString : false,
warnings : false,
mangle : {},
mangleProperties : false,
nameCache : null,
output : null,
compress : {},
parse : {}
});
UglifyJS.base54.reset();
@@ -73,17 +56,21 @@ exports.minify = function(files, options) {
} else {
if (typeof files == "string")
files = [ files ];
files.forEach(function(file){
files.forEach(function(file, i){
var code = options.fromString
? file
: fs.readFileSync(file, "utf8");
sourcesContent[file] = code;
toplevel = UglifyJS.parse(code, {
filename: options.fromString ? "?" : file,
toplevel: toplevel
filename: options.fromString ? i : file,
toplevel: toplevel,
bare_returns: options.parse ? options.parse.bare_returns : undefined
});
});
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
}
// 2. compress
if (options.compress) {
@@ -94,14 +81,21 @@ exports.minify = function(files, options) {
toplevel = toplevel.transform(sq);
}
// 3. mangle
// 3. mangle properties
if (options.mangleProperties || options.nameCache) {
options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
}
// 4. mangle
if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
}
// 4. output
// 5. output
var inMap = options.inSourceMap;
var output = {};
if (typeof options.inSourceMap == "string") {
@@ -128,7 +122,7 @@ exports.minify = function(files, options) {
var stream = UglifyJS.OutputStream(output);
toplevel.print(stream);
if(options.outSourceMap){
if (options.outSourceMap && "string" === typeof options.outSourceMap) {
stream += "\n//# sourceMappingURL=" + options.outSourceMap;
}