Compare commits

...

141 Commits

Author SHA1 Message Date
Alex Lam S.L
491f16c766 v2.8.16 2017-03-25 03:21:16 +08:00
Alex Lam S.L
a30092e20f fix invalid AST_For.init (#1657)
Turns out the only place in `Compressor` which can generate invalid `AST_For.init` is within `drop_unused()`, so focus the fix-up efforts.

supercedes #1652
fixes #1656
2017-03-25 03:18:36 +08:00
Alex Lam S.L
b1abe92e1a introduce ufuzz.js (#1655)
closes #1647
2017-03-25 01:46:12 +08:00
Alex Lam S.L
b454ce667e Update ISSUE_TEMPLATE.md 2017-03-24 23:12:58 +08:00
Alex Lam S.L
32283a0def fix cascade of evaluate optimisation (#1654)
Operator has changed, so break out from rest of the rules.

fixes #1649
2017-03-24 22:09:19 +08:00
Alex Lam S.L
ac51d4c5a0 fix corner case in AST_For.init (#1652)
Enforce `null` as value for empty initialisation blocks.

fixes #1648
2017-03-24 19:31:17 +08:00
Alex Lam S.L
0432a7abb9 fix assignment extraction from conditional (#1651)
fixes #1645
fixes #1646
2017-03-24 18:52:48 +08:00
Alex Lam S.L
f3a1694a41 fix assignment substitution in sequences (#1643)
take side effects of binary boolean operations into account

fixes #1639
2017-03-24 14:30:31 +08:00
Alex Lam S.L
2e0dc97003 improve error marker placement (#1644)
For AST_UnaryPrefix, points to the operator rather than end of expression.
2017-03-24 14:28:40 +08:00
Alex Lam S.L
701035621d fix expect_stdout (#1642)
`compress()` may modify input ASTs

add tests for #1627 & #1640
2017-03-24 13:19:50 +08:00
kzc
79334dda10 fix regression: CLI options with hyphens like -b ascii-only (#1640)
fixes #1637
2017-03-24 11:55:03 +08:00
Alex Lam S.L
e918748d88 improve collapsible value detection (#1638)
- #1634 bars variables with cross-scope references in between to collapse
- but if assigned value is side-effect-free, no states can be modified, so it is safe to move
2017-03-24 02:55:32 +08:00
Alex Lam S.L
6b2f34769a v2.8.15 2017-03-23 13:36:47 +08:00
Alex Lam S.L
48ffbef51d account for cross-scope modifications in collapse_vars (#1634)
mostly done by @kzc

fixes #1631
2017-03-23 07:17:34 +08:00
Alex Lam S.L
c0f3feae9f introduce compressor.info() (#1633)
report the following only when `options.warnings = "verbose"`
- unused elements due to inlining
- collpased variables
2017-03-23 06:49:49 +08:00
Alex Lam S.L
a00040dd93 fix a bug in simple_glob (#1632)
- "?" should not match "/"
- other minor clean-ups
2017-03-23 06:11:16 +08:00
Alex Lam S.L
ee95c1b38b metadata cleanup (#1630)
- mention performance anomaly in Node 7 and drop from CI
- remove unused npm "scripts"
- mark browserify dependency as optional
- stop `test/mozilla-ast.js` from spamming console output in later versions of Node.js
2017-03-23 01:31:46 +08:00
Alex Lam S.L
4bceb85cbf throw parse error on invalid assignments (#1627)
fixes #1626
2017-03-21 14:11:32 +08:00
Alex Lam S.L
30a75049f5 v2.8.14 2017-03-19 15:24:57 +08:00
Alex Lam S.L
a3cc3a9b87 make expect_stdout work on Node.js 0.12 (#1623)
That particular version of Node.js has messed up error messages, so provide a version-specific workaround.

Also fixed an formatting issue which would cause `expect_stdout` to fail if error message contains excerpts of input.

Apply `expect_stdout` to more applicable tests.
2017-03-19 12:00:32 +08:00
Alex Lam S.L
96f8befdd7 fix commit 88fb83a (#1622)
The following is wrong:
    `a == (b ? a : c)` => `b`
Because:
- `b` may not be boolean
- `a` might have side effects
- `a == a` is not always `true` (think `NaN`)
- `a == c` is not always `false`
2017-03-19 11:59:42 +08:00
Alex Lam S.L
cd58635dcc fix AST_Binary.lift_sequences() (#1621)
Commit eab99a1c fails to account for side effects from compound assignments.
2017-03-19 03:04:22 +08:00
Alex Lam S.L
274331d0ea transform String.charAt() to index access (#1620)
Guarded by `unsafe` as `charAt()` can be overridden.
2017-03-19 02:17:15 +08:00
Alex Lam S.L
0489d6de64 handle runtime errors in expect_stdout (#1618)
allow test to pass if both `input` and `expect` throws the same kind of error
2017-03-18 02:33:51 +08:00
Alex Lam S.L
fb092839c2 fix top-level directives in compress tests (#1615)
`input` and `expect` are parsed as `AST_BlockStatement` which does not support `AST_Directive` by default.

Emulate that by transforming preceding `AST_SimpleStatement`s of `AST_String` into `AST_Directive`.
2017-03-18 01:56:15 +08:00
Christian Maughan Tegnér
b7c112eefe Add --in-source-map inline documentation (#1611) 2017-03-17 03:08:38 +08:00
Alex Lam S.L
b2b8a0d386 v2.8.13 2017-03-17 02:01:33 +08:00
Alex Lam S.L
ac40301813 fix chained evaluation (#1610)
`reduce_vars` enables substitution of variables but did not clone the value's `AST_Node`.

This confuses `collapse_vars` and result in invalid AST and subsequent crash.

fixes #1609
2017-03-17 00:26:48 +08:00
Alex Lam S.L
3563d8c09e extend test/run-tests.js to optionally execute uglified output (#1604)
fixes #1588
2017-03-16 23:20:06 +08:00
Alex Lam S.L
5ae04b3545 make collapse_vars consistent with toplevel (#1608)
fixes #1605
2017-03-16 13:22:26 +08:00
Alex Lam S.L
a80b228d8b fix hoist_vars on reduce_vars (#1607)
`hoist_vars` converts variable declarations into plain assignments, which then confuses `reduce_vars`

fixes #1606
2017-03-16 12:03:30 +08:00
Alex Lam S.L
cf4bf4ceb1 fix stack issues with AST_Node.evaluate() (#1603)
As patched in #1597, `make_node_from_constant()` makes inconsistent and sometimes incorrect calls to `optimize()` and `transform()`.

Fix those issues properly by changing the semantics of `evaluate()` and `make_node_from_constant()`, with the side effect that `evaluate()` no longer eagerly converts constant to `AST_Node`.
2017-03-16 01:02:59 +08:00
Alex Lam S.L
8223b2e0db fix AST_Node.optimize() (#1602)
Liberal use of `Compressor.transform()` and `AST_Node.optimize()` presents an issue for look-up operations like `TreeWalker.in_boolean_context()` and `TreeWalker.parent()`.

This is an incremental fix such that `AST_Node.optimize()` would now contain the correct stack information when called correctly.
2017-03-15 18:44:13 +08:00
Alex Lam S.L
381bd3836e minor clean-ups (#1600)
- remove obsolete optimisation in `AST_Binary` after #1477
- improve `TreeWalker.has_directive()` readability and resilience against multiple visits
2017-03-14 13:19:05 +08:00
Alex Lam S.L
919d5e3482 v2.8.12 2017-03-11 05:00:55 +08:00
Alex Lam S.L
e3a3db73ae temporary fix for boolean bug (#1597)
fixes #1592
2017-03-11 04:59:55 +08:00
Alex Lam S.L
d9344f30b8 disallow parameter substitution for named IIFEs (#1596)
Self-referenced function has non-fixed values assigned to its parameters.

Let `unused` & `!keep_fnames` do the scanning, then apply `reduce_vars` only to unnamed functions.

fixes #1595
2017-03-11 03:34:55 +08:00
Alex Lam S.L
be80f7e706 support multi-line string in tests (#1590)
`expect_exact` sometimes have multiple lines and `\n` are hard to read.

Use array of strings to emulate line breaks and improve readability.
2017-03-10 11:27:30 +08:00
Alex Lam S.L
cf45e2f79b fixup for #1585 (#1589)
As patched on `harmony`, `statement()` is the only user of `embed_tokens()` with a missing error branch.

Updated test case and match up with `harmony` to facilitate future merging.
2017-03-10 10:49:41 +08:00
Alex Lam S.L
8354758f30 v2.8.11 2017-03-10 04:17:21 +08:00
Alex Lam S.L
9e6b128374 fix catch variable reference in IE8 (#1587)
`AST_Scope.def_variable()` will overwrite `AST_Symbol.thedef`, so save a copy before calling.

fixes #1586
2017-03-10 03:15:21 +08:00
Michael Mior
93cdb194f4 Correctly raise a parse exception with a missing loop body (#1585) 2017-03-10 03:08:43 +08:00
Alex Lam S.L
b633706ce4 fix & improve function argument compression (#1584)
- one-use function call => IIFE should take `eval()` & `arguments` into account
- if unused parameter cannot be eliminated, replace it with `0`

fixes #1583
2017-03-09 19:11:05 +08:00
Alex Lam S.L
e9920f7ca1 v2.8.10 2017-03-09 05:48:06 +08:00
Alex Lam S.L
7e465d4a01 scan RHS of dropped assignments (#1581)
- similar case as #1578 but against #1450 instead
- fix `this` binding in #1450 as well

closes #1580
2017-03-09 05:22:27 +08:00
Alex Lam S.L
aa80ee349d remove checkboxes from Issues template 2017-03-08 19:19:54 +08:00
Alex Lam S.L
80e81765cf explain how to make a proper bug report (#1579)
fixes #1574
2017-03-08 18:56:01 +08:00
Alex Lam S.L
711f88dcb4 scan assignment value in drop_unused() (#1578)
those were not optimised for `unused` before, which made it necessary for `reduce_vars` to have separate steps for `keep_fnames`

docs update by @kzc

closes #1577
2017-03-08 18:37:32 +08:00
Alex Lam S.L
344d11d591 v2.8.9 2017-03-08 12:41:22 +08:00
Alex Lam S.L
c7cdcf06a6 fix function name eliminiation (#1576)
Function expression can be assigned to a variable and be given a name. Ensure function name is the reduced variable before clearing it out.

fixes #1573
fixes #1575
2017-03-08 12:39:57 +08:00
Alex Lam S.L
3ee55748d4 only run benchmark & jetstream on CI (#1571) 2017-03-08 06:00:51 +08:00
Alex Lam S.L
dedbeeff15 plan B for IE8 do-while semi-colon fix (#1572)
- omitting trailing semi-colon in do-while breaks non-browser parser, e.g. uglify-js 1.x
- trailing semi-colon only breaks IE8 if followed by `else` or `while`
- always use braces in do-while body to workaround 2nd case with no size loss in compression

fixes #1568
2017-03-08 05:07:05 +08:00
Alex Lam S.L
bd6dee52ab fix return from recursive IIFE (#1570)
`side-effects` did not account for IIFEs being able to reference itself thus making its return value potentially significant
2017-03-08 03:31:51 +08:00
Alex Lam S.L
144052ca49 v2.8.8 2017-03-07 19:58:41 +08:00
Alex Lam S.L
65c848cc6f include benchmark.js in test suite (#1564)
- report file sizes and overall run time
- exit with non-zero code upon error
2017-03-07 19:25:12 +08:00
Alex Lam S.L
8a8a94a596 fix deep cloning of labels (#1565)
`AST_Label.references` get `.initialize()` to `[]` every time after `.clone()`

So walk down the tree to pick up the cloned `AST_LoopControl` pieces and put it back together.
2017-03-07 18:38:27 +08:00
Alex Lam S.L
8153b7bd8a transform function calls to IIFEs (#1560)
- expose function body to call sites for potential optimisations
- suppress substitution of variable used within `AST_Defun`
2017-03-07 15:37:52 +08:00
Alex Lam S.L
d787d70127 avoid substitution of global variables (#1557)
- unless `toplevel` is enabled
- global `const` works as before
2017-03-07 03:11:03 +08:00
kzc
3ac2421932 collapse_vars: do not replace a constant in loop condition or init (#1562) 2017-03-07 01:42:33 +08:00
Alex Lam S.L
a9fc9ddc33 suppress semicolons after do/while (#1556)
- unless both `beautify` & `screw-ie8` are enabled
- deprecate workaround for if-do-while-else

fixes #186
2017-03-06 17:31:35 +08:00
Alex Lam S.L
a5d62a3fc6 v2.8.7 2017-03-05 17:17:08 +08:00
Alex Lam S.L
067e5a5762 fixup for #1553 (#1555)
- `++a` is the one that is foldable
- transform `a++` into `++a` for better optimisation
2017-03-05 17:15:37 +08:00
Alex Lam S.L
33b5f31984 v2.8.6 2017-03-05 15:48:28 +08:00
Alex Lam S.L
35a849dc48 collapse assignment with adjacent subsequent usage (#1553)
- consolidate `cascade` optimisations
- support ++/-- postfixes
- remove redundant optimisation identified in #1460

fixes #368
2017-03-05 14:56:14 +08:00
Alex Lam S.L
b70591be1a handle variable declaration within catch blocks (#1546)
accounts for IE8- scoping
2017-03-05 13:13:44 +08:00
Alex Lam S.L
b33e7f88e6 improve unsafe on undefined (#1548)
`unsafe` turns undefined keyword into a variable of the same name if found, but that interferes with other related optimisations.

Keep track of such transformations to ensure zero information loss in the process.
2017-03-05 13:09:27 +08:00
Alex Lam S.L
1f0333e9f1 stay safe with constants in IE8- (#1547)
- `undefined` etc. can be redefined at top-level for IE8-, so disable related optimisations
- fixed `--support-ie8` catch mangle bug
2017-03-05 12:51:11 +08:00
Alex Lam S.L
eb98a7f2f3 fix handling of shebang and preamble (#1545)
fixes #1332
2017-03-05 12:16:02 +08:00
Alex Lam S.L
78d1bb92d4 fix a corner case in #1530 (#1552) 2017-03-05 12:12:59 +08:00
Alex Lam S.L
ea9ab9fb0e resolve issue with outdated version of async (#1549)
fixes #746
2017-03-05 01:54:20 +08:00
kzc
ce54c9ccee disallow collapse_vars constant replacement in for-in statements (#1543) 2017-03-04 02:39:54 +08:00
Alex Lam S.L
07accd2fbb process code with implicit return statement (#1522)
Bookmarklet for instance implicitedly assumes a "completion value" without using `return`.
The `expression` option now supports such use cases.
Optimisations on IIFEs also enhanced.

fixes #354
fixes #543
fixes #625
fixes #628
fixes #640
closes #1293
2017-03-03 18:13:07 +08:00
Alex Lam S.L
18059cc94f compress numerical expressions (#1513)
safe operations
- `a === b` => `a == b`
- `a + -b`  => `a - b`
- `-a + b`  => `b - a`
- `a+ +b`   => `+b+a`

associative operations
(bit-wise operations are safe, otherwise `unsafe_math`)
- `a + (b + c)`       => `(a + b) + c`
- `(n + 2) + 3`       => `5 + n`
- `(2 * n) * 3`       => `6 * n`
- `(a | 1) | (2 | d)` => `(3 | a) | b`

fixes #412
2017-03-03 18:04:32 +08:00
Alex Lam S.L
b5e0e8c203 facilitate fix for #1531 (#1542) 2017-03-03 07:12:24 +08:00
Alex Lam S.L
e5cb9275df v2.8.5 2017-03-03 05:14:21 +08:00
Alex Lam S.L
17b81350d4 fix chained assignment with unused (#1540)
When #1450 optimises `a=b=42`, it stops after the first variable even if both are unused.

fixes #1539
2017-03-03 04:45:20 +08:00
kzc
4d63d4f5b3 collapse_vars should not replace constant in for-in init section (#1538)
fixes #1537
2017-03-03 03:51:15 +08:00
Alex Lam S.L
70d72ad806 properly cover all cases of for-in loop variables (#1536) 2017-03-03 02:39:57 +08:00
Alex Lam S.L
fe9227a41b fix reference marking in for-in loops (#1535)
fixes #1533
2017-03-03 00:56:06 +08:00
Alex Lam S.L
b49e142a26 disable do{...}while(false) optimisation (#1534)
- fails to handle `break` in body

fixes #1532
2017-03-03 00:54:41 +08:00
kzc
ee3b39b909 optimize trivial IIFEs returning constants (#1530) 2017-03-02 15:11:40 +08:00
Alex Lam S.L
9699ffb1af trim unused invocation parameters (#1526) 2017-03-02 11:33:59 +08:00
Alex Lam S.L
fdc9b9413b minor improvement to string optimisation (#1514)
- "" + "a"     => "a"
- "" + a + "b" => a + "b"
- "a" + ""     => "a" (improving on #45)
2017-03-02 11:31:39 +08:00
Alex Lam S.L
40ceddb48a v2.8.4 2017-03-02 00:24:49 +08:00
Alex Lam S.L
7aa69117e1 fix corner cases in reduce_vars (#1524)
Avoid variable substitution in the following cases:
- use of variable before declaration
- declaration within conditional code blocks
- declaration within loop body

fixes #1518
fixes #1525
2017-03-02 00:20:53 +08:00
Alex Lam S.L
bff7ad67bb v2.8.3 2017-03-01 15:28:46 +08:00
Alex Lam S.L
c2334baa48 fix crash on missing props to string_template() (#1523)
Patched up `make_node()` without `orig`.

There may be other cases where `start` could be missing, so make it print "undefined" instead of crashing.

fixes #1518
2017-03-01 15:25:26 +08:00
Alex Lam S.L
fb2b6c7c6f v2.8.2 2017-03-01 04:46:12 +08:00
Alex Lam S.L
f5cbe19b75 invert reduce_vars tracking flag (#1519)
Modules like webpack and grunt-contrib-uglify still uses `ast.transform(compressor)` before `Compressor.compress(ast)` was introduced.

Workaround this compatibility issue by deactivating `reduce_vars` in such case.

Also fix use case with omitted `options` when calling `Compressor()`.

fixes #1516
2017-03-01 04:12:10 +08:00
Alex Lam S.L
b34fa11a13 fix evaluate on object getter & setter (#1515) 2017-03-01 02:03:47 +08:00
Alex Lam S.L
320984c5f5 v2.8.1 2017-03-01 00:27:08 +08:00
Alex Lam S.L
4365a51237 temporarily disables reduce_vars (#1517)
... as we investigate #1516
2017-03-01 00:25:43 +08:00
Alex Lam S.L
858e6c78a4 warn & drop #__PURE__ iff IIFE is dropped (#1511)
- consolidate `side-effects` optimisations
- improve string `+` optimisation
- enhance literal & `conditionals` optimisations
2017-02-28 02:25:44 +08:00
Alex Lam S.L
0b0296eb2a v2.8.0 2017-02-27 03:47:54 +08:00
Alex Lam S.L
872270b149 improve error messages (#1506)
- better inheritance of `Error` sub-classes
- mark parse error against source in CLI

closes #235
closes #348
closes #524
closes #1356
closes #1405
2017-02-27 03:40:54 +08:00
kzc
b1c593a041 add harmony branch details in README (#1507) 2017-02-27 01:55:24 +08:00
Alex Lam S.L
13be50a4a9 faster tree transversal (#1462)
- convert `[].forEach()` to for-loops
2017-02-26 05:58:26 +08:00
Alex Lam S.L
16cd5d57a5 consolidate evaluate & reduce_vars (#1505)
- improve marking efficiency
- apply smarter `const` replacement to `var`

fixes #1501
2017-02-26 00:40:33 +08:00
Alex Lam S.L
834f9f3924 update docs for pure_funcs & drop_console (#1503)
closes #1362
closes #1399
2017-02-25 04:13:10 +08:00
Alex Lam S.L
cf0951f726 allow --in-source-map inline (#1490)
- limited to one input file (or `stdin`)
- only works with built-in parser

fixes #520
2017-02-25 04:11:21 +08:00
Ondřej Španěl
852f78491a Avoid using exports when undefined (#1471)
Makes direct usage within web browser easier, even if officially unsupported.
2017-02-24 08:51:24 +08:00
Alex Lam S.L
229e42cdee Merge pull request #1485 from alexlamsl/merge-2.8.0
2.8.0 staging
2017-02-24 07:33:57 +08:00
alexlamsl
4e49302916 enable collapse_vars & reduce_vars by default
- fix corner cases in `const` optimisation
- deprecate `/*@const*/`

fixes #1497
closes #1498
2017-02-24 01:46:57 +08:00
kzc
1e51586996 Support marking a call as pure
A function call or IIFE with an immediately preceding comment
containing `@__PURE__` or `#__PURE__` is deemed to be a
side-effect-free pure function call and can potentially be
dropped.

Depends on `side_effects` option.

`[#@]__PURE__` hint will be removed from comment when pure
call is dropped.

fixes #1261
closes #1448
2017-02-21 14:24:18 +08:00
Ondřej Španěl
d48a3080ac Fix: AST_Accessor missing start / end tokens
fixes #1492
closes #1493
2017-02-21 13:32:16 +08:00
alexlamsl
26fbeece1c fix pure_funcs & improve side_effects
- only drops side-effect-free arguments
- drop side-effect-free parts with discarded value from `AST_Seq` & `AST_SimpleStatement`

closes #1494
2017-02-21 13:31:59 +08:00
alexlamsl
8898b8a0fe clean up max_line_len
- never exceed specified limit
- otherwise warning is shown
- enabled only for final output

closes #1496
2017-02-21 13:29:58 +08:00
alexlamsl
ec64acd2c8 introduce unsafe_proto
- `Array.prototype.slice` => `[].slice`

closes #1491
2017-02-21 13:29:58 +08:00
alexlamsl
ac0b61ed6e remove extraneous spaces between ++/+/--/-
fixes #1377
closes #1488
2017-02-21 13:29:58 +08:00
Anthony Van de Gejuchte
c06a50f338 Add .gitattributes to checkout lf eol style
closes #1487
2017-02-21 13:29:58 +08:00
alexlamsl
09f9ae2de9 improve --beautify bracketize
reduce whitespaces from if-else statements

fixes #1482
closes #1483
2017-02-21 13:29:58 +08:00
alexlamsl
7e6331bb39 add benchmark & JetStream tests
- `test/benchmark.js` measures performance
- `test/jetstream.js` verifies correctness
- configurable mangle/compress/output options

closes #1479
2017-02-21 13:29:58 +08:00
alexlamsl
e275148998 enhance global_defs
- support arrays, objects & AST_Node
- support `"a.b":1` on both cli & API
- emit warning if variable is modified
- override top-level variables

fixes #1416
closes #1198
closes #1469
2017-02-21 13:29:58 +08:00
alexlamsl
974247c8c0 evaluate AST_SymbolRef as parameter
fix invalid boolean conversion now exposed in `make_node_from_constant()`

closes #1477
2017-02-21 13:29:58 +08:00
alexlamsl
a0f4fd390a improve reduce_vars and fix a bug
- update modified flag between compress() passes
- support IIFE arguments
- fix corner case with multiple definitions

closes #1473
2017-02-21 13:29:58 +08:00
alexlamsl
b8b133d91a improve keep_fargs & keep_fnames
- utilise in_use_ids instead of unreferenced()
- drop_unused now up-to-date for subsequent passes

closes #1476
2017-02-21 13:29:58 +08:00
alexlamsl
c525a2b190 fix duplicated test names
previously test cases with the same name would be skipped except for the last one

`test/run-test.js` will now report duplicated names as errors

closes #1461
2017-02-21 13:29:58 +08:00
kzc
6ffbecb72b smarter const replacement taking name length into account
closes #1459
2017-02-21 13:29:58 +08:00
alexlamsl
f0ff6189be clean up negate_iife
- remove extra tree scanning phase for `negate_iife`
- `negate_iife` now only deals with the narrowest form, i.e. IIFE sitting directly under `AST_SimpleStatement`
- `booleans`, `conditionals` etc. will now take care the rest via more accurate accounting
- `a(); void b();` => `a(); b();`

fixes #1288
closes #1451
2017-02-21 13:29:58 +08:00
alexlamsl
6b3c49e458 improve string concatenation
shuffle associative operations to minimise parentheses and aid other uglification efforts

closes #1454
2017-02-21 13:29:57 +08:00
alexlamsl
f584ca8d07 -c sequences=N suboptimal at N expression cutoff
N = 2:
  a;
  b;
  c;
  d;
was:
  a, b;
  c;
  d;
now:
  a, b;
  c, d;

fixes #1455
closes #1457
2017-02-21 13:29:57 +08:00
alexlamsl
ae4db00991 tweak do-while loops
- `do{...}while(false)` => `{...}`
- clean up `AST_While` logic

closes #1452
2017-02-21 13:29:57 +08:00
alexlamsl
100307ab31 fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"

improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c

closes #1453
2017-02-21 13:29:57 +08:00
alexlamsl
148047fbbf drop unused: toplevel, assign-only
- assign statement does not count towards variable usage by default
- only works with assignments on the same scope level as declaration
- can be disabled with `unused` set to "keep_assign"
- `toplevel` to drop unused top-level variables and/or functions
- `top_retain` to whitelist top-level exceptions

closes #1450
2017-02-21 13:29:57 +08:00
kzc
d11dca3cf9 fix stray else in compress with conditionals=false
closes #1449
2017-02-21 13:29:57 +08:00
alexlamsl
e5badb9541 enable typeof "undefined" for general use
move out of unsafe, guard corner case with screw_id8 instead

closes #1446
2017-02-18 19:01:42 +08:00
alexlamsl
fa668a28b4 fix corner case in keep_fnames
happens when inner function:
- just below top level
- not referenced
- `unused` is disabled

closes #1445
2017-02-18 19:00:54 +08:00
alexlamsl
686a496b1c remove unused AST_Scope.nesting & AST_SymbolRef.frame
they are computed but never used

closes #1444
2017-02-18 18:59:40 +08:00
alexlamsl
11676f9d72 fix crash in unsafe replacement of undefined
remove extraneous call to AST_SymbolRef.reference()

closes #1443
2017-02-18 18:58:23 +08:00
Anthony Van de Gejuchte
dd31d12a91 Improve optimizing function() { if(c){return foo} bar();}
closes #1437
2017-02-18 18:56:18 +08:00
Mihai Bazon
eb55d8a9bb Merge pull request #1481 from anatdagan/propsmangle_only_identifiers
verify that property names after mangle are legal
2017-02-12 10:59:43 +02:00
Anat Dagan
81f1df14d7 in mangle_names there is a check that the variable name is legal and that it is not a reserved word. This should apply to propsmangle as well. 2017-02-10 14:13:47 +02:00
Alex Lam S.L
7f8d72d9d3 update test (#1441)
improved reduce_vars & binary operands produce more optimal results
2017-01-26 12:59:32 +01:00
Alex Lam S.L
1eaa211e09 fix mangling collision with keep_fnames (#1431)
* fix mangling collision with keep_fnames
fixes #1423

* pass mangle options to figure_out_scope()
bring command-line in line with minify()
2017-01-26 12:18:28 +01:00
Alex Lam S.L
0610c020b1 optimise binary operands with evaluate() (#1427)
- remove call to evaluate() in is_constant() and let nested optimize() does its job instead
- reject RegExp in is_constant() and remove special case logic under collapse_vars
- operands to conditionals optimisation are now always evaluate()-ed
- throw error in constant_value() instead of returning undefined to catch possible bugs, similar to make_node_from_constant()
- optimise binary boolean operators under `evaluate` instead of `conditionals`
2017-01-26 12:16:50 +01:00
Alex Lam S.L
0d7d4918eb augment evaluate to extract within objects (#1425)
- gated by `unsafe`
- replaces previous optimisation specific to String.length
- "123"[0] => 1
- [1, 2, 3][0] => 1
- [1, 2, 3].length => 3
- does not apply to objects with overridden prototype functions
2017-01-26 12:14:18 +01:00
alexlamsl
48284844a4 add missing LHS cases which global_defs should avoid 2017-01-19 21:06:28 +01:00
kzc
ec2e5fa3a2 Have minify() and tests use figure_out_scope() as uglifyjs CLI does
Clarify docs, help and tests for --support-ie8 and screw_ie8=false
2017-01-19 17:14:33 +01:00
Anthony Van de Gejuchte
da17766ddd Add preventive test involving non-ascii function identifiers 2017-01-19 17:13:33 +01:00
Wiktor Kwapisiewicz
0913db8c84 Add note about name mangling when using --mangle-props=unquoted (#1314) 2017-01-19 16:47:10 +01:00
kzc
5c7705fcad remove npm-shrinkwrap.json to work around npm@4.0.2 bug (#1384) 2016-11-30 18:09:52 +01:00
101 changed files with 10569 additions and 1572 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.js text eol=lf

9
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,9 @@
- Bug report or feature request? <!-- Note: sub-optimal but correct code is not a bug -->
- `uglify-js` version (`uglifyjs -V`)
- JavaScript input - ideally as small as possible.
- The `uglifyjs` CLI command executed or `minify()` options used.
- An example of JavaScript output produced and/or the error or warning.
<!--
Note: the release version of uglify-js only supports ES5. Those wishing
to minify ES6 should use the experimental harmony branch.
-->

View File

@@ -1,10 +1,12 @@
language: node_js language: node_js
before_install: "npm install -g npm" before_install: "npm install -g npm"
node_js: node_js:
- "0.12"
- "0.10" - "0.10"
- "0.12"
- "4" - "4"
- "6" - "6"
env:
- UGLIFYJS_TEST_ALL=1
matrix: matrix:
fast_finish: true fast_finish: true
sudo: false sudo: false

105
README.md
View File

@@ -10,6 +10,11 @@ There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox, [in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari). Chrome and probably Safari).
#### Note:
- release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
Install Install
------- -------
@@ -65,12 +70,14 @@ The available options are:
--source-map-inline Write base64-encoded source map to the end of js output. --source-map-inline Write base64-encoded source map to the end of js output.
--in-source-map Input source map, useful if you're compressing --in-source-map Input source map, useful if you're compressing
JS that was generated from some other original JS that was generated from some other original
code. code. Specify "inline" if the source map is included
inline with the sources.
--screw-ie8 Use this flag if you don't wish to support --screw-ie8 Use this flag if you don't wish to support
Internet Explorer 6-8 quirks. Internet Explorer 6/7/8.
By default UglifyJS will not try to be IE-proof. By default UglifyJS will not try to be IE-proof.
--support-ie8 Use this flag to support Internet Explorer 6-8 quirks. --support-ie8 Use this flag to support Internet Explorer 6/7/8.
Note: may break standards compliant `catch` identifiers. Equivalent to setting `screw_ie8: false` in `minify()`
for `compress`, `mangle` and `output` options.
--expr Parse a single expression, rather than a --expr Parse a single expression, rather than a
program (for parsing JSON) program (for parsing JSON)
-p, --prefix Skip prefix for original filenames that appear -p, --prefix Skip prefix for original filenames that appear
@@ -84,10 +91,9 @@ The available options are:
-b, --beautify Beautify output/specify output options. -b, --beautify Beautify output/specify output options.
-m, --mangle Mangle names/pass mangler options. -m, --mangle Mangle names/pass mangler options.
-r, --reserved Reserved names to exclude from mangling. -r, --reserved Reserved names to exclude from mangling.
-c, --compress Enable compressor/pass compressor options. Pass -c, --compress Enable compressor/pass compressor options, e.g.
options like -c `-c 'if_return=false,pure_funcs=["Math.pow","console.log"]'`
hoist_vars=false,if_return=false. Use -c with Use `-c` with no argument to enable default compression
no argument to use the default compression
options. options.
-d, --define Global definitions -d, --define Global definitions
-e, --enclose Embed everything in a big function, with a -e, --enclose Embed everything in a big function, with a
@@ -148,8 +154,10 @@ The available options are:
them explicitly on the command line. them explicitly on the command line.
--mangle-regex Only mangle property names matching the regex --mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings --name-cache File to hold mangled names mappings
--pure-funcs List of functions that can be safely removed if --pure-funcs Functions that can be safely removed if their
their return value is not used [array] return value is not used, e.g.
`--pure-funcs Math.floor console.info`
(requires `--compress`)
``` ```
Specify `--output` (`-o`) to declare the output file. Otherwise the output Specify `--output` (`-o`) to declare the output file. Otherwise the output
@@ -195,9 +203,10 @@ compressed JS by mapping every token in the compiled JS to its original
location. location.
To use this feature you need to pass `--in-source-map To use this feature you need to pass `--in-source-map
/path/to/input/source.map`. Normally the input source map should also point /path/to/input/source.map` or `--in-source-map inline` if the source map is
to the file containing the generated JS, so if that's correct you can omit included inline with the sources. Normally the input source map should also
input files from the command line. point to the file containing the generated JS, so if that's correct you can
omit input files from the command line.
## Mangler options ## Mangler options
@@ -285,6 +294,17 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS. single call to UglifyJS.
#### Mangling unquoted names (`--mangle-props=unquoted` or `--mangle-props=2`)
Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example:
```
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props=2 -mc
var o={"foo":1,a:3};o.foo+=o.a,console.log(o.foo);
```
#### Debugging property name mangling #### Debugging property name mangling
You can also pass `--mangle-props-debug` in order to mangle property names You can also pass `--mangle-props-debug` in order to mangle property names
@@ -332,6 +352,12 @@ to set `true`; it's effectively a shortcut for `foo=true`).
comparison are switching. Compression only works if both `comparisons` and comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true. `unsafe_comps` are both set to true.
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `conditionals` -- apply optimizations for `if`-s and conditional - `conditionals` -- apply optimizations for `if`-s and conditional
expressions expressions
@@ -347,7 +373,15 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `loops` -- optimizations for `do`, `while` and `for` loops when we can - `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition statically determine the condition
- `unused` -- drop unreferenced functions and variables - `unused` -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `hoist_funs` -- hoist function declarations - `hoist_funs` -- hoist function declarations
@@ -361,11 +395,11 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `cascade` -- small optimization for sequences, transform `x, x` into `x` - `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()` and `x = something(), x` into `x = something()`
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const` - `collapse_vars` -- Collapse single-use `var` and `const` definitions
definitions when possible. when possible.
- `reduce_vars` -- default `false`. Improve optimization on variables assigned - `reduce_vars` -- Improve optimization on variables assigned with and
with and used as constant values. used as constant values.
- `warnings` -- display warnings when dropping unreachable code or unused - `warnings` -- display warnings when dropping unreachable code or unused
declarations etc. declarations etc.
@@ -390,7 +424,12 @@ to set `true`; it's effectively a shortcut for `foo=true`).
overhead (compression will be slower). overhead (compression will be slower).
- `drop_console` -- default `false`. Pass `true` to discard calls to - `drop_console` -- default `false`. Pass `true` to discard calls to
`console.*` functions. `console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `expression` -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `keep_fargs` -- default `true`. Prevents the - `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this compressor from discarding unused function arguments. You need this
@@ -432,6 +471,8 @@ if (DEBUG) {
} }
``` ```
You can specify nested constants in the form of `--define env.DEBUG=false`.
UglifyJS will warn about the condition being always false and about dropping UglifyJS will warn about the condition being always false and about dropping
unreachable code; for now there is no option to turn off only this specific unreachable code; for now there is no option to turn off only this specific
warning, you can pass `warnings=false` to turn off *all* warnings. warning, you can pass `warnings=false` to turn off *all* warnings.
@@ -442,8 +483,6 @@ separate file and include it into the build. For example you can have a
```javascript ```javascript
const DEBUG = false; const DEBUG = false;
const PRODUCTION = true; const PRODUCTION = true;
// Alternative for environments that don't support `const`
/** @const */ var STAGING = false;
// etc. // etc.
``` ```
@@ -454,7 +493,8 @@ and build your code like this:
UglifyJS will notice the constants and, since they cannot be altered, it UglifyJS will notice the constants and, since they cannot be altered, it
will evaluate references to them to the value itself and drop unreachable will evaluate references to them to the value itself and drop unreachable
code as usual. The build will contain the `const` declarations if you use code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments, use `/** @const */ var`. them. If you are targeting < ES6 environments which does not support `const`,
using `var` with `reduce_vars` (enabled by default) should suffice.
<a name="codegen-options"></a> <a name="codegen-options"></a>
@@ -589,7 +629,7 @@ function uglify(ast, options, mangle) {
// Compression // Compression
uAST.figure_out_scope(); uAST.figure_out_scope();
uAST = uAST.transform(UglifyJS.Compressor(options)); uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional) // Mangling (optional)
if (mangle) { if (mangle) {
@@ -833,7 +873,7 @@ toplevel.figure_out_scope()
Like this: Like this:
```javascript ```javascript
var compressor = UglifyJS.Compressor(options); var compressor = UglifyJS.Compressor(options);
var compressed_ast = toplevel.transform(compressor); var compressed_ast = compressor.compress(toplevel);
``` ```
The `options` can be missing. Available options are discussed above in The `options` can be missing. Available options are discussed above in
@@ -937,3 +977,20 @@ The `source_map_options` (optional) can contain the following properties:
[codegen]: http://lisperator.net/uglifyjs/codegen [codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress [compressor]: http://lisperator.net/uglifyjs/compress
[parser]: http://lisperator.net/uglifyjs/parser [parser]: http://lisperator.net/uglifyjs/parser
#### Harmony
If you wish to use the experimental [harmony](https://github.com/mishoo/UglifyJS2/commits/harmony)
branch to minify ES2015+ (ES6+) code please use the following in your `package.json` file:
```
"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony"
```
or to directly install the experimental harmony version of uglify:
```
npm install --save-dev uglify-js@github:mishoo/UglifyJS2#harmony
```
See [#448](https://github.com/mishoo/UglifyJS2/issues/448) for additional details.

View File

@@ -8,7 +8,6 @@ var sys = require("util");
var yargs = require("yargs"); var yargs = require("yargs");
var fs = require("fs"); var fs = require("fs");
var path = require("path"); var path = require("path");
var async = require("async");
var acorn; var acorn;
var screw_ie8 = true; var screw_ie8 = true;
var ARGS = yargs var ARGS = yargs
@@ -26,8 +25,8 @@ mangling you need to use `-c` and `-m`.\
.describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default") .describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default")
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.") .describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.") .describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("screw-ie8", "Do not support Internet Explorer 6-8 quirks. This flag is enabled by default.") .describe("screw-ie8", "Do not support Internet Explorer 6/7/8. This flag is enabled by default.")
.describe("support-ie8", "Support non-standard Internet Explorer 6-8 javascript. Note: may break standards compliant `catch` identifiers.") .describe("support-ie8", "Support non-standard Internet Explorer 6/7/8 javascript.")
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)") .describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
.describe("p", "Skip prefix for original filenames that appear in source maps. \ .describe("p", "Skip prefix for original filenames that appear in source maps. \
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \ For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
@@ -228,9 +227,10 @@ if (ARGS.mangle_props === true) {
} }
var OUTPUT_OPTIONS = { var OUTPUT_OPTIONS = {
beautify : BEAUTIFY ? true : false, beautify : BEAUTIFY ? true : false,
preamble : ARGS.preamble || null, max_line_len : 32000,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0 preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0,
}; };
if (ARGS.mangle_props == 2) { if (ARGS.mangle_props == 2) {
@@ -281,21 +281,29 @@ if (ARGS.self) {
var ORIG_MAP = ARGS.in_source_map; var ORIG_MAP = ARGS.in_source_map;
if (ORIG_MAP) { if (ORIG_MAP && ORIG_MAP != "inline") {
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP)); ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (files.length == 0) { if (files.length == 0) {
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file); print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
files = [ ORIG_MAP.file ]; files = [ ORIG_MAP.file ];
} }
if (ARGS.source_map_root == null) {
ARGS.source_map_root = ORIG_MAP.sourceRoot;
}
} }
if (files.length == 0) { if (files.length == 0) {
files = [ "-" ]; files = [ "-" ];
} }
if (ORIG_MAP == "inline") {
if (files.length > 1) {
print_error("ERROR: Inline source map only works with singular input");
process.exit(1);
}
if (ARGS.acorn || ARGS.spidermonkey) {
print_error("ERROR: Inline source map only works with built-in parser");
process.exit(1);
}
}
if (files.indexOf("-") >= 0 && ARGS.source_map) { if (files.indexOf("-") >= 0 && ARGS.source_map) {
print_error("ERROR: Source map doesn't work with input from STDIN"); print_error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1); process.exit(1);
@@ -307,37 +315,22 @@ if (files.filter(function(el){ return el == "-" }).length > 1) {
} }
var STATS = {}; var STATS = {};
var OUTPUT_FILE = ARGS.o;
var TOPLEVEL = null; var TOPLEVEL = null;
var P_RELATIVE = ARGS.p && ARGS.p == "relative"; var P_RELATIVE = ARGS.p && ARGS.p == "relative";
var SOURCES_CONTENT = {}; var SOURCES_CONTENT = {};
var index = 0;
var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({ !function cb() {
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE, if (index == files.length) return done();
root: ARGS.source_map_root, var file = files[index++];
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.msg);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
async.eachLimit(files, 1, function (file, cb) {
read_whole_file(file, function (err, code) { read_whole_file(file, function (err, code) {
if (err) { if (err) {
print_error("ERROR: can't read file: " + file); print_error("ERROR: can't read file: " + file);
process.exit(1); process.exit(1);
} }
if (ORIG_MAP == "inline") {
ORIG_MAP = read_source_map(code);
}
if (ARGS.p != null) { if (ARGS.p != null) {
if (P_RELATIVE) { if (P_RELATIVE) {
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/'); file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
@@ -373,7 +366,21 @@ async.eachLimit(files, 1, function (file, cb) {
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) { if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col); print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
print_error(ex.message); var col = ex.col;
var line = code.split(/\r?\n/)[ex.line - (col ? 1 : 2)];
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
if (col) {
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
} else {
print_error(line.slice(-40));
print_error(line.slice(-40).replace(/\S/g, " ") + "^");
}
}
print_error(ex.stack); print_error(ex.stack);
process.exit(1); process.exit(1);
} }
@@ -383,7 +390,31 @@ async.eachLimit(files, 1, function (file, cb) {
}); });
cb(); cb();
}); });
}, function () { }();
function done() {
var OUTPUT_FILE = ARGS.o;
var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
root: ARGS.source_map_root || ORIG_MAP && ORIG_MAP.sourceRoot,
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.message);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){ if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL); TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
}); });
@@ -428,10 +459,11 @@ async.eachLimit(files, 1, function (file, cb) {
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars"); var TL_CACHE = readNameCache("vars");
if (MANGLE) MANGLE.cache = TL_CACHE;
if (SCOPE_IS_NEEDED) { if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
TOPLEVEL.figure_out_scope({ screw_ie8: screw_ie8, cache: TL_CACHE }); TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (ARGS.lint) { if (ARGS.lint) {
TOPLEVEL.scope_warnings(); TOPLEVEL.scope_warnings();
} }
@@ -446,7 +478,7 @@ async.eachLimit(files, 1, function (file, cb) {
if (SCOPE_IS_NEEDED) { if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
TOPLEVEL.figure_out_scope({ screw_ie8: screw_ie8, cache: TL_CACHE }); TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (MANGLE && !TL_CACHE) { if (MANGLE && !TL_CACHE) {
TOPLEVEL.compute_char_frequency(MANGLE); TOPLEVEL.compute_char_frequency(MANGLE);
} }
@@ -454,7 +486,6 @@ async.eachLimit(files, 1, function (file, cb) {
} }
if (MANGLE) time_it("mangle", function(){ if (MANGLE) time_it("mangle", function(){
MANGLE.cache = TL_CACHE;
TOPLEVEL.mangle_names(MANGLE); TOPLEVEL.mangle_names(MANGLE);
}); });
@@ -510,7 +541,7 @@ async.eachLimit(files, 1, function (file, cb) {
})); }));
} }
} }
}); }
/* -----[ functions ]----- */ /* -----[ functions ]----- */
@@ -530,7 +561,7 @@ function getOptions(flag, constants) {
var ast; var ast;
try { try {
ast = UglifyJS.parse(x, { expression: true }); ast = UglifyJS.parse(x, { cli: true, expression: true });
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) { if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments for flag `" + flag + "': " + x); print_error("Error parsing arguments for flag `" + flag + "': " + x);
@@ -540,7 +571,7 @@ function getOptions(flag, constants) {
ast.walk(new UglifyJS.TreeWalker(function(node){ ast.walk(new UglifyJS.TreeWalker(function(node){
if (node instanceof UglifyJS.AST_Seq) return; // descend if (node instanceof UglifyJS.AST_Seq) return; // descend
if (node instanceof UglifyJS.AST_Assign) { if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_"); var name = node.left.print_to_string().replace(/-/g, "_");
var value = node.right; var value = node.right;
if (constants) if (constants)
value = new Function("return (" + value.print_to_string() + ")")(); value = new Function("return (" + value.print_to_string() + ")")();
@@ -548,7 +579,7 @@ function getOptions(flag, constants) {
return true; // no descend return true; // no descend
} }
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) { if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
var name = node.print_to_string({ beautify: false }).replace(/-/g, "_"); var name = node.print_to_string().replace(/-/g, "_");
ret[name] = true; ret[name] = true;
return true; // no descend return true; // no descend
} }
@@ -575,6 +606,15 @@ function read_whole_file(filename, cb) {
} }
} }
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
print_error("WARN: inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
function time_it(name, cont) { function time_it(name, cont) {
var t1 = new Date().getTime(); var t1 = new Date().getTime();
var ret = cont(); var ret = cont();

View File

@@ -81,7 +81,9 @@ function DEFNODE(type, props, methods, base) {
ctor.DEFMETHOD = function(name, method) { ctor.DEFMETHOD = function(name, method) {
this.prototype[name] = method; this.prototype[name] = method;
}; };
exports["AST_" + type] = ctor; if (typeof exports !== "undefined") {
exports["AST_" + type] = ctor;
}
return ctor; return ctor;
}; };
@@ -89,9 +91,20 @@ var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos
}, null); }, null);
var AST_Node = DEFNODE("Node", "start end", { var AST_Node = DEFNODE("Node", "start end", {
clone: function() { _clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
return new this.CTOR(this); return new this.CTOR(this);
}, },
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes", $documentation: "Base class of all AST nodes",
$propdoc: { $propdoc: {
start: "[AST_Token] The first token of this node", start: "[AST_Token] The first token of this node",
@@ -143,12 +156,13 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
}, AST_Statement); }, AST_Statement);
function walk_body(node, visitor) { function walk_body(node, visitor) {
if (node.body instanceof AST_Statement) { var body = node.body;
node.body._walk(visitor); if (body instanceof AST_Statement) {
body._walk(visitor);
}
else for (var i = 0, len = body.length; i < len; i++) {
body[i]._walk(visitor);
} }
else node.body.forEach(function(stat){
stat._walk(visitor);
});
}; };
var AST_Block = DEFNODE("Block", "body", { var AST_Block = DEFNODE("Block", "body", {
@@ -196,6 +210,20 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
this.label._walk(visitor); this.label._walk(visitor);
this.body._walk(visitor); this.body._walk(visitor);
}); });
},
clone: function(deep) {
var node = this._clone(deep);
if (deep) {
var refs = node.label.references;
var label = this.label;
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_LoopControl
&& node.label && node.label.thedef === label) {
refs.push(node);
}
}));
}
return node;
} }
}, AST_StatementWithBody); }, AST_StatementWithBody);
@@ -369,9 +397,10 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
if (this.name) this.name._walk(visitor); if (this.name) this.name._walk(visitor);
this.argnames.forEach(function(arg){ var argnames = this.argnames;
arg._walk(visitor); for (var i = 0, len = argnames.length; i < len; i++) {
}); argnames[i]._walk(visitor);
}
walk_body(this, visitor); walk_body(this, visitor);
}); });
} }
@@ -531,9 +560,10 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.definitions.forEach(function(def){ var definitions = this.definitions;
def._walk(visitor); for (var i = 0, len = definitions.length; i < len; i++) {
}); definitions[i]._walk(visitor);
}
}); });
} }
}, AST_Statement); }, AST_Statement);
@@ -571,9 +601,10 @@ var AST_Call = DEFNODE("Call", "expression args", {
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.expression._walk(visitor); this.expression._walk(visitor);
this.args.forEach(function(arg){ var args = this.args;
arg._walk(visitor); for (var i = 0, len = args.length; i < len; i++) {
}); args[i]._walk(visitor);
}
}); });
} }
}); });
@@ -740,9 +771,10 @@ var AST_Array = DEFNODE("Array", "elements", {
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.elements.forEach(function(el){ var elements = this.elements;
el._walk(visitor); for (var i = 0, len = elements.length; i < len; i++) {
}); elements[i]._walk(visitor);
}
}); });
} }
}); });
@@ -754,9 +786,10 @@ var AST_Object = DEFNODE("Object", "properties", {
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.properties.forEach(function(prop){ var properties = this.properties;
prop._walk(visitor); for (var i = 0, len = properties.length; i < len; i++) {
}); properties[i]._walk(visitor);
}
}); });
} }
}); });
@@ -804,9 +837,6 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
$propdoc: {
init: "[AST_Node*/S] array of initializers for this declaration."
}
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolVar = DEFNODE("SymbolVar", null, { var AST_SymbolVar = DEFNODE("SymbolVar", null, {
@@ -954,8 +984,8 @@ TreeWalker.prototype = {
push: function (node) { push: function (node) {
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) { } else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = this.directives[node.value] ? "up" : true; this.directives[node.value] = node;
} }
this.stack.push(node); this.stack.push(node);
}, },
@@ -983,7 +1013,7 @@ TreeWalker.prototype = {
for (var i = 0; i < node.body.length; ++i) { for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i]; var st = node.body[i];
if (!(st instanceof AST_Directive)) break; if (!(st instanceof AST_Directive)) break;
if (st.value == type) return true; if (st.value == type) return st;
} }
} }
}, },

File diff suppressed because it is too large Load Diff

View File

@@ -46,17 +46,8 @@
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/; var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
function is_some_comments(comment) { function is_some_comments(comment) {
var text = comment.value; // multiline comment
var type = comment.type; return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
return type == "comment5";
}
function is_comment5(comment) {
return comment.type == "comment5";
} }
function OutputStream(options) { function OutputStream(options) {
@@ -70,7 +61,7 @@ function OutputStream(options) {
unescape_regexps : false, unescape_regexps : false,
inline_script : false, inline_script : false,
width : 80, width : 80,
max_line_len : 32000, max_line_len : false,
beautify : false, beautify : false,
source_map : null, source_map : null,
bracketize : false, bracketize : false,
@@ -86,7 +77,7 @@ function OutputStream(options) {
}, true); }, true);
// Convert comment option to RegExp if neccessary and set up comments filter // Convert comment option to RegExp if neccessary and set up comments filter
var comment_filter = options.shebang ? is_comment5 : return_false; // Default case, throw all comments away except shebangs var comment_filter = return_false; // Default case, throw all comments away
if (options.comments) { if (options.comments) {
var comments = options.comments; var comments = options.comments;
if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) {
@@ -98,12 +89,12 @@ function OutputStream(options) {
} }
if (comments instanceof RegExp) { if (comments instanceof RegExp) {
comment_filter = function(comment) { comment_filter = function(comment) {
return comment.type == "comment5" || comments.test(comment.value); return comment.type != "comment5" && comments.test(comment.value);
}; };
} }
else if (typeof comments === "function") { else if (typeof comments === "function") {
comment_filter = function(comment) { comment_filter = function(comment) {
return comment.type == "comment5" || comments(this, comment); return comment.type != "comment5" && comments(this, comment);
}; };
} }
else if (comments === "some") { else if (comments === "some") {
@@ -198,16 +189,29 @@ function OutputStream(options) {
var might_need_space = false; var might_need_space = false;
var might_need_semicolon = false; var might_need_semicolon = false;
var might_add_newline = 0;
var last = null; var last = null;
function last_char() { function last_char() {
return last.charAt(last.length - 1); return last.charAt(last.length - 1);
}; };
function maybe_newline() { var ensure_line_len = options.max_line_len ? function() {
if (options.max_line_len && current_col > options.max_line_len) if (current_col > options.max_line_len) {
print("\n"); if (might_add_newline) {
}; var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline);
OUTPUT = left + "\n" + right;
current_line++;
current_pos++;
current_col = right.length;
}
if (current_col > options.max_line_len) {
AST_Node.warn("Output exceeds {max_line_len} characters", options);
}
}
might_add_newline = 0;
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , ."); var requireSemicolonChars = makePredicate("( [ + * / - , .");
@@ -223,6 +227,7 @@ function OutputStream(options) {
current_col++; current_col++;
current_pos++; current_pos++;
} else { } else {
ensure_line_len();
OUTPUT += "\n"; OUTPUT += "\n";
current_pos++; current_pos++;
current_line++; current_line++;
@@ -243,6 +248,7 @@ function OutputStream(options) {
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
var target_line = stack[stack.length - 1].start.line; var target_line = stack[stack.length - 1].start.line;
while (current_line < target_line) { while (current_line < target_line) {
ensure_line_len();
OUTPUT += "\n"; OUTPUT += "\n";
current_pos++; current_pos++;
current_line++; current_line++;
@@ -254,8 +260,9 @@ function OutputStream(options) {
if (might_need_space) { if (might_need_space) {
var prev = last_char(); var prev = last_char();
if ((is_identifier_char(prev) if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\\")) && (is_identifier_char(ch) || ch == "\\"))
|| (/^[\+\-\/]$/.test(ch) && ch == prev)) || (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{ {
OUTPUT += " "; OUTPUT += " ";
current_col++; current_col++;
@@ -263,16 +270,16 @@ function OutputStream(options) {
} }
might_need_space = false; might_need_space = false;
} }
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1; var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n; current_line += n;
if (n == 0) { current_col += a[0].length;
current_col += a[n].length; if (n > 0) {
} else { ensure_line_len();
current_col = a[n].length; current_col = a[n].length;
} }
current_pos += str.length;
last = str; last = str;
OUTPUT += str;
}; };
var space = options.beautify ? function() { var space = options.beautify ? function() {
@@ -298,7 +305,10 @@ function OutputStream(options) {
var newline = options.beautify ? function() { var newline = options.beautify ? function() {
print("\n"); print("\n");
} : maybe_newline; } : options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() { var semicolon = options.beautify ? function() {
print(";"); print(";");
@@ -375,13 +385,12 @@ function OutputStream(options) {
} : noop; } : noop;
function get() { function get() {
if (might_add_newline) {
ensure_line_len();
}
return OUTPUT; return OUTPUT;
}; };
if (options.preamble) {
print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
}
var stack = []; var stack = [];
return { return {
get : get, get : get,
@@ -425,7 +434,6 @@ function OutputStream(options) {
pos : function() { return current_pos }, pos : function() { return current_pos },
push_node : function(node) { stack.push(node) }, push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() }, pop_node : function() { return stack.pop() },
stack : function() { return stack },
parent : function(n) { parent : function(n) {
return stack[stack.length - 2 - (n || 0)]; return stack[stack.length - 2 - (n || 0)];
} }
@@ -502,6 +510,17 @@ function OutputStream(options) {
})); }));
} }
if (comments.length > 0 && output.pos() == 0) {
if (output.option("shebang") && comments[0].type == "comment5") {
output.print("#!" + comments.shift().value + "\n");
output.indent();
}
var preamble = output.option("preamble");
if (preamble) {
output.print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
}
}
comments = comments.filter(output.comment_filter, self); comments = comments.filter(output.comment_filter, self);
// Keep single line comments after nlb, after nlb // Keep single line comments after nlb, after nlb
@@ -526,10 +545,6 @@ function OutputStream(options) {
output.space(); output.space();
} }
} }
else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) {
output.print("#!" + c.value + "\n");
output.indent();
}
}); });
} }
}); });
@@ -762,7 +777,7 @@ function OutputStream(options) {
DEFPRINT(AST_Do, function(self, output){ DEFPRINT(AST_Do, function(self, output){
output.print("do"); output.print("do");
output.space(); output.space();
self._do_print_body(output); make_block(self.body, output);
output.space(); output.space();
output.print("while"); output.print("while");
output.space(); output.space();
@@ -784,7 +799,7 @@ function OutputStream(options) {
output.print("for"); output.print("for");
output.space(); output.space();
output.with_parens(function(){ output.with_parens(function(){
if (self.init && !(self.init instanceof AST_EmptyStatement)) { if (self.init) {
if (self.init instanceof AST_Definitions) { if (self.init instanceof AST_Definitions) {
self.init.print(output); self.init.print(output);
} else { } else {
@@ -889,10 +904,10 @@ function OutputStream(options) {
/* -----[ if ]----- */ /* -----[ if ]----- */
function make_then(self, output) { function make_then(self, output) {
if (output.option("bracketize")) { var b = self.body;
make_block(self.body, output); if (output.option("bracketize")
return; || !output.option("screw_ie8") && b instanceof AST_Do)
} return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single // The squeezer replaces "block"-s that contain only a single
// statement with the statement itself; technically, the AST // statement with the statement itself; technically, the AST
// is correct, but this can create problems when we output an // is correct, but this can create problems when we output an
@@ -900,18 +915,7 @@ function OutputStream(options) {
// IF *without* an ELSE block (then the outer ELSE would refer // IF *without* an ELSE block (then the outer ELSE would refer
// to the inner IF). This function checks for this case and // to the inner IF). This function checks for this case and
// adds the block brackets if needed. // adds the block brackets if needed.
if (!self.body) if (!b) return output.force_semicolon();
return output.force_semicolon();
if (self.body instanceof AST_Do) {
// Unconditionally use the if/do-while workaround for all browsers.
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
// croaks with "syntax error" on code like this: if (foo)
// do ... while(cond); else ... we need block brackets
// around do/while
make_block(self.body, output);
return;
}
var b = self.body;
while (true) { while (true) {
if (b instanceof AST_If) { if (b instanceof AST_If) {
if (!b.alternative) { if (!b.alternative) {
@@ -939,7 +943,10 @@ function OutputStream(options) {
output.space(); output.space();
output.print("else"); output.print("else");
output.space(); output.space();
force_statement(self.alternative, output); if (self.alternative instanceof AST_If)
self.alternative.print(output);
else
force_statement(self.alternative, output);
} else { } else {
self._do_print_body(output); self._do_print_body(output);
} }
@@ -1317,15 +1324,7 @@ function OutputStream(options) {
function force_statement(stat, output) { function force_statement(stat, output) {
if (output.option("bracketize")) { if (output.option("bracketize")) {
if (!stat || stat instanceof AST_EmptyStatement) make_block(stat, output);
output.print("{}");
else if (stat instanceof AST_BlockStatement)
stat.print(output);
else output.with_block(function(){
output.indent();
stat.print(output);
output.newline();
});
} else { } else {
if (!stat || stat instanceof AST_EmptyStatement) if (!stat || stat instanceof AST_EmptyStatement)
output.force_semicolon(); output.force_semicolon();
@@ -1334,30 +1333,6 @@ function OutputStream(options) {
} }
}; };
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(output) {
var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
while (i > 0) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
p = a[--i];
} else {
return false;
}
}
};
// self should be AST_New. decide if we want to show parens or not. // self should be AST_New. decide if we want to show parens or not.
function need_constructor_parens(self, output) { function need_constructor_parens(self, output) {
// Always print parentheses with arguments // Always print parentheses with arguments
@@ -1398,11 +1373,11 @@ function OutputStream(options) {
}; };
function make_block(stmt, output) { function make_block(stmt, output) {
if (stmt instanceof AST_BlockStatement) { if (!stmt || stmt instanceof AST_EmptyStatement)
output.print("{}");
else if (stmt instanceof AST_BlockStatement)
stmt.print(output); stmt.print(output);
return; else output.with_block(function(){
}
output.with_block(function(){
output.indent(); output.indent();
stmt.print(output); stmt.print(output);
output.newline(); output.newline();

View File

@@ -195,12 +195,11 @@ function JS_Parse_Error(message, filename, line, col, pos) {
this.line = line; this.line = line;
this.col = col; this.col = col;
this.pos = pos; this.pos = pos;
this.stack = new Error().stack;
};
JS_Parse_Error.prototype.toString = function() {
return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
}; };
JS_Parse_Error.prototype = Object.create(Error.prototype);
JS_Parse_Error.prototype.constructor = JS_Parse_Error;
JS_Parse_Error.prototype.name = "SyntaxError";
configure_error_stack(JS_Parse_Error);
function js_error(message, filename, line, col, pos) { function js_error(message, filename, line, col, pos) {
throw new JS_Parse_Error(message, filename, line, col, pos); throw new JS_Parse_Error(message, filename, line, col, pos);
@@ -350,13 +349,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
}); });
if (prefix) num = prefix + num; if (prefix) num = prefix + num;
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
parse_error("SyntaxError: Legacy octal literals are not allowed in strict mode"); parse_error("Legacy octal literals are not allowed in strict mode");
} }
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) { if (!isNaN(valid)) {
return token("num", valid); return token("num", valid);
} else { } else {
parse_error("SyntaxError: Invalid syntax: " + num); parse_error("Invalid syntax: " + num);
} }
}; };
@@ -395,7 +394,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
// Parse // Parse
if (ch === "0") return "\0"; if (ch === "0") return "\0";
if (ch.length > 0 && next_token.has_directive("use strict")) if (ch.length > 0 && next_token.has_directive("use strict"))
parse_error("SyntaxError: Legacy octal escape sequences are not allowed in strict mode"); parse_error("Legacy octal escape sequences are not allowed in strict mode");
return String.fromCharCode(parseInt(ch, 8)); return String.fromCharCode(parseInt(ch, 8));
} }
@@ -404,18 +403,18 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
for (; n > 0; --n) { for (; n > 0; --n) {
var digit = parseInt(next(true), 16); var digit = parseInt(next(true), 16);
if (isNaN(digit)) if (isNaN(digit))
parse_error("SyntaxError: Invalid hex-character pattern in string"); parse_error("Invalid hex-character pattern in string");
num = (num << 4) | digit; num = (num << 4) | digit;
} }
return num; return num;
}; };
var read_string = with_eof_error("SyntaxError: Unterminated string constant", function(quote_char){ var read_string = with_eof_error("Unterminated string constant", function(quote_char){
var quote = next(), ret = ""; var quote = next(), ret = "";
for (;;) { for (;;) {
var ch = next(true, true); var ch = next(true, true);
if (ch == "\\") ch = read_escaped_char(true); if (ch == "\\") ch = read_escaped_char(true);
else if (NEWLINE_CHARS(ch)) parse_error("SyntaxError: Unterminated string constant"); else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant");
else if (ch == quote) break; else if (ch == quote) break;
ret += ch; ret += ch;
} }
@@ -440,7 +439,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return next_token; return next_token;
}; };
var skip_multiline_comment = with_eof_error("SyntaxError: Unterminated multiline comment", function(){ var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
var regex_allowed = S.regex_allowed; var regex_allowed = S.regex_allowed;
var i = find("*/", true); var i = find("*/", true);
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n'); var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
@@ -460,9 +459,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
else break; else break;
} }
else { else {
if (ch != "u") parse_error("SyntaxError: Expecting UnicodeEscapeSequence -- uXXXX"); if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
ch = read_escaped_char(); ch = read_escaped_char();
if (!is_identifier_char(ch)) parse_error("SyntaxError: Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
name += ch; name += ch;
backslash = false; backslash = false;
} }
@@ -474,10 +473,10 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return name; return name;
}; };
var read_regexp = with_eof_error("SyntaxError: Unterminated regular expression", function(regexp){ var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var prev_backslash = false, ch, in_class = false; var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) { while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("SyntaxError: Unexpected line terminator"); parse_error("Unexpected line terminator");
} else if (prev_backslash) { } else if (prev_backslash) {
regexp += "\\" + ch; regexp += "\\" + ch;
prev_backslash = false; prev_backslash = false;
@@ -498,7 +497,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
try { try {
return token("regexp", new RegExp(regexp, mods)); return token("regexp", new RegExp(regexp, mods));
} catch(e) { } catch(e) {
parse_error("SyntaxError: " + e.message); parse_error(e.message);
} }
}); });
@@ -559,6 +558,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function next_token(force_regexp) { function next_token(force_regexp) {
if (force_regexp != null) if (force_regexp != null)
return read_regexp(force_regexp); return read_regexp(force_regexp);
if (shebang && S.pos == 0 && looking_at("#!")) {
start_token();
forward(2);
skip_line_comment("comment5");
}
for (;;) { for (;;) {
skip_whitespace(); skip_whitespace();
start_token(); start_token();
@@ -590,16 +594,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if (PUNC_CHARS(ch)) return token("punc", next()); if (PUNC_CHARS(ch)) return token("punc", next());
if (OPERATOR_CHARS(ch)) return read_operator(); if (OPERATOR_CHARS(ch)) return read_operator();
if (code == 92 || is_identifier_start(code)) return read_word(); if (code == 92 || is_identifier_start(code)) return read_word();
if (shebang) {
if (S.pos == 0 && looking_at("#!")) {
forward(2);
skip_line_comment("comment5");
continue;
}
}
break; break;
} }
parse_error("SyntaxError: Unexpected character '" + ch + "'"); parse_error("Unexpected character '" + ch + "'");
}; };
next_token.context = function(nc) { next_token.context = function(nc) {
@@ -698,6 +695,7 @@ function parse($TEXT, options) {
html5_comments : true, html5_comments : true,
bare_returns : false, bare_returns : false,
shebang : true, shebang : true,
cli : false,
}); });
var S = { var S = {
@@ -756,14 +754,14 @@ function parse($TEXT, options) {
function unexpected(token) { function unexpected(token) {
if (token == null) if (token == null)
token = S.token; token = S.token;
token_error(token, "SyntaxError: Unexpected token: " + token.type + " (" + token.value + ")"); token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
}; };
function expect_token(type, val) { function expect_token(type, val) {
if (is(type, val)) { if (is(type, val)) {
return next(); return next();
} }
token_error(S.token, "SyntaxError: Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
}; };
function expect(punc) { return expect_token("punc", punc); }; function expect(punc) { return expect_token("punc", punc); };
@@ -892,7 +890,7 @@ function parse($TEXT, options) {
case "return": case "return":
if (S.in_function == 0 && !options.bare_returns) if (S.in_function == 0 && !options.bare_returns)
croak("SyntaxError: 'return' outside of function"); croak("'return' outside of function");
return new AST_Return({ return new AST_Return({
value: ( is("punc", ";") value: ( is("punc", ";")
? (next(), null) ? (next(), null)
@@ -909,7 +907,7 @@ function parse($TEXT, options) {
case "throw": case "throw":
if (S.token.nlb) if (S.token.nlb)
croak("SyntaxError: Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
return new AST_Throw({ return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp) value: (tmp = expression(true), semicolon(), tmp)
}); });
@@ -925,17 +923,15 @@ function parse($TEXT, options) {
case "with": case "with":
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
croak("SyntaxError: Strict mode may not include a with statement"); croak("Strict mode may not include a with statement");
} }
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesised(),
body : statement() body : statement()
}); });
default:
unexpected();
} }
} }
unexpected();
}); });
function labeled_statement() { function labeled_statement() {
@@ -945,7 +941,7 @@ function parse($TEXT, options) {
// syntactically incorrect if it contains a // syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a // LabelledStatement that is enclosed by a
// LabelledStatement with the same Identifier as label. // LabelledStatement with the same Identifier as label.
croak("SyntaxError: Label " + label.name + " defined twice"); croak("Label " + label.name + " defined twice");
} }
expect(":"); expect(":");
S.labels.push(label); S.labels.push(label);
@@ -958,7 +954,7 @@ function parse($TEXT, options) {
label.references.forEach(function(ref){ label.references.forEach(function(ref){
if (ref instanceof AST_Continue) { if (ref instanceof AST_Continue) {
ref = ref.label.start; ref = ref.label.start;
croak("SyntaxError: Continue label `" + label.name + "` refers to non-IterationStatement.", croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
ref.line, ref.col, ref.pos); ref.line, ref.col, ref.pos);
} }
}); });
@@ -978,11 +974,11 @@ function parse($TEXT, options) {
if (label != null) { if (label != null) {
ldef = find_if(function(l){ return l.name == label.name }, S.labels); ldef = find_if(function(l){ return l.name == label.name }, S.labels);
if (!ldef) if (!ldef)
croak("SyntaxError: Undefined label " + label.name); croak("Undefined label " + label.name);
label.thedef = ldef; label.thedef = ldef;
} }
else if (S.in_loop == 0) else if (S.in_loop == 0)
croak("SyntaxError: " + type.TYPE + " not inside a loop or switch"); croak(type.TYPE + " not inside a loop or switch");
semicolon(); semicolon();
var stat = new type({ label: label }); var stat = new type({ label: label });
if (ldef) ldef.references.push(stat); if (ldef) ldef.references.push(stat);
@@ -998,7 +994,7 @@ function parse($TEXT, options) {
: expression(true, true); : expression(true, true);
if (is("operator", "in")) { if (is("operator", "in")) {
if (init instanceof AST_Var && init.definitions.length > 1) if (init instanceof AST_Var && init.definitions.length > 1)
croak("SyntaxError: Only one variable declaration allowed in for..in loop"); croak("Only one variable declaration allowed in for..in loop");
next(); next();
return for_in(init); return for_in(init);
} }
@@ -1148,7 +1144,7 @@ function parse($TEXT, options) {
}); });
} }
if (!bcatch && !bfinally) if (!bcatch && !bfinally)
croak("SyntaxError: Missing catch/finally blocks"); croak("Missing catch/finally blocks");
return new AST_Try({ return new AST_Try({
body : body, body : body,
bcatch : bcatch, bcatch : bcatch,
@@ -1242,7 +1238,7 @@ function parse($TEXT, options) {
break; break;
case "operator": case "operator":
if (!is_identifier_string(tok.value)) { if (!is_identifier_string(tok.value)) {
croak("SyntaxError: Invalid getter/setter name: " + tok.value, croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos); tok.line, tok.col, tok.pos);
} }
ret = _make_symbol(AST_SymbolRef); ret = _make_symbol(AST_SymbolRef);
@@ -1308,6 +1304,10 @@ function parse($TEXT, options) {
}); });
}); });
var create_accessor = embed_tokens(function() {
return function_(AST_Accessor);
});
var object_ = embed_tokens(function() { var object_ = embed_tokens(function() {
expect("{"); expect("{");
var first = true, a = []; var first = true, a = [];
@@ -1324,7 +1324,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectGetter({ a.push(new AST_ObjectGetter({
start : start, start : start,
key : as_atom_node(), key : as_atom_node(),
value : function_(AST_Accessor), value : create_accessor(),
end : prev() end : prev()
})); }));
continue; continue;
@@ -1333,7 +1333,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectSetter({ a.push(new AST_ObjectSetter({
start : start, start : start,
key : as_atom_node(), key : as_atom_node(),
value : function_(AST_Accessor), value : create_accessor(),
end : prev() end : prev()
})); }));
continue; continue;
@@ -1393,7 +1393,7 @@ function parse($TEXT, options) {
function as_symbol(type, noerror) { function as_symbol(type, noerror) {
if (!is("name")) { if (!is("name")) {
if (!noerror) croak("SyntaxError: Name expected"); if (!noerror) croak("Name expected");
return null; return null;
} }
var sym = _make_symbol(type); var sym = _make_symbol(type);
@@ -1457,7 +1457,7 @@ function parse($TEXT, options) {
function make_unary(ctor, op, expr) { function make_unary(ctor, op, expr) {
if ((op == "++" || op == "--") && !is_assignable(expr)) if ((op == "++" || op == "--") && !is_assignable(expr))
croak("SyntaxError: Invalid use of " + op + " operator"); croak("Invalid use of " + op + " operator", null, ctor === AST_UnaryPrefix ? expr.start.col - 1 : null);
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
}; };
@@ -1502,9 +1502,8 @@ function parse($TEXT, options) {
}; };
function is_assignable(expr) { function is_assignable(expr) {
if (!options.strict) return true; if (options.cli) return true;
if (expr instanceof AST_This) return false; return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
}; };
var maybe_assign = function(no_in) { var maybe_assign = function(no_in) {
@@ -1521,7 +1520,7 @@ function parse($TEXT, options) {
end : prev() end : prev()
}); });
} }
croak("SyntaxError: Invalid assignment"); croak("Invalid assignment");
} }
return left; return left;
}; };

View File

@@ -149,6 +149,7 @@ function mangle_properties(ast, options) {
// only function declarations after this line // only function declarations after this line
function can_mangle(name) { function can_mangle(name) {
if (!is_identifier(name)) return false;
if (unmangleable.indexOf(name) >= 0) return false; if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) { if (options.only_cache) {

View File

@@ -51,7 +51,6 @@ function SymbolDef(scope, index, orig) {
this.global = false; this.global = false;
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.constant = false;
this.index = index; this.index = index;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
}; };
@@ -97,26 +96,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var scope = self.parent_scope = null; var scope = self.parent_scope = null;
var labels = new Dictionary(); var labels = new Dictionary();
var defun = null; var defun = null;
var last_var_had_const_pragma = false;
var nesting = 0;
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (options.screw_ie8 && node instanceof AST_Catch) { if (node instanceof AST_Catch) {
var save_scope = scope; var save_scope = scope;
scope = new AST_Scope(node); scope = new AST_Scope(node);
scope.init_scope_vars(nesting); scope.init_scope_vars();
scope.parent_scope = save_scope; scope.parent_scope = save_scope;
descend(); descend();
scope = save_scope; scope = save_scope;
return true; return true;
} }
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
node.init_scope_vars(nesting); node.init_scope_vars();
var save_scope = node.parent_scope = scope; var save_scope = node.parent_scope = scope;
var save_defun = defun; var save_defun = defun;
var save_labels = labels; var save_labels = labels;
defun = scope = node; defun = scope = node;
labels = new Dictionary(); labels = new Dictionary();
++nesting; descend(); --nesting; descend();
scope = save_scope; scope = save_scope;
defun = save_defun; defun = save_defun;
labels = save_labels; labels = save_labels;
@@ -155,18 +152,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// later. // later.
(node.scope = defun.parent_scope).def_function(node); (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 else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) { || node instanceof AST_SymbolConst) {
var def = defun.def_variable(node); defun.def_variable(node);
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
def.init = tw.parent().value;
} }
else if (node instanceof AST_SymbolCatch) { else if (node instanceof AST_SymbolCatch) {
(options.screw_ie8 ? scope : defun) scope.def_variable(node);
.def_variable(node);
} }
else if (node instanceof AST_LabelRef) { else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name); var sym = labels.get(node.name);
@@ -197,8 +188,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var name = node.name; var name = node.name;
var parent = tw.parent(); if (name == "eval" && tw.parent() instanceof AST_Call) {
if (name == "eval" && parent instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true; s.uses_eval = true;
} }
@@ -208,34 +198,52 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.scope.uses_arguments = true; node.scope.uses_arguments = true;
} }
if (!sym) { if (!sym) {
var g; sym = self.def_global(node);
if (globals.has(name)) {
g = globals.get(name);
} else {
g = new SymbolDef(self, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
}
sym = g;
} }
node.thedef = sym; node.thedef = sym;
if (parent instanceof AST_Unary && (parent.operator === '++' || parent.operator === '--') node.reference(options);
|| parent instanceof AST_Assign && parent.left === node) {
sym.modified = true;
}
node.reference();
return true; return true;
} }
}); });
self.walk(tw); self.walk(tw);
// pass 3: fix up any scoping issue with IE8
if (!options.screw_ie8) {
self.walk(new TreeWalker(function(node, descend) {
if (node instanceof AST_SymbolCatch) {
var name = node.name;
var refs = node.thedef.references;
var scope = node.thedef.scope.parent_scope;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
});
node.thedef = def;
return true;
}
}));
}
if (options.cache) { if (options.cache) {
this.cname = options.cache.cname; this.cname = options.cache.cname;
} }
}); });
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ AST_Toplevel.DEFMETHOD("def_global", function(node){
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
return g;
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(){
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
@@ -243,7 +251,6 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.parent_scope = null; // the parent scope this.parent_scope = null; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables this.cname = -1; // the current index for mangling functions/variables
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
}); });
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
@@ -255,16 +262,20 @@ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
}); });
AST_SymbolRef.DEFMETHOD("reference", function() { AST_SymbolRef.DEFMETHOD("reference", function(options) {
var def = this.definition(); var def = this.definition();
def.references.push(this); def.references.push(this);
var s = this.scope; var s = this.scope;
while (s) { while (s) {
push_uniq(s.enclosed, def); push_uniq(s.enclosed, def);
if (options.keep_fnames) {
s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
if (s === def.scope) break; if (s === def.scope) break;
s = s.parent_scope; s = s.parent_scope;
} }
this.frame = this.scope.nesting - def.scope.nesting;
}); });
AST_Scope.DEFMETHOD("find_variable", function(name){ AST_Scope.DEFMETHOD("find_variable", function(name){
@@ -329,11 +340,6 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
} }
}); });
AST_Scope.DEFMETHOD("references", function(sym){
if (sym instanceof AST_Symbol) sym = sym.definition();
return this.enclosed.indexOf(sym) < 0 ? null : sym;
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){ AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options); return this.definition().unmangleable(options);
}); });
@@ -373,12 +379,6 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global; 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){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, { return defaults(options, {
except : [], except : [],

View File

@@ -78,13 +78,28 @@ function repeat_string(str, i) {
return d; return d;
}; };
function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", {
get: function() {
var err = new Error(this.message);
err.name = this.name;
try {
throw err;
} catch(e) {
return e.stack;
}
}
});
}
function DefaultsError(msg, defs) { function DefaultsError(msg, defs) {
Error.call(this, msg); this.message = msg;
this.msg = msg;
this.defs = defs; this.defs = defs;
}; };
DefaultsError.prototype = Object.create(Error.prototype); DefaultsError.prototype = Object.create(Error.prototype);
DefaultsError.prototype.constructor = DefaultsError; DefaultsError.prototype.constructor = DefaultsError;
DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError);
DefaultsError.croak = function(msg, defs) { DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs); throw new DefaultsError(msg, defs);
@@ -111,9 +126,11 @@ function merge(obj, ext) {
return count; return count;
}; };
function noop() {}; function noop() {}
function return_false() { return false; } function return_false() { return false; }
function return_true() { return true; } function return_true() { return true; }
function return_this() { return this; }
function return_null() { return null; }
var MAP = (function(){ var MAP = (function(){
function MAP(a, f, backwards) { function MAP(a, f, backwards) {
@@ -170,7 +187,7 @@ function push_uniq(array, el) {
function string_template(text, props) { function string_template(text, props) {
return text.replace(/\{(.+?)\}/g, function(str, p){ return text.replace(/\{(.+?)\}/g, function(str, p){
return props[p]; return props && props[p];
}); });
}; };
@@ -320,3 +337,26 @@ Dictionary.fromObject = function(obj) {
function HOP(obj, prop) { function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop); return Object.prototype.hasOwnProperty.call(obj, prop);
} }
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack) {
var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
} else {
return false;
}
}
}

128
npm-shrinkwrap.json generated
View File

@@ -1,128 +0,0 @@
{
"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

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "2.7.5", "version": "2.8.16",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -29,9 +29,7 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"async": "~0.2.6",
"source-map": "~0.5.1", "source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0" "yargs": "~3.10.0"
}, },
"devDependencies": { "devDependencies": {
@@ -41,13 +39,15 @@
"estraverse": "~1.5.1", "estraverse": "~1.5.1",
"mocha": "~2.3.4" "mocha": "~2.3.4"
}, },
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": { "browserify": {
"transform": [ "transform": [
"uglify-to-browserify" "uglify-to-browserify"
] ]
}, },
"scripts": { "scripts": {
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
}, },
"keywords": ["uglify", "uglify-js", "minify", "minifier"] "keywords": ["uglify", "uglify-js", "minify", "minifier"]

80
test/benchmark.js Normal file
View File

@@ -0,0 +1,80 @@
#! /usr/bin/env node
// -*- js -*-
"use strict";
var createHash = require("crypto").createHash;
var fork = require("child_process").fork;
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc", "warnings=false");
}
args.push("--stats");
var urls = [
"https://code.jquery.com/jquery-3.1.1.js",
"https://code.angularjs.org/1.6.1/angular.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js",
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://unpkg.com/react@15.3.2/dist/react.js",
"http://builds.emberjs.com/tags/v2.11.0/ember.prod.js",
"https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js",
"https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js",
];
var results = {};
var remaining = 2 * urls.length;
function done() {
if (!--remaining) {
var failures = [];
urls.forEach(function(url) {
var info = results[url];
console.log();
console.log(url);
console.log(info.log);
var elapsed = 0;
info.log.replace(/: ([0-9]+\.[0-9]{3})s/g, function(match, time) {
elapsed += parseFloat(time);
});
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes");
console.log("SHA1 sum:", info.sha1);
if (info.code) {
failures.push(url);
}
});
if (failures.length) {
console.error("Benchmark failed:");
failures.forEach(function(url) {
console.error(url);
});
process.exit(1);
}
}
}
urls.forEach(function(url) {
results[url] = {
input: 0,
output: 0,
log: ""
};
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.on("data", function(data) {
results[url].input += data.length;
}).pipe(uglifyjs.stdin);
uglifyjs.stdout.on("data", function(data) {
results[url].output += data.length;
}).pipe(createHash("sha1")).on("data", function(data) {
results[url].sha1 = data.toString("hex");
done();
});
uglifyjs.stderr.setEncoding("utf8");
uglifyjs.stderr.on("data", function(data) {
results[url].log += data;
});
uglifyjs.on("exit", function(code) {
results[url].code = code;
done();
});
});
});

View File

@@ -21,10 +21,19 @@ constant_join: {
input: { input: {
var a = [ "foo", "bar", "baz" ].join(""); var a = [ "foo", "bar", "baz" ].join("");
var a1 = [ "foo", "bar", "baz" ].join(); var a1 = [ "foo", "bar", "baz" ].join();
var a2 = [ "foo", "bar", "baz" ].join(null);
var a3 = [ "foo", "bar", "baz" ].join(void 0);
var a4 = [ "foo", , "baz" ].join();
var a5 = [ "foo", null, "baz" ].join();
var a6 = [ "foo", void 0, "baz" ].join();
var b = [ "foo", 1, 2, 3, "bar" ].join(""); var b = [ "foo", 1, 2, 3, "bar" ].join("");
var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join(""); var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join("");
var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join(""); var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join("");
var c2 = [ 1, 2, "foo", "bar", baz() ].join(""); var c2 = [ 1, 2, "foo", "bar", baz() ].join("");
var c3 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join("");
var c4 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join("");
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
var c6 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join();
var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-"); var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-");
var e = [].join(foo + bar); var e = [].join(foo + bar);
var f = [].join(""); var f = [].join("");
@@ -33,10 +42,19 @@ constant_join: {
expect: { expect: {
var a = "foobarbaz"; var a = "foobarbaz";
var a1 = "foo,bar,baz"; var a1 = "foo,bar,baz";
var a2 = "foonullbarnullbaz";
var a3 = "foo,bar,baz";
var a4 = "foo,,baz";
var a5 = "foo,,baz";
var a6 = "foo,,baz";
var b = "foo123bar"; var b = "foo123bar";
var c = boo() + "foo123bar" + bar(); var c = boo() + "foo123bar" + bar();
var c1 = "" + boo() + bar() + "foo123bar" + bar(); var c1 = "" + boo() + bar() + "foo123bar" + bar();
var c2 = "12foobar" + baz(); var c2 = "12foobar" + baz();
var c3 = boo() + bar() + "foo123bar" + bar() + "foo";
var c4 = "12foobar" + baz();
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
var c6 = [ "1,2,,,foo,bar", baz() ].join();
var d = "foo-3bar-baz"; var d = "foo-3bar-baz";
var e = [].join(foo + bar); var e = [].join(foo + bar);
var f = ""; var f = "";
@@ -72,3 +90,88 @@ constant_join_2: {
var f = "strstr" + variable + "foobarmoo" + foo; var f = "strstr" + variable + "foobarmoo" + foo;
} }
} }
constant_join_3: {
options = {
unsafe: true,
evaluate: true,
};
input: {
var a = [ null ].join();
var b = [ , ].join();
var c = [ , 1, , 3 ].join();
var d = [ foo ].join();
var e = [ foo, null, undefined, bar ].join("-");
var f = [ foo, bar ].join("");
var g = [ null, "foo", null, bar + "baz" ].join("");
var h = [ null, "foo", null, bar + "baz" ].join("-");
var i = [ "foo" + bar, null, baz + "moo" ].join("");
var j = [ foo + "bar", baz ].join("");
var k = [ foo, "bar" + baz ].join("");
var l = [ foo, bar + "baz" ].join("");
}
expect: {
var a = "";
var b = "";
var c = ",1,,3";
var d = "" + foo;
var e = [ foo, "-", bar ].join("-");
var f = "" + foo + bar;
var g = "foo" + bar + "baz";
var h = [ "-foo-", bar + "baz" ].join("-");
var i = "foo" + bar + baz + "moo";
var j = foo + "bar" + baz;
var k = foo + "bar" + baz;
var l = foo + (bar + "baz");
}
}
for_loop: {
options = {
unsafe : true,
unused : true,
evaluate : true,
reduce_vars : true
};
input: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
}
function f1() {
var a = [1, 2, 3];
for (var i = 0, len = a.length; i < len; i++) {
console.log(a[i]);
}
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
a[i]++;
}
}
}
expect: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f1() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++)
a[i]++;
}
}
}

View File

@@ -43,6 +43,7 @@ collapse_vars_side_effects_1: {
z = i += 4; z = i += 4;
log(x, z, y, i); log(x, z, y, i);
} }
f1(), f2(), f3(), f4();
} }
expect: { expect: {
function f1() { function f1() {
@@ -73,7 +74,9 @@ collapse_vars_side_effects_1: {
y = i += 3; y = i += 3;
log(x, i += 4, y, i); log(x, i += 4, y, i);
} }
f1(), f2(), f3(), f4();
} }
expect_stdout: true
} }
collapse_vars_side_effects_2: { collapse_vars_side_effects_2: {
@@ -338,14 +341,15 @@ collapse_vars_while: {
collapse_vars_do_while: { collapse_vars_do_while: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:false, loops:false, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:false, loops:false, unused:"keep_assign",
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true hoist_funs:true, keep_fargs:true, if_return:true, join_vars:true, cascade:true,
side_effects:true
} }
input: { input: {
function f1(y) { function f1(y) {
// The constant do-while condition `c` will be replaced. // The constant do-while condition `c` will not be replaced.
var c = 9; var c = 9;
do { } while (c === 77); do {} while (c === 77);
} }
function f2(y) { function f2(y) {
// The non-constant do-while condition `c` will not be replaced. // The non-constant do-while condition `c` will not be replaced.
@@ -380,7 +384,8 @@ collapse_vars_do_while: {
} }
expect: { expect: {
function f1(y) { function f1(y) {
do ; while (false); var c = 9;
do ; while (77 === c);
} }
function f2(y) { function f2(y) {
var c = 5 - y; var c = 5 - y;
@@ -409,6 +414,80 @@ collapse_vars_do_while: {
} }
} }
collapse_vars_do_while_drop_assign: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:false, loops:false, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1(y) {
// The constant do-while condition `c` will be not replaced.
var c = 9;
do {} while (c === 77);
}
function f2(y) {
// The non-constant do-while condition `c` will not be replaced.
var c = 5 - y;
do { } while (c);
}
function f3(y) {
// The constant `x` will be replaced in the do loop body.
function fn(n) { console.log(n); }
var a = 2, x = 7;
do {
fn(a = x);
break;
} while (y);
}
function f4(y) {
// The non-constant `a` will not be replaced in the do loop body.
var a = y / 4;
do {
return a;
} while (y);
}
function f5(y) {
function p(x) { console.log(x); }
do {
// The non-constant `a` will be replaced in p(a)
// because it is declared in same block.
var a = y - 3;
p(a);
} while (--y);
}
}
expect: {
function f1(y) {
var c = 9;
do ; while (77 === c);
}
function f2(y) {
var c = 5 - y;
do ; while (c);
}
function f3(y) {
function fn(n) { console.log(n); }
do {
fn(7);
break;
} while (y);
}
function f4(y) {
var a = y / 4;
do
return a;
while (y);
}
function f5(y) {
function p(x) { console.log(x); }
do {
p(y - 3);
} while (--y);
}
}
}
collapse_vars_seq: { collapse_vars_seq: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
@@ -567,8 +646,9 @@ collapse_vars_assignment: {
collapse_vars_lvalues: { collapse_vars_lvalues: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:"keep_assign",
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true hoist_funs:true, keep_fargs:true, if_return:true, join_vars:true, cascade:true,
side_effects:true
} }
input: { input: {
function f0(x) { var i = ++x; return x += i; } function f0(x) { var i = ++x; return x += i; }
@@ -593,7 +673,38 @@ collapse_vars_lvalues: {
function f7(x) { var w = e1(), v = e2(), c = v - x; return (w = x) - c; } function f7(x) { var w = e1(), v = e2(), c = v - x; return (w = x) - c; }
function f8(x) { var w = e1(), v = e2(); return (w = x) - (v - x); } function f8(x) { var w = e1(), v = e2(); return (w = x) - (v - x); }
function f9(x) { var w = e1(); return e2() - x - (w = x); } function f9(x) { var w = e1(); return e2() - x - (w = x); }
}
}
collapse_vars_lvalues_drop_assign: {
options = {
collapse_vars:true, 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 f0(x) { var i = ++x; return x += i; }
function f1(x) { var a = (x -= 3); return x += a; }
function f2(x) { var z = x, a = ++z; return z += a; }
function f3(x) { var a = (x -= 3), b = x + a; return b; }
function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { var w = e1(), v = e2(), c = v = --x, b = w = x; return b - c; }
function f6(x) { var w = e1(), v = e2(), c = v = --x, b = w = x; return c - b; }
function f7(x) { var w = e1(), v = e2(), c = v - x, b = w = x; return b - c; }
function f8(x) { var w = e1(), v = e2(), b = w = x, c = v - x; return b - c; }
function f9(x) { var w = e1(), v = e2(), b = w = x, c = v - x; return c - b; }
}
expect: {
function f0(x) { var i = ++x; return x += i; }
function f1(x) { var a = (x -= 3); return x += a; }
function f2(x) { var z = x, a = ++z; return z += a; }
function f3(x) { var a = (x -= 3); return x + a; }
function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { var v = (e1(), e2()), c = v = --x; return x - c; }
function f6(x) { e1(), e2(); return --x - x; }
function f7(x) { var v = (e1(), e2()), c = v - x; return x - c; }
function f8(x) { var v = (e1(), e2()); return x - (v - x); }
function f9(x) { e1(); return e2() - x - x; }
} }
} }
@@ -715,6 +826,7 @@ collapse_vars_repeated: {
console.log(e + "!"); console.log(e + "!");
})("!"); })("!");
} }
expect_stdout: true
} }
collapse_vars_closures: { collapse_vars_closures: {
@@ -1001,6 +1113,7 @@ collapse_vars_eval_and_with: {
return function() { with (o) console.log(a) }; return function() { with (o) console.log(a) };
})()(); })()();
} }
expect_stdout: true
} }
collapse_vars_constants: { collapse_vars_constants: {
@@ -1035,7 +1148,7 @@ collapse_vars_constants: {
function f3(x) { function f3(x) {
var b = x.prop; var b = x.prop;
sideeffect1(); sideeffect1();
return b + (function() { return -9; })(); return b + -9;
} }
} }
} }
@@ -1044,7 +1157,8 @@ collapse_vars_arguments: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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 keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
toplevel:true
} }
input: { input: {
var outer = function() { var outer = function() {
@@ -1059,6 +1173,7 @@ collapse_vars_arguments: {
(function(){console.log(arguments);})(7, 1); (function(){console.log(arguments);})(7, 1);
})(); })();
} }
expect_stdout: true
} }
collapse_vars_short_circuit: { collapse_vars_short_circuit: {
@@ -1203,9 +1318,275 @@ collapse_vars_regexp: {
}; };
} }
(function(){ (function(){
var result, rx = /ab*/g; var result, s = "acdabcdeabbb", rx = /ab*/g;
while (result = rx.exec('acdabcdeabbb')) while (result = rx.exec(s))
console.log(result[0]); console.log(result[0]);
})(); })();
} }
expect_stdout: true
}
issue_1537: {
options = {
collapse_vars: true,
}
input: {
var k = '';
for (k in {prop: 'val'}){}
}
expect: {
var k = '';
for (k in {prop: 'val'});
}
}
issue_1562: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var v = 1, B = 2;
for (v in objs) f(B);
var x = 3, C = 10;
while(x + 2) bar(C);
var y = 4, D = 20;
do bar(D); while(y + 2);
var z = 5, E = 30;
for (; f(z + 2) ;) bar(E);
}
expect: {
var v = 1;
for (v in objs) f(2);
var x = 3;
while(x + 2) bar(10);
var y = 4;
do bar(20); while(y + 2);
var z = 5;
for (; f(z + 2) ;) bar(30);
}
}
issue_1605_1: {
options = {
collapse_vars: true,
toplevel: false,
}
input: {
function foo(x) {
var y = x;
return y;
}
var o = new Object;
o.p = 1;
}
expect: {
function foo(x) {
return x;
}
var o = new Object;
o.p = 1;
}
}
issue_1605_2: {
options = {
collapse_vars: true,
toplevel: "vars",
}
input: {
function foo(x) {
var y = x;
return y;
}
var o = new Object;
o.p = 1;
}
expect: {
function foo(x) {
return x;
}
(new Object).p = 1;
}
}
issue_1631_1: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var pc = 0;
function f(x) {
pc = 200;
return 100;
}
function x() {
var t = f();
pc += t;
return pc;
}
console.log(x());
}
expect: {
function f(x) {
return pc = 200, 100;
}
function x() {
var t = f();
return pc += t;
}
var pc = 0;
console.log(x());
}
expect_stdout: "300"
}
issue_1631_2: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
function g() {
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function f() {
return a = 2, 4;
}
function g() {
var t = f();
return b = a + t;
}
var a = 0, b = 1;
console.log(g());
}
expect_stdout: "6"
}
issue_1631_3: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
function g() {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function g() {
function f() {
return a = 2, 4;
}
var a = 0, b = 1, t = f();
return b = a + t;
}
console.log(g());
}
expect_stdout: "6"
}
var_side_effects_1: {
options = {
collapse_vars: true,
}
input: {
var print = console.log.bind(console);
function foo(x) {
var twice = x * 2;
print('Foo:', twice);
}
foo(10);
}
expect: {
var print = console.log.bind(console);
function foo(x) {
print('Foo:', 2 * x);
}
foo(10);
}
expect_stdout: true
}
var_side_effects_2: {
options = {
collapse_vars: true,
}
input: {
var print = console.log.bind(console);
function foo(x) {
var twice = x.y * 2;
print('Foo:', twice);
}
foo({ y: 10 });
}
expect: {
var print = console.log.bind(console);
function foo(x) {
var twice = 2 * x.y;
print('Foo:', twice);
}
foo({ y: 10 });
}
expect_stdout: true
}
var_side_effects_3: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
var print = console.log.bind(console);
function foo(x) {
var twice = x.y * 2;
print('Foo:', twice);
}
foo({ y: 10 });
}
expect: {
var print = console.log.bind(console);
function foo(x) {
print('Foo:', 2 * x.y);
}
foo({ y: 10 });
}
expect_stdout: true
} }

View File

@@ -24,3 +24,200 @@ concat_1: {
var f = "\x00360\08\0"; var f = "\x00360\08\0";
} }
} }
concat_2: {
options = {};
input: {
console.log(
1 + (2 + 3),
1 + (2 + "3"),
1 + ("2" + 3),
1 + ("2" + "3"),
"1" + (2 + 3),
"1" + (2 + "3"),
"1" + ("2" + 3),
"1" + ("2" + "3")
);
}
expect: {
console.log(
1 + (2 + 3),
1 + (2 + "3"),
1 + "2" + 3,
1 + "2" + "3",
"1" + (2 + 3),
"1" + 2 + "3",
"1" + "2" + 3,
"1" + "2" + "3"
);
}
expect_stdout: true
}
concat_3: {
options = {};
input: {
console.log(
1 + 2 + (3 + 4 + 5),
1 + 2 + (3 + 4 + "5"),
1 + 2 + (3 + "4" + 5),
1 + 2 + (3 + "4" + "5"),
1 + 2 + ("3" + 4 + 5),
1 + 2 + ("3" + 4 + "5"),
1 + 2 + ("3" + "4" + 5),
1 + 2 + ("3" + "4" + "5")
);
}
expect: {
console.log(
1 + 2 + (3 + 4 + 5),
1 + 2 + (3 + 4 + "5"),
1 + 2 + (3 + "4") + 5,
1 + 2 + (3 + "4") + "5",
1 + 2 + "3" + 4 + 5,
1 + 2 + "3" + 4 + "5",
1 + 2 + "3" + "4" + 5,
1 + 2 + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_4: {
options = {};
input: {
console.log(
1 + "2" + (3 + 4 + 5),
1 + "2" + (3 + 4 + "5"),
1 + "2" + (3 + "4" + 5),
1 + "2" + (3 + "4" + "5"),
1 + "2" + ("3" + 4 + 5),
1 + "2" + ("3" + 4 + "5"),
1 + "2" + ("3" + "4" + 5),
1 + "2" + ("3" + "4" + "5")
);
}
expect: {
console.log(
1 + "2" + (3 + 4 + 5),
1 + "2" + (3 + 4) + "5",
1 + "2" + 3 + "4" + 5,
1 + "2" + 3 + "4" + "5",
1 + "2" + "3" + 4 + 5,
1 + "2" + "3" + 4 + "5",
1 + "2" + "3" + "4" + 5,
1 + "2" + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_5: {
options = {};
input: {
console.log(
"1" + 2 + (3 + 4 + 5),
"1" + 2 + (3 + 4 + "5"),
"1" + 2 + (3 + "4" + 5),
"1" + 2 + (3 + "4" + "5"),
"1" + 2 + ("3" + 4 + 5),
"1" + 2 + ("3" + 4 + "5"),
"1" + 2 + ("3" + "4" + 5),
"1" + 2 + ("3" + "4" + "5")
);
}
expect: {
console.log(
"1" + 2 + (3 + 4 + 5),
"1" + 2 + (3 + 4) + "5",
"1" + 2 + 3 + "4" + 5,
"1" + 2 + 3 + "4" + "5",
"1" + 2 + "3" + 4 + 5,
"1" + 2 + "3" + 4 + "5",
"1" + 2 + "3" + "4" + 5,
"1" + 2 + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_6: {
options = {};
input: {
console.log(
"1" + "2" + (3 + 4 + 5),
"1" + "2" + (3 + 4 + "5"),
"1" + "2" + (3 + "4" + 5),
"1" + "2" + (3 + "4" + "5"),
"1" + "2" + ("3" + 4 + 5),
"1" + "2" + ("3" + 4 + "5"),
"1" + "2" + ("3" + "4" + 5),
"1" + "2" + ("3" + "4" + "5")
);
}
expect: {
console.log(
"1" + "2" + (3 + 4 + 5),
"1" + "2" + (3 + 4) + "5",
"1" + "2" + 3 + "4" + 5,
"1" + "2" + 3 + "4" + "5",
"1" + "2" + "3" + 4 + 5,
"1" + "2" + "3" + 4 + "5",
"1" + "2" + "3" + "4" + 5,
"1" + "2" + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_7: {
input: {
console.log(
"" + 1,
"" + "1",
"" + 1 + 2,
"" + 1 + "2",
"" + "1" + 2,
"" + "1" + "2",
"" + (x += "foo")
);
}
expect: {
console.log(
"" + 1,
"1",
"" + 1 + 2,
1 + "2",
"1" + 2,
"1" + "2",
x += "foo"
);
}
expect_stdout: true
}
concat_8: {
input: {
console.log(
1 + "",
"1" + "",
1 + 2 + "",
1 + "2" + "",
"1" + 2 + "",
"1" + "2" + "",
(x += "foo") + ""
);
}
expect: {
console.log(
1 + "",
"1",
1 + 2 + "",
1 + "2",
"1" + 2,
"1" + "2",
x += "foo"
);
}
expect_stdout: true
}

View File

@@ -50,7 +50,8 @@ ifs_3_should_warn: {
conditionals : true, conditionals : true,
dead_code : true, dead_code : true,
evaluate : true, evaluate : true,
booleans : true booleans : true,
side_effects : true,
}; };
input: { input: {
var x, y; var x, y;
@@ -135,16 +136,28 @@ ifs_6: {
comparisons: true comparisons: true
}; };
input: { input: {
var x; var x, y;
if (!foo && !bar && !baz && !boo) { if (!foo && !bar && !baz && !boo) {
x = 10; x = 10;
} else { } else {
x = 20; x = 20;
} }
if (y) {
x[foo] = 10;
} else {
x[foo] = 20;
}
if (foo) {
x[bar] = 10;
} else {
x[bar] = 20;
}
} }
expect: { expect: {
var x; var x, y;
x = foo || bar || baz || boo ? 20 : 10; x = foo || bar || baz || boo ? 20 : 10;
x[foo] = y ? 10 : 20;
foo ? x[bar] = 10 : x[bar] = 20;
} }
} }
@@ -159,10 +172,16 @@ cond_1: {
} else { } else {
do_something(y); do_something(y);
} }
if (some_condition()) {
side_effects(x);
} else {
side_effects(y);
}
} }
expect: { expect: {
var do_something; var do_something;
do_something(some_condition() ? x : y); do_something(some_condition() ? x : y);
some_condition() ? side_effects(x) : side_effects(y);
} }
} }
@@ -213,10 +232,16 @@ cond_4: {
} else { } else {
do_something(); do_something();
} }
if (some_condition()) {
side_effects();
} else {
side_effects();
}
} }
expect: { expect: {
var do_something; var do_something;
some_condition(), do_something(); some_condition(), do_something();
some_condition(), side_effects();
} }
} }
@@ -250,7 +275,8 @@ cond_5: {
cond_7: { cond_7: {
options = { options = {
conditionals: true, conditionals: true,
evaluate : true evaluate : true,
side_effects: true,
}; };
input: { input: {
var x, y, z, a, b; var x, y, z, a, b;
@@ -635,166 +661,6 @@ ternary_boolean_alternative: {
} }
} }
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 : { trivial_boolean_ternary_expressions : {
options = { options = {
conditionals: true, conditionals: true,
@@ -874,6 +740,7 @@ issue_1154: {
conditionals: true, conditionals: true,
evaluate : true, evaluate : true,
booleans : true, booleans : true,
side_effects: true,
}; };
input: { input: {
function f1(x) { return x ? -1 : -1; } function f1(x) { return x ? -1 : -1; }
@@ -902,7 +769,167 @@ issue_1154: {
function g2() { return g(), 2; } function g2() { return g(), 2; }
function g3() { return g(), -4; } function g3() { return g(), -4; }
function g4() { return g(), !1; } function g4() { return g(), !1; }
function g5() { return g(), void 0; } function g5() { return void g(); }
function g6() { return g(), "number"; } function g6() { return g(), "number"; }
} }
} }
no_evaluate: {
options = {
conditionals: true,
evaluate : false,
side_effects: true,
}
input: {
function f(b) {
a = b ? !0 : !0;
a = b ? ~1 : ~1;
a = b ? -2 : -2;
a = b ? +3 : +3;
}
}
expect: {
function f(b) {
a = !0;
a = ~1;
a = -2;
a = +3;
}
}
}
equality_conditionals_false: {
options = {
conditionals: false,
sequences: true,
}
input: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0);
f(0, true, 0);
f(1, 2, 3);
f(1, null, 3);
f(NaN);
f(NaN, "foo");
}
expect: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0),
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
}
expect_stdout: true
}
equality_conditionals_true: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0);
f(0, true, 0);
f(1, 2, 3);
f(1, null, 3);
f(NaN);
f(NaN, "foo");
}
expect: {
function f(a, b, c) {
console.log(
(b, a == a),
a == (b ? a : c),
(b, a != a),
a != (b ? a : c),
(b, a === a),
a === (b ? a : c),
(b, a !== a),
a !== (b ? a : c)
);
}
f(0, 0, 0),
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
}
expect_stdout: true
}
issue_1645_1: {
options = {
conditionals: true,
}
input: {
var a = 100, b = 10;
(b = a) ? a++ + (b += a) ? b += a : b += a : b ^= a;
console.log(a, b);
}
expect: {
var a = 100, b = 10;
(b = a) ? (a++ + (b += a), b += a) : b ^= a;
console.log(a,b);
}
expect_stdout: true
}
issue_1645_2: {
options = {
conditionals: true,
}
input: {
var a = 0;
function f() {
return a++;
}
f() ? a += 2 : a += 4;
console.log(a);
}
expect: {
var a = 0;
function f(){
return a++;
}
f() ? a += 2 : a += 4;
console.log(a);
}
expect_stdout: true
}

166
test/compress/const.js Normal file
View File

@@ -0,0 +1,166 @@
issue_1191: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(rot) {
const rotTol = 5;
if (rot < -rotTol || rot > rotTol)
bar();
baz();
}
}
expect: {
function foo(rot) {
(rot < -5 || rot > 5) && bar();
baz();
}
}
}
issue_1194: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function f1() {const a = "X"; return a + a;}
function f2() {const aa = "X"; return aa + aa;}
function f3() {const aaa = "X"; return aaa + aaa;}
}
expect: {
function f1(){return"XX"}
function f2(){return"XX"}
function f3(){return"XX"}
}
}
issue_1396: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(a) {
const VALUE = 1;
console.log(2 | VALUE);
console.log(VALUE + 1);
console.log(VALUE);
console.log(a & VALUE);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
const CONSTANT = "abc";
console.log(CONSTANT + CONSTANT + CONSTANT + CONSTANT + CONSTANT);
}
}
expect: {
function foo(a) {
console.log(3);
console.log(2);
console.log(1);
console.log(1 & a);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
console.log("abcabcabcabcabc");
}
}
}
unused_regexp_literal: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
}
input: {
function f(){ var a = /b/; }
}
expect: {
function f(){}
}
}
regexp_literal_not_const: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
(function(){
var result;
const s = 'acdabcdeabbb';
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec(s)) {
console.log(result[0]);
}
})();
}
expect: {
(function() {
var result;
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
})();
}
expect_stdout: true
}

View File

@@ -1,206 +1,216 @@
dead_code_1: { dead_code_1: {
options = { options = {
dead_code: true dead_code: true
}; };
input: { input: {
function f() { function f() {
a(); a();
b(); b();
x = 10; x = 10;
return; return;
if (x) { if (x) {
y(); y();
} }
} }
} }
expect: { expect: {
function f() { function f() {
a(); a();
b(); b();
x = 10; x = 10;
return; return;
} }
} }
} }
dead_code_2_should_warn: { dead_code_2_should_warn: {
options = { options = {
dead_code: true dead_code: true
}; };
input: { input: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw "foo"; throw "foo";
// completely discarding the `if` would introduce some // completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2 // bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope. // we copy any declarations to the upper scope.
if (x) { if (x) {
y(); y();
var x; var x;
function g(){}; function g(){};
// but nested declarations should not be kept. // but nested declarations should not be kept.
(function(){ (function(){
var q; var q;
function y(){}; function y(){};
})(); })();
} }
} }
} }
expect: { expect: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw "foo"; throw "foo";
var x; var x;
function g(){}; function g(){};
} }
} }
} }
dead_code_constant_boolean_should_warn_more: { dead_code_constant_boolean_should_warn_more: {
options = { options = {
dead_code : true, dead_code : true,
loops : true, loops : true,
booleans : true, booleans : true,
conditionals : true, conditionals : true,
evaluate : true evaluate : true,
}; side_effects : true,
input: { };
while (!((foo && bar) || (x + "0"))) { input: {
console.log("unreachable"); while (!((foo && bar) || (x + "0"))) {
var foo; console.log("unreachable");
function bar() {} var foo;
} function bar() {}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) { }
asdf(); for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
foo(); asdf();
var moo; foo();
} var moo;
} }
expect: { }
var foo; expect: {
function bar() {} var foo;
// nothing for the while function bar() {}
// as for the for, it should keep: // nothing for the while
var x = 10, y; // as for the for, it should keep:
var moo; var x = 10, y;
} var moo;
} }
expect_stdout: true
dead_code_const_declaration: { }
options = {
dead_code : true, dead_code_const_declaration: {
loops : true, options = {
booleans : true, dead_code : true,
conditionals : true, loops : true,
evaluate : true booleans : true,
}; conditionals : true,
input: { evaluate : true,
var unused; reduce_vars : true,
const CONST_FOO = false; };
if (CONST_FOO) { input: {
console.log("unreachable"); var unused;
var moo; const CONST_FOO = false;
function bar() {} if (CONST_FOO) {
} console.log("unreachable");
} var moo;
expect: { function bar() {}
var unused; }
const CONST_FOO = !1; }
var moo; expect: {
function bar() {} var unused;
} const CONST_FOO = !1;
} var moo;
function bar() {}
dead_code_const_annotation: { }
options = { expect_stdout: true
dead_code : true, }
loops : true,
booleans : true, dead_code_const_annotation: {
conditionals : true, options = {
evaluate : true dead_code : true,
}; loops : true,
input: { booleans : true,
var unused; conditionals : true,
/** @const */ var CONST_FOO_ANN = false; evaluate : true,
if (CONST_FOO_ANN) { reduce_vars : true,
console.log("unreachable"); toplevel : true,
var moo; };
function bar() {} input: {
} var unused;
} /** @const */ var CONST_FOO_ANN = false;
expect: { if (CONST_FOO_ANN) {
var unused; console.log("unreachable");
var CONST_FOO_ANN = !1; var moo;
var moo; function bar() {}
function bar() {} }
} }
} expect: {
var unused;
dead_code_const_annotation_regex: { var CONST_FOO_ANN = !1;
options = { var moo;
dead_code : true, function bar() {}
loops : true, }
booleans : true, expect_stdout: true
conditionals : true, }
evaluate : true
}; dead_code_const_annotation_regex: {
input: { options = {
var unused; dead_code : true,
// @constraint this shouldn't be a constant loops : true,
var CONST_FOO_ANN = false; booleans : true,
if (CONST_FOO_ANN) { conditionals : true,
console.log("reachable"); evaluate : true
} };
} input: {
expect: { var unused;
var unused; // @constraint this shouldn't be a constant
var CONST_FOO_ANN = !1; var CONST_FOO_ANN = false;
CONST_FOO_ANN && console.log('reachable'); if (CONST_FOO_ANN) {
} console.log("reachable");
} }
}
dead_code_const_annotation_complex_scope: { expect: {
options = { var unused;
dead_code : true, var CONST_FOO_ANN = !1;
loops : true, CONST_FOO_ANN && console.log('reachable');
booleans : true, }
conditionals : true, expect_stdout: true
evaluate : true }
};
input: { dead_code_const_annotation_complex_scope: {
var unused_var; options = {
/** @const */ var test = 'test'; dead_code : true,
// @const loops : true,
var CONST_FOO_ANN = false; booleans : true,
var unused_var_2; conditionals : true,
if (CONST_FOO_ANN) { evaluate : true,
console.log("unreachable"); reduce_vars : true,
var moo; toplevel : true,
function bar() {} };
} input: {
if (test === 'test') { var unused_var;
var beef = 'good'; /** @const */ var test = 'test';
/** @const */ var meat = 'beef'; // @const
var pork = 'bad'; var CONST_FOO_ANN = false;
if (meat === 'pork') { var unused_var_2;
console.log('also unreachable'); if (CONST_FOO_ANN) {
} else if (pork === 'good') { console.log("unreachable");
console.log('reached, not const'); var moo;
} function bar() {}
} }
} if (test === 'test') {
expect: { var beef = 'good';
var unused_var; /** @const */ var meat = 'beef';
var test = 'test'; var pork = 'bad';
var CONST_FOO_ANN = !1; if (meat === 'pork') {
var unused_var_2; console.log('also unreachable');
var moo; } else if (pork === 'good') {
function bar() {} console.log('reached, not const');
var beef = 'good'; }
var meat = 'beef'; }
var pork = 'bad'; }
'good' === pork && 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';
}
expect_stdout: true
}

View File

@@ -1,24 +1,24 @@
drop_console_1: { drop_console_1: {
options = {}; options = {};
input: { input: {
console.log('foo'); console.log('foo');
console.log.apply(console, arguments); console.log.apply(console, arguments);
} }
expect: { expect: {
console.log('foo'); console.log('foo');
console.log.apply(console, arguments); console.log.apply(console, arguments);
} }
} }
drop_console_1: { drop_console_2: {
options = { drop_console: true }; options = { drop_console: true };
input: { input: {
console.log('foo'); console.log('foo');
console.log.apply(console, arguments); console.log.apply(console, arguments);
} }
expect: { expect: {
// with regular compression these will be stripped out as well // with regular compression these will be stripped out as well
void 0; void 0;
void 0; void 0;
} }
} }

View File

@@ -177,3 +177,631 @@ keep_fnames: {
} }
} }
} }
drop_assign: {
options = { unused: true };
input: {
function f1() {
var a;
a = 1;
}
function f2() {
var a = 1;
a = 2;
}
function f3(a) {
a = 1;
}
function f4() {
var a;
return a = 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
expect: {
function f1() {
1;
}
function f2() {
2;
}
function f3(a) {
1;
}
function f4() {
return 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
}
keep_assign: {
options = { unused: "keep_assign" };
input: {
function f1() {
var a;
a = 1;
}
function f2() {
var a = 1;
a = 2;
}
function f3(a) {
a = 1;
}
function f4() {
var a;
return a = 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
expect: {
function f1() {
var a;
a = 1;
}
function f2() {
var a = 1;
a = 2;
}
function f3(a) {
a = 1;
}
function f4() {
var a;
return a = 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
}
drop_toplevel_funcs: {
options = { toplevel: "funcs", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, b = 1, c = g;
a = 2;
function g() {}
console.log(b = 3);
}
}
drop_toplevel_vars: {
options = { toplevel: "vars", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var c = g;
function f(d) {
return function() {
c = 2;
}
}
2;
function g() {}
function h() {}
console.log(3);
}
}
drop_toplevel_vars_fargs: {
options = { keep_fargs: false, toplevel: "vars", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var c = g;
function f() {
return function() {
c = 2;
}
}
2;
function g() {}
function h() {}
console.log(3);
}
}
drop_toplevel_all: {
options = { toplevel: true, unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
2;
console.log(3);
}
}
drop_toplevel_retain: {
options = { top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_retain_array: {
options = { top_retain: [ "f", "a", "o" ], unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_retain_regex: {
options = { top_retain: /^[fao]$/, unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_all_retain: {
options = { toplevel: true, top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_funcs_retain: {
options = { toplevel: "funcs", top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(b = 3);
}
}
drop_toplevel_vars_retain: {
options = { toplevel: "vars", top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(3);
}
}
drop_toplevel_keep_assign: {
options = { toplevel: true, unused: "keep_assign" };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, b = 1;
a = 2;
console.log(b = 3);
}
}
drop_fargs: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a) {
var b = a;
}
}
expect: {
function f() {}
}
}
drop_fnames: {
options = {
keep_fnames: false,
unused: true,
}
input: {
function f() {
return function g() {
var a = g;
};
}
}
expect: {
function f() {
return function() {};
}
}
}
global_var: {
options = {
side_effects: true,
unused: true,
}
input: {
var a;
function foo(b) {
a;
b;
c;
typeof c === "undefined";
c + b + a;
b && b.ar();
return b;
}
}
expect: {
var a;
function foo(b) {
c;
c;
b && b.ar();
return b;
}
}
}
iife: {
options = {
side_effects: true,
unused: true,
}
input: {
function f() {
var a;
~function() {}(b);
}
}
expect: {
function f() {
b;
}
}
}
drop_value: {
options = {
side_effects: true,
}
input: {
(1, [2, foo()], 3, {a:1, b:bar()});
}
expect: {
foo(), bar();
}
}
const_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
const b = 2;
return 1 + b;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
expect: {
function f() {
return 3;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
}
issue_1539: {
options = {
cascade: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f() {
var a, b;
a = b = 42;
return a;
}
}
expect: {
function f() {
return 42;
}
}
}
vardef_value: {
options = {
keep_fnames: false,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function g(){
return x();
}
var a = g();
return a(42);
}
}
expect: {
function f() {
var a = function(){
return x();
}();
return a(42);
}
}
}
assign_binding: {
options = {
cascade: true,
side_effects: true,
unused: true,
}
input: {
function f() {
var a;
a = f.g, a();
}
}
expect: {
function f() {
(0, f.g)();
}
}
}
assign_chain: {
options = {
unused: true,
}
input: {
function f() {
var a, b;
x = a = y = b = 42;
}
}
expect: {
function f() {
x = y = 42;
}
}
}
issue_1583: {
options = {
keep_fargs: true,
reduce_vars: true,
unused: true,
}
input: {
function m(t) {
(function(e) {
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
expect: {
function m(t) {
(function(e) {
t = (function() {
return (function(a) {
return a;
})(function(a) {});
})();
})();
}
}
}
issue_1656: {
options = {
toplevel: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
for(var a=0;;);
}
expect_exact: "for (;;) ;"
}

View File

@@ -1,3 +1,187 @@
and: {
options = {
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;
}
}
or: {
options = {
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;
}
}
unary_prefix: {
options = {
evaluate: true
}
input: {
a = !0 && b;
a = !0 || b;
a = ~1 && b;
a = ~1 || b;
a = -2 && b;
a = -2 || b;
a = +3 && b;
a = +3 || b;
}
expect: {
a = b;
a = !0;
a = b;
a = -2;
a = b;
a = -2;
a = b;
a = 3;
}
}
negative_zero: { negative_zero: {
options = { evaluate: true } options = { evaluate: true }
input: { input: {
@@ -16,6 +200,7 @@ negative_zero: {
1 / (-0) 1 / (-0)
); );
} }
expect_stdout: true
} }
positive_zero: { positive_zero: {
@@ -36,4 +221,584 @@ positive_zero: {
1 / (0) 1 / (0)
); );
} }
expect_stdout: true
}
unsafe_constant: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
true.a,
false.a,
null.a,
undefined.a
);
}
expect: {
console.log(
true.a,
false.a,
null.a,
(void 0).a
);
}
expect_stdout: true
}
unsafe_object: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:1}) + 1,
({a:1}).a + 1,
({a:1}).b + 1,
({a:1}).a.b + 1
);
}
expect: {
console.log(
({a:1}) + 1,
2,
({a:1}).b + 1,
1..b + 1
);
}
expect_stdout: true
}
unsafe_object_nested: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
({a:{b:1}}).a.b + 1
);
}
expect: {
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
2
);
}
expect_stdout: true
}
unsafe_object_complex: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:{b:1},b:1}) + 1,
({a:{b:1},b:1}).a + 1,
({a:{b:1},b:1}).b + 1,
({a:{b:1},b:1}).a.b + 1
);
}
expect: {
console.log(
({a:{b:1},b:1}) + 1,
({a:{b:1},b:1}).a + 1,
2,
2
);
}
expect_stdout: true
}
unsafe_object_repeated: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:{b:1},a:1}) + 1,
({a:{b:1},a:1}).a + 1,
({a:{b:1},a:1}).b + 1,
({a:{b:1},a:1}).a.b + 1
);
}
expect: {
console.log(
({a:{b:1},a:1}) + 1,
2,
({a:{b:1},a:1}).b + 1,
1..b + 1
);
}
expect_stdout: true
}
unsafe_object_accessor: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
function f() {
var a = {
get b() {},
set b() {}
};
return {a:a};
}
}
expect: {
function f() {
var a = {
get b() {},
set b() {}
};
return {a:a};
}
}
}
unsafe_function: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:{b:1},b:function(){}}) + 1,
({a:{b:1},b:function(){}}).a + 1,
({a:{b:1},b:function(){}}).b + 1,
({a:{b:1},b:function(){}}).a.b + 1
);
}
expect: {
console.log(
({a:{b:1},b:function(){}}) + 1,
({a:{b:1},b:function(){}}).a + 1,
({a:{b:1},b:function(){}}).b + 1,
({a:{b:1},b:function(){}}).a.b + 1
);
}
expect_stdout: true
}
unsafe_integer_key: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({0:1}) + 1,
({0:1})[0] + 1,
({0:1})["0"] + 1,
({0:1})[1] + 1,
({0:1})[0][1] + 1,
({0:1})[0]["1"] + 1
);
}
expect: {
console.log(
({0:1}) + 1,
2,
2,
({0:1})[1] + 1,
1[1] + 1,
1["1"] + 1
);
}
expect_stdout: true
}
unsafe_integer_key_complex: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({0:{1:1},1:1}) + 1,
({0:{1:1},1:1})[0] + 1,
({0:{1:1},1:1})["0"] + 1,
({0:{1:1},1:1})[1] + 1,
({0:{1:1},1:1})[0][1] + 1,
({0:{1:1},1:1})[0]["1"] + 1
);
}
expect: {
console.log(
({0:{1:1},1:1}) + 1,
"[object Object]1",
"[object Object]1",
2,
2,
2
);
}
expect_stdout: true
}
unsafe_float_key: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({2.72:1}) + 1,
({2.72:1})[2.72] + 1,
({2.72:1})["2.72"] + 1,
({2.72:1})[3.14] + 1,
({2.72:1})[2.72][3.14] + 1,
({2.72:1})[2.72]["3.14"] + 1
);
}
expect: {
console.log(
({2.72:1}) + 1,
2,
2,
({2.72:1})[3.14] + 1,
1[3.14] + 1,
1["3.14"] + 1
);
}
expect_stdout: true
}
unsafe_float_key_complex: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({2.72:{3.14:1},3.14:1}) + 1,
({2.72:{3.14:1},3.14:1})[2.72] + 1,
({2.72:{3.14:1},3.14:1})["2.72"] + 1,
({2.72:{3.14:1},3.14:1})[3.14] + 1,
({2.72:{3.14:1},3.14:1})[2.72][3.14] + 1,
({2.72:{3.14:1},3.14:1})[2.72]["3.14"] + 1
);
}
expect: {
console.log(
"[object Object]1",
"[object Object]1",
"[object Object]1",
2,
2,
2
);
}
expect_stdout: true
}
unsafe_array: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
[1, , 3][1],
[1, 2, 3, a] + 1,
[1, 2, 3, 4] + 1,
[1, 2, 3, a][0] + 1,
[1, 2, 3, 4][0] + 1,
[1, 2, 3, 4][6 - 5] + 1,
[1, , 3, 4][6 - 5] + 1,
[[1, 2], [3, 4]][0] + 1,
[[1, 2], [3, 4]][6 - 5][1] + 1,
[[1, 2], , [3, 4]][6 - 5][1] + 1
);
}
expect: {
console.log(
void 0,
[1, 2, 3, a] + 1,
"1,2,3,41",
[1, 2, 3, a][0] + 1,
2,
3,
NaN,
"1,21",
5,
(void 0)[1] + 1
);
}
expect_stdout: true
}
unsafe_string: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234" + 1,
"1234"[0] + 1,
"1234"[6 - 5] + 1,
("12" + "34")[0] + 1,
("12" + "34")[6 - 5] + 1,
[1, 2, 3, 4].join("")[0] + 1
);
}
expect: {
console.log(
"12341",
"11",
"21",
"11",
"21",
"11"
);
}
expect_stdout: true
}
unsafe_array_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
[1, 2, 3, 4].a + 1,
[1, 2, 3, 4]["a"] + 1,
[1, 2, 3, 4][3.14] + 1
);
}
expect: {
console.log(
[1, 2, 3, 4].a + 1,
[1, 2, 3, 4]["a"] + 1,
[1, 2, 3, 4][3.14] + 1
);
}
expect_stdout: true
}
unsafe_string_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234".a + 1,
"1234"["a"] + 1,
"1234"[3.14] + 1
);
}
expect: {
console.log(
"1234".a + 1,
"1234"["a"] + 1,
"1234"[3.14] + 1
);
}
expect_stdout: true
}
unsafe_prototype_function: {
options = {
evaluate : true,
unsafe : true
}
input: {
var a = ({valueOf: 0}) < 1;
var b = ({toString: 0}) < 1;
var c = ({valueOf: 0}) + "";
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf();
var h = ({toString: 0}).toString();
}
expect: {
var a = ({valueOf: 0}) < 1;
var b = ({toString: 0}) < 1;
var c = ({valueOf: 0}) + "";
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf();
var h = "" + ({toString: 0});
}
}
call_args: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
const a = 1;
console.log(a);
+function(a) {
return a;
}(a);
}
expect: {
const a = 1;
console.log(1);
+(1, 1);
}
expect_stdout: true
}
call_args_drop_param: {
options = {
evaluate: true,
keep_fargs: false,
reduce_vars: true,
unused: true,
}
input: {
const a = 1;
console.log(a);
+function(a) {
return a;
}(a, b);
}
expect: {
const a = 1;
console.log(1);
+(b, 1);
}
expect_stdout: true
}
in_boolean_context: {
options = {
booleans: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
console.log(
!42,
!"foo",
![1, 2],
!/foo/,
!b(42),
!b("foo"),
!b([1, 2]),
!b(/foo/),
![1, foo()],
![1, foo(), 2]
);
}
expect: {
console.log(
!1,
!1,
!1,
!1,
!b(42),
!b("foo"),
!b([1, 2]),
!b(/foo/),
![1, foo()],
(foo(), !1)
);
}
expect_stdout: true
}
unsafe_charAt: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234" + 1,
"1234".charAt(0) + 1,
"1234".charAt(6 - 5) + 1,
("12" + "34").charAt(0) + 1,
("12" + "34").charAt(6 - 5) + 1,
[1, 2, 3, 4].join("").charAt(0) + 1
);
}
expect: {
console.log(
"12341",
"11",
"21",
"11",
"21",
"11"
);
}
expect_stdout: true
}
unsafe_charAt_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234".charAt() + 1,
"1234".charAt("a") + 1,
"1234".charAt(3.14) + 1
);
}
expect: {
console.log(
"11",
"11",
"41"
);
}
expect_stdout: true
}
unsafe_charAt_noop: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
s.charAt(0),
"string".charAt(x)
);
}
expect: {
console.log(
s.charAt(0),
"string".charAt(x)
);
}
}
issue_1649: {
options = {
evaluate: true,
}
input: {
console.log(-1 + -1);
}
expect: {
console.log(-2);
}
expect_stdout: "-2";
} }

View File

@@ -0,0 +1,95 @@
non_ascii_function_identifier_name: {
input: {
function fooλ(δλ) {}
function λ(δλ) {}
(function λ(δλ) {})()
}
expect_exact: "function fooλ(δλ){}function λ(δλ){}(function λ(δλ){})();"
}
iifes_returning_constants_keep_fargs_true: {
options = {
keep_fargs : true,
side_effects : true,
evaluate : true,
unused : true,
dead_code : true,
conditionals : true,
comparisons : true,
booleans : true,
if_return : true,
join_vars : true,
reduce_vars : true,
cascade : true,
}
input: {
(function(){ return -1.23; }());
console.log( function foo(){ return "okay"; }() );
console.log( function foo(x, y, z){ return 123; }() );
console.log( function(x, y, z){ return z; }() );
console.log( function(x, y, z){ if (x) return y; return z; }(1, 2, 3) );
console.log( function(x, y){ return x * y; }(2, 3) );
console.log( function(x, y){ return x * y; }(2, 3, a(), b()) );
}
expect: {
console.log("okay");
console.log(123);
console.log(void 0);
console.log(2);
console.log(6);
console.log((a(), b(), 6));
}
expect_stdout: true
}
iifes_returning_constants_keep_fargs_false: {
options = {
keep_fargs : false,
side_effects : true,
evaluate : true,
unused : true,
dead_code : true,
conditionals : true,
comparisons : true,
booleans : true,
if_return : true,
join_vars : true,
reduce_vars : true,
cascade : true,
}
input: {
(function(){ return -1.23; }());
console.log( function foo(){ return "okay"; }() );
console.log( function foo(x, y, z){ return 123; }() );
console.log( function(x, y, z){ return z; }() );
console.log( function(x, y, z){ if (x) return y; return z; }(1, 2, 3) );
console.log( function(x, y){ return x * y; }(2, 3) );
console.log( function(x, y){ return x * y; }(2, 3, a(), b()) );
}
expect: {
console.log("okay");
console.log(123);
console.log(void 0);
console.log(2);
console.log(6);
console.log((a(), b(), 6));
}
expect_stdout: true
}
issue_485_crashing_1530: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
(function(a) {
if (true) return;
var b = 42;
})(this);
}
expect: {
this, void 0;
}
}

View File

@@ -0,0 +1,147 @@
must_replace: {
options = {
global_defs: {
D: "foo bar",
}
}
input: {
console.log(D);
}
expect: {
console.log("foo bar");
}
}
keyword: {
options = {
global_defs: {
undefined: 0,
NaN: 1,
Infinity: 2,
},
}
input: {
console.log(undefined, NaN, Infinity);
}
expect: {
console.log(0, 1, 2);
}
}
object: {
options = {
evaluate: true,
global_defs: {
CONFIG: {
DEBUG: [ 0 ],
VALUE: 42,
},
},
unsafe: true,
}
input: {
function f(CONFIG) {
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function h() {
return CONFIG.VALUE;
}
if (CONFIG.DEBUG[0])
console.debug("foo");
}
expect: {
function f(CONFIG) {
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
return CONFIG.VALUE;
}
function h() {
return 42;
}
if (0)
console.debug("foo");
}
}
expanded: {
options = {
global_defs: {
"CONFIG.DEBUG": [ 0 ],
"CONFIG.VALUE": 42,
},
}
input: {
function f(CONFIG) {
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function h() {
return CONFIG.VALUE;
}
if (CONFIG.DEBUG[0])
console.debug("foo");
}
expect: {
function f(CONFIG) {
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
return CONFIG.VALUE;
}
function h() {
return 42;
}
if ([0][0])
console.debug("foo");
}
}
mixed: {
options = {
evaluate: true,
global_defs: {
"CONFIG.VALUE": 42,
"FOO.BAR": "moo",
},
properties: true,
}
input: {
const FOO = { BAR: 0 };
console.log(FOO.BAR);
console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE);
console.log(++CONFIG["VAL" + "UE"]);
console.log(++DEBUG[CONFIG.VALUE]);
CONFIG.VALUE.FOO = "bar";
console.log(CONFIG);
}
expect: {
const FOO = { BAR: 0 };
console.log("moo");
console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE);
console.log(++CONFIG.VALUE);
console.log(++DEBUG[42]);
CONFIG.VALUE.FOO = "bar";
console.log(CONFIG);
}
expect_warnings: [
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
]
}

View File

@@ -0,0 +1,90 @@
statements: {
options = {
hoist_funs: false,
hoist_vars: true,
}
input: {
function f() {
var a = 1;
var b = 2;
var c = 3;
function g() {}
return g(a, b, c);
}
}
expect: {
function f() {
var a = 1, b = 2, c = 3;
function g() {}
return g(a, b, c);
}
}
}
statements_funs: {
options = {
hoist_funs: true,
hoist_vars: true,
}
input: {
function f() {
var a = 1;
var b = 2;
var c = 3;
function g() {}
return g(a, b, c);
}
}
expect: {
function f() {
function g() {}
var a = 1, b = 2, c = 3;
return g(a, b, c);
}
}
}
sequences: {
options = {
hoist_funs: false,
hoist_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
function g() {}
var c = 3;
return g(a, b, c);
}
}
expect: {
function f() {
var c, a = 1, b = 2;
function g() {}
c = 3;
return g(a, b, c);
}
}
}
sequences_funs: {
options = {
hoist_funs: true,
hoist_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
function g() {}
var c = 3;
return g(a, b, c);
}
}
expect: {
function f() {
function g() {}
var a = 1, b = 2, c = 3;
return g(a, b, c);
}
}
}

View File

@@ -47,22 +47,6 @@ html_comment_in_greater_than_or_equal: {
expect_exact: "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: { html_comment_in_string_literal: {
input: { input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; } function f() { return "<!--HTML-->comment in<!--string literal-->"; }

View File

@@ -170,8 +170,51 @@ if_return_7: {
} }
} }
expect: { expect: {
// suboptimal function f(x){if(x)return!0;foo(),bar()}
function f(x){return!!x||(foo(),void bar())} }
}
if_return_8: {
options = {
if_return: true,
sequences: true,
conditionals: true,
side_effects : true,
}
input: {
function f(e) {
if (2 == e) return foo();
if (3 == e) return bar();
if (4 == e) return baz();
fail(e);
}
function g(e) {
if (a(e)) return foo();
if (b(e)) return bar();
if (c(e)) return baz();
fail(e);
}
function h(e) {
if (a(e)) return foo();
else if (b(e)) return bar();
else if (c(e)) return baz();
else fail(e);
}
function i(e) {
if (a(e)) return foo();
else if (b(e)) return bar();
else if (c(e)) return baz();
fail(e);
}
}
expect: {
function f(e){return 2==e?foo():3==e?bar():4==e?baz():void fail(e)}
function g(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)}
function h(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)}
function i(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)}
} }
} }
@@ -205,3 +248,57 @@ issue_1089: {
} }
} }
} }
issue_1437: {
options = {
if_return : true,
sequences : true,
conditionals : false
}
input: {
function x() {
if (a())
return b();
if (c())
return d();
else
e();
f();
}
}
expect: {
function x() {
if (a())
return b();
if (c())
return d();
else
e()
f();
}
}
}
issue_1437_conditionals: {
options = {
conditionals : true,
if_return : true,
sequences : true
}
input: {
function x() {
if (a())
return b();
if (c())
return d();
else
e();
f();
}
}
expect: {
function x() {
return a() ? b() : c() ? d() : (e(), f(), void 0);
}
}
}

View File

@@ -39,7 +39,7 @@ non_hoisted_function_after_return_2a: {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true, hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true, evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true, if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2 collapse_vars: false, passes: 2, warnings: "verbose"
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -75,7 +75,7 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]", "WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]" "WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
] ]
} }
@@ -114,8 +114,5 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:95,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:97,16]"
] ]
} }

View File

@@ -13,7 +13,8 @@ const_declaration: {
const_pragma: { const_pragma: {
options = { options = {
evaluate: true evaluate: true,
reduce_vars: true,
}; };
input: { input: {
@@ -27,7 +28,8 @@ const_pragma: {
// for completeness' sake // for completeness' sake
not_const: { not_const: {
options = { options = {
evaluate: true evaluate: true,
reduce_vars: true,
}; };
input: { input: {

View File

@@ -1,25 +0,0 @@
typeof_eq_undefined: {
options = {
comparisons: true
};
input: { a = typeof b.c != "undefined" }
expect: { a = "undefined" != typeof b.c }
}
typeof_eq_undefined_unsafe: {
options = {
comparisons: true,
unsafe: true
};
input: { a = typeof b.c != "undefined" }
expect: { a = void 0 !== b.c }
}
typeof_eq_undefined_unsafe2: {
options = {
comparisons: true,
unsafe: true
};
input: { a = "undefined" != typeof b.c }
expect: { a = void 0 !== b.c }
}

View File

@@ -49,4 +49,3 @@ mangle_keep_fnames_true: {
} }
} }
} }

176
test/compress/issue-1261.js Normal file
View File

@@ -0,0 +1,176 @@
pure_function_calls: {
options = {
evaluate : true,
conditionals : true,
comparisons : true,
side_effects : true,
booleans : true,
unused : true,
if_return : true,
join_vars : true,
cascade : true,
negate_iife : true,
}
input: {
// pure top-level IIFE will be dropped
// @__PURE__ - comment
(function() {
console.log("iife0");
})();
// pure top-level IIFE assigned to unreferenced var will not be dropped
var iife1 = /*@__PURE__*/(function() {
console.log("iife1");
function iife1() {}
return iife1;
})();
(function(){
// pure IIFE in function scope assigned to unreferenced var will be dropped
var iife2 = /*#__PURE__*/(function() {
console.log("iife2");
function iife2() {}
return iife2;
})();
})();
// comment #__PURE__ comment
bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g();
}
expect: {
var iife1 = function() {
console.log("iife1");
function iife1() {}
return iife1;
}();
baz(), quux();
a.b(), f.g();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:17,8]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:17,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:30,37]",
"WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:30,16]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:28,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:38,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:39,31]",
]
}
pure_function_calls_toplevel: {
options = {
evaluate : true,
conditionals : true,
comparisons : true,
side_effects : true,
booleans : true,
unused : true,
if_return : true,
join_vars : true,
cascade : true,
negate_iife : true,
toplevel : true,
}
input: {
// pure top-level IIFE will be dropped
// @__PURE__ - comment
(function() {
console.log("iife0");
})();
// pure top-level IIFE assigned to unreferenced var will be dropped
var iife1 = /*@__PURE__*/(function() {
console.log("iife1");
function iife1() {}
return iife1;
})();
(function(){
// pure IIFE in function scope assigned to unreferenced var will be dropped
var iife2 = /*#__PURE__*/(function() {
console.log("iife2");
function iife2() {}
return iife2;
})();
})();
// comment #__PURE__ comment
bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g();
}
expect: {
baz(), quux();
a.b(), f.g();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:79,8]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:79,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:92,37]",
"WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:92,16]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:90,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:101,31]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:84,33]",
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
]
}
should_warn: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
side_effects: true,
}
input: {
/* @__PURE__ */(function(){x})(), void/* @__PURE__ */(function(){y})();
/* @__PURE__ */(function(){x})() || true ? foo() : bar();
true || /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() && false ? foo() : bar();
false && /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() + "foo" ? bar() : baz();
"foo" + /* @__PURE__ */(function(){y})() ? bar() : baz();
/* @__PURE__ */(function(){x})() ? foo() : foo();
[/* @__PURE__ */(function(){x})()] ? foo() : bar();
!{ foo: /* @__PURE__ */(function(){x})() } ? bar() : baz();
}
expect: {
foo();
foo();
bar();
bar();
bar();
bar();
foo();
foo();
baz();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:128,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:129,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]",
"WARN: Condition always true [test/compress/issue-1261.js:129,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:130,8]",
"WARN: Condition always true [test/compress/issue-1261.js:130,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:131,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:131,23]",
"WARN: Condition always false [test/compress/issue-1261.js:131,23]",
"WARN: Boolean && always false [test/compress/issue-1261.js:132,8]",
"WARN: Condition always false [test/compress/issue-1261.js:132,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:133,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:133,23]",
"WARN: Condition always true [test/compress/issue-1261.js:133,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:134,31]",
"WARN: Condition always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:135,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:136,24]",
"WARN: Condition always true [test/compress/issue-1261.js:136,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,31]",
"WARN: Condition always false [test/compress/issue-1261.js:137,8]",
]
}

View File

@@ -35,7 +35,7 @@ string_plus_optimization: {
throw "nope"; throw "nope";
} }
try { try {
console.log('0' + throwing_function() ? "yes" : "no"); console.log((throwing_function(), "yes"));
} catch (ex) { } catch (ex) {
console.log(ex); console.log(ex);
} }
@@ -46,4 +46,5 @@ string_plus_optimization: {
} }
foo(); foo();
} }
expect_stdout: true
} }

View File

@@ -14,6 +14,7 @@ issue_1321_no_debug: {
x["a"] = 2 * x.b; x["a"] = 2 * x.b;
console.log(x.b, x["a"]); console.log(x.b, x["a"]);
} }
expect_stdout: true
} }
issue_1321_debug: { issue_1321_debug: {
@@ -33,6 +34,7 @@ issue_1321_debug: {
x["_$foo$_"] = 2 * x.a; x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]); console.log(x.a, x["_$foo$_"]);
} }
expect_stdout: true
} }
issue_1321_with_quoted: { issue_1321_with_quoted: {
@@ -51,4 +53,5 @@ issue_1321_with_quoted: {
x["b"] = 2 * x.a; x["b"] = 2 * x.a;
console.log(x.a, x["b"]); console.log(x.a, x["b"]);
} }
expect_stdout: true
} }

151
test/compress/issue-1431.js Normal file
View File

@@ -0,0 +1,151 @@
level_zero: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
function n(a) {
return a * a;
}
return function() {
return x;
};
}
}
expect: {
function f(r) {
function n(n) {
return n * n;
}
return function() {
return r;
};
}
}
}
level_one: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
return function() {
function n(a) {
return a * a;
}
return x(n);
};
}
}
expect: {
function f(r) {
return function() {
function n(n) {
return n * n;
}
return r(n);
};
}
}
}
level_two: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
return function() {
function r(a) {
return a * a;
}
return function() {
function n(a) {
return a * a;
}
return x(n);
};
};
}
}
expect: {
function f(t) {
return function() {
function r(n) {
return n * n;
}
return function() {
function n(n) {
return n * n;
}
return t(n);
};
};
}
}
}
level_three: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
return function() {
function r(a) {
return a * a;
}
return [
function() {
function t(a) {
return a * a;
}
return t;
},
function() {
function n(a) {
return a * a;
}
return x(n);
}
];
};
}
}
expect: {
function f(t) {
return function() {
function r(n) {
return n * n;
}
return [
function() {
function t(n) {
return n * n;
}
return t;
},
function() {
function n(n) {
return n * n;
}
return t(n);
}
];
};
}
}
}

View File

@@ -0,0 +1,61 @@
// tests assume that variable `undefined` not redefined and has `void 0` as value
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true
}
mangle = {}
input: {
function f(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}
}
expect: {
function f(n) {
return function() {
return a ? b : c ? d : n;
};
}
}
}
keep_fnames: {
options = {
conditionals: true,
if_return: true,
unsafe: true
}
mangle = {
keep_fnames: true
}
input: {
function f(undefined) {
return function() {
function n(a) {
return a * a;
}
if (a)
return b;
if (c)
return d;
};
}
}
expect: {
function f(r) {
return function() {
function n(n) {
return n * n;
}
return a ? b : c ? d : r;
};
}
}
}

View File

@@ -0,0 +1,71 @@
typeof_eq_undefined: {
options = {
comparisons: true
}
input: {
var a = typeof b != "undefined";
b = typeof a != "undefined";
var c = typeof d.e !== "undefined";
var f = "undefined" === typeof g;
g = "undefined" === typeof f;
var h = "undefined" == typeof i.j;
}
expect: {
var a = "undefined" != typeof b;
b = void 0 !== a;
var c = void 0 !== d.e;
var f = "undefined" == typeof g;
g = void 0 === f;
var h = void 0 === i.j;
}
}
typeof_eq_undefined_ie8: {
options = {
comparisons: true,
screw_ie8: false
}
input: {
var a = typeof b != "undefined";
b = typeof a != "undefined";
var c = typeof d.e !== "undefined";
var f = "undefined" === typeof g;
g = "undefined" === typeof f;
var h = "undefined" == typeof i.j;
}
expect: {
var a = "undefined" != typeof b;
b = void 0 !== a;
var c = "undefined" != typeof d.e;
var f = "undefined" == typeof g;
g = void 0 === f;
var h = "undefined" == typeof i.j;
}
}
undefined_redefined: {
options = {
comparisons: true
}
input: {
function f(undefined) {
var n = 1;
return typeof n == "undefined";
}
}
expect_exact: "function f(undefined){var n=1;return void 0===n}"
}
undefined_redefined_mangle: {
options = {
comparisons: true
}
mangle = {}
input: {
function f(undefined) {
var n = 1;
return typeof n == "undefined";
}
}
expect_exact: "function f(n){var r=1;return void 0===r}"
}

View File

@@ -0,0 +1,46 @@
else_with_empty_block: {
options = {}
input: {
if (x)
yes();
else {
}
}
expect_exact: "if(x)yes();"
}
else_with_empty_statement: {
options = {}
input: {
if (x)
yes();
else
;
}
expect_exact: "if(x)yes();"
}
conditional_false_stray_else_in_loop: {
options = {
evaluate : true,
comparisons : true,
booleans : true,
unused : true,
loops : true,
side_effects : true,
dead_code : true,
hoist_vars : true,
join_vars : true,
if_return : true,
cascade : true,
conditionals : false,
}
input: {
for (var i = 1; i <= 4; ++i) {
if (i <= 2) continue;
console.log(i);
}
}
expect_exact: "for(var i=1;i<=4;++i)if(!(i<=2))console.log(i);"
expect_stdout: true
}

View File

@@ -0,0 +1,19 @@
inner_reference: {
options = {
side_effects: true,
}
input: {
!function f(a) {
return a && f(a - 1) + a;
}(42);
!function g(a) {
return a;
}(42);
}
expect: {
!function f(a) {
return a && f(a - 1) + a;
}(42);
!void 0;
}
}

View File

@@ -0,0 +1,99 @@
screw_ie8: {
options = {
screw_ie8: true,
}
mangle = {
screw_ie8: true,
}
input: {
try { throw "foo"; } catch (x) { console.log(x); }
}
expect_exact: 'try{throw"foo"}catch(o){console.log(o)}'
expect_stdout: [
"foo"
]
}
support_ie8: {
options = {
screw_ie8: false,
}
mangle = {
screw_ie8: false,
}
input: {
try { throw "foo"; } catch (x) { console.log(x); }
}
expect_exact: 'try{throw"foo"}catch(x){console.log(x)}'
expect_stdout: "foo"
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: false,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}(1)());
}
expect: {
var a, c;
console.log(function(n) {
return function() {
return a ? b : c ? d : void 0;
};
}(1)());
}
expect_stdout: true
}
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}()());
}
expect: {
var a, c;
console.log(function(n) {
return function() {
return a ? b : c ? d : n;
};
}()());
}
expect_stdout: true
}
runtime_error: {
input: {
const a = 1;
console.log(a++);
}
expect: {
const a = 1;
console.log(a++);
}
expect_stdout: true
}

View File

@@ -0,0 +1,56 @@
chained_evaluation_1: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = 1;
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
(function() {
var c;
c = f(1);
c.bar = 1;
})();
})();
}
}
chained_evaluation_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = "long piece of string";
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
var a = "long piece of string";
(function() {
var c;
c = f(a);
c.bar = a;
})();
})();
}
}

View File

@@ -0,0 +1,88 @@
issue_1639_1: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
join_vars: true,
loops: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
var L1 = 5;
while (--L1 > 0) {
if ((--b), false) {
if (b) {
var ignore = 0;
}
}
}
console.log(a, b);
}
expect: {
for (var a = 100, b = 10, L1 = 5; --L1 > 0;)
if (--b, !1) var ignore = 0;
console.log(a, b);
}
expect_stdout: true
}
issue_1639_2: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
function f19() {
if (++a, false)
if (a)
if (++a);
}
f19();
console.log(a, b);
}
expect: {
var a = 100, b = 10;
function f19() {
++a, 1;
}
f19(),
console.log(a, b);
}
expect_stdout: true
}
issue_1639_3: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
a++ && false && a ? 0 : 0;
console.log(a, b);
}
expect: {
var a = 100, b = 10;
a++,
console.log(a, b);
}
expect_stdout: true
}

View File

@@ -0,0 +1,45 @@
f7: {
options = {
booleans: true,
cascade: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
drop_debugger: true,
evaluate: true,
hoist_funs: true,
if_return: true,
join_vars: true,
loops: true,
negate_iife: true,
passes: 3,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
var a = 100, b = 10;
function f22464() {
var brake146670 = 5;
while (((b = a) ? !a : ~a ? null : b += a) && --brake146670 > 0) {
}
}
f22464();
console.log(a, b);
}
expect_exact: [
"var a = 100, b = 10;",
"",
"!function() {",
" for (;b = a, !1; ) ;",
"}(), console.log(a, b);",
]
expect_stdout: true
}

View File

@@ -1,11 +1,70 @@
do_not_update_lhs: { do_not_update_lhs: {
options = { global_defs: { DEBUG: false } }; options = {
input: { DEBUG = false; } global_defs: { DEBUG: 0 }
expect: { DEBUG = false; } }
input: {
DEBUG++;
DEBUG += 1;
DEBUG = 1;
}
expect: {
DEBUG++;
DEBUG += 1;
DEBUG = 1;
}
} }
do_update_rhs: { do_update_rhs: {
options = { global_defs: { DEBUG: false } }; options = {
input: { MY_DEBUG = DEBUG; } global_defs: { DEBUG: 0 }
expect: { MY_DEBUG = false; } }
input: {
MY_DEBUG = DEBUG;
MY_DEBUG += DEBUG;
}
expect: {
MY_DEBUG = 0;
MY_DEBUG += 0;
}
}
mixed: {
options = {
evaluate: true,
global_defs: {
DEBUG: 0,
ENV: 1,
FOO: 2,
}
}
input: {
const ENV = 3;
var FOO = 4;
f(ENV * 10);
--FOO;
DEBUG = 1;
DEBUG++;
DEBUG += 1;
f(DEBUG);
x = DEBUG;
}
expect: {
const ENV = 3;
var FOO = 4;
f(10);
--FOO;
DEBUG = 1;
DEBUG++;
DEBUG += 1;
f(0);
x = 0;
}
expect_warnings: [
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:46,8]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:47,8]',
]
} }

View File

@@ -0,0 +1,55 @@
collapse: {
options = {
cascade: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var a;
a = typeof b === 'function' ? b() : b;
return a !== undefined && c();
}
function f2(b) {
var a;
b = c();
a = typeof b === 'function' ? b() : b;
return 'stirng' == typeof a && d();
}
function f3(c) {
var a;
a = b(a / 2);
if (a < 0) {
a++;
++c;
return c / 2;
}
}
function f4(c) {
var a;
a = b(a / 2);
if (a < 0) {
a++;
c++;
return c / 2;
}
}
}
expect: {
function f1() {
return void 0 !== ('function' === typeof b ? b() : b) && c();
}
function f2(b) {
return b = c(), 'stirng' == typeof ('function' === typeof b ? b() : b) && d();
}
function f3(c) {
var a;
if ((a = b(a / 2)) < 0) return a++, ++c / 2;
}
function f4(c) {
var a;
if ((a = b(a / 2)) < 0) return a++, ++c / 2;
}
}
}

320
test/compress/issue-640.js Normal file
View File

@@ -0,0 +1,320 @@
cond_5: {
options = {
conditionals: true,
expression: true,
}
input: {
if (some_condition()) {
if (some_other_condition()) {
do_something();
} else {
alternate();
}
} else {
alternate();
}
if (some_condition()) {
if (some_other_condition()) {
do_something();
}
}
}
expect: {
some_condition() && some_other_condition() ? do_something() : alternate();
if (some_condition() && some_other_condition()) do_something();
}
}
dead_code_const_annotation_regex: {
options = {
booleans : true,
conditionals : true,
dead_code : true,
evaluate : true,
expression : true,
loops : 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;
if (CONST_FOO_ANN) console.log('reachable');
}
expect_stdout: true
}
drop_console_2: {
options = {
drop_console: true,
expression: true,
}
input: {
console.log('foo');
console.log.apply(console, arguments);
}
expect: {
// with regular compression these will be stripped out as well
void 0;
void 0;
}
}
drop_value: {
options = {
expression: true,
side_effects: true,
}
input: {
(1, [2, foo()], 3, {a:1, b:bar()});
}
expect: {
foo(), {a:1, b:bar()};
}
}
wrongly_optimized: {
options = {
conditionals: true,
booleans: true,
evaluate: true,
expression: true,
}
input: {
function func() {
foo();
}
if (func() || true) {
bar();
}
}
expect: {
function func() {
foo();
}
// TODO: optimize to `func(), bar()`
if (func(), !0) bar();
}
}
negate_iife_1: {
options = {
expression: true,
negate_iife: true,
}
input: {
(function(){ stuff() })();
}
expect: {
(function(){ stuff() })();
}
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
(function(){ return t })() ? console.log(true) : console.log(false), function(){
console.log("something");
}();
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
(function(){ return t })() ? foo(true) : bar(false), function(){
console.log("something");
}();
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
(function(){ return t })() ? foo(true) : bar(false), function(){
console.log("something");
}();
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
expect_stdout: true
}
conditional: {
options = {
expression: true,
pure_funcs: [ "pure" ],
side_effects: true,
}
input: {
pure(1 | a() ? 2 & b() : 7 ^ c());
pure(1 | a() ? 2 & b() : 5);
pure(1 | a() ? 4 : 7 ^ c());
pure(1 | a() ? 4 : 5);
pure(3 ? 2 & b() : 7 ^ c());
pure(3 ? 2 & b() : 5);
pure(3 ? 4 : 7 ^ c());
pure(3 ? 4 : 5);
}
expect: {
1 | a() ? b() : c();
1 | a() && b();
1 | a() || c();
a();
3 ? b() : c();
3 && b();
3 || c();
pure(3 ? 4 : 5);
}
}
limit_1: {
options = {
expression: true,
sequences: 3,
}
input: {
a;
b;
c;
d;
e;
f;
g;
h;
i;
j;
k;
}
expect: {
// Turned into a single return statement
// so it can no longer be split into lines
a,b,c,d,e,f,g,h,i,j,k;
}
}
iife: {
options = {
expression: true,
sequences: true,
}
input: {
x = 42;
(function a() {})();
!function b() {}();
~function c() {}();
+function d() {}();
-function e() {}();
void function f() {}();
typeof function g() {}();
}
expect: {
x = 42, function a() {}(), function b() {}(), function c() {}(),
function d() {}(), function e() {}(), function f() {}(), typeof function g() {}();
}
}

View File

@@ -29,4 +29,5 @@ dont_mangle_arguments: {
})(5,6,7); })(5,6,7);
} }
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);" expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
expect_stdout: true
} }

View File

@@ -50,6 +50,7 @@ this_binding_conditionals: {
this_binding_collapse_vars: { this_binding_collapse_vars: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
}; };
input: { input: {
var c = a; c(); var c = a; c();

View File

@@ -42,6 +42,7 @@ eval_collapse_vars: {
eval("console.log(a);"); eval("console.log(a);");
})(eval); })(eval);
} }
expect_stdout: true
} }
eval_unused: { eval_unused: {

View File

@@ -82,7 +82,7 @@ issue979_test_negated_is_best: {
1!=a||2!=b||foo(); 1!=a||2!=b||foo();
} }
function f7() { function f7() {
return 1!=a&&2!=b?bar():void foo(); if(1!=a&&2!=b)return bar();foo()
} }
} }
} }

View File

@@ -9,6 +9,7 @@ labels_1: {
expect: { expect: {
foo || console.log("bar"); foo || console.log("bar");
} }
expect_stdout: true
} }
labels_2: { labels_2: {
@@ -40,6 +41,7 @@ labels_3: {
for (var i = 0; i < 5; ++i) for (var i = 0; i < 5; ++i)
i < 3 || console.log(i); i < 3 || console.log(i);
} }
expect_stdout: true
} }
labels_4: { labels_4: {
@@ -54,6 +56,7 @@ labels_4: {
for (var i = 0; i < 5; ++i) for (var i = 0; i < 5; ++i)
i < 3 || console.log(i); i < 3 || console.log(i);
} }
expect_stdout: true
} }
labels_5: { labels_5: {

View File

@@ -166,6 +166,7 @@ keep_collapse_const_in_own_block_scope: {
console.log(i); console.log(i);
console.log(c); console.log(c);
} }
expect_stdout: true
} }
keep_collapse_const_in_own_block_scope_2: { keep_collapse_const_in_own_block_scope_2: {
@@ -186,4 +187,274 @@ keep_collapse_const_in_own_block_scope_2: {
console.log(i); console.log(i);
console.log(c); console.log(c);
} }
expect_stdout: true
}
evaluate: {
options = {
loops: true,
dead_code: true,
evaluate: true,
};
input: {
while (true) {
a();
}
while (false) {
b();
}
do {
c();
} while (true);
do {
d();
} while (false);
}
expect: {
for(;;)
a();
for(;;)
c();
// rule disabled due to issue_1532
do d(); while (false);
}
}
issue_1532: {
options = {
evaluate: true,
loops: true,
}
input: {
function f(x, y) {
do {
if (x) break;
foo();
} while (false);
}
}
expect: {
function f(x, y) {
do {
if (x) break;
foo();
} while (false);
}
}
}
issue_186: {
beautify = {
beautify: false,
screw_ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo())do{do{alert(x)}while(--x)}while(x);else bar();'
}
issue_186_ie8: {
beautify = {
beautify: false,
screw_ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else bar();'
}
issue_186_beautify: {
beautify = {
beautify: true,
screw_ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) do {',
' do {',
' alert(x);',
' } while (--x);',
'} while (x); else bar();',
]
}
issue_186_beautify_ie8: {
beautify = {
beautify: true,
screw_ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else bar();',
]
}
issue_186_bracketize: {
beautify = {
beautify: false,
bracketize: true,
screw_ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_bracketize_ie8: {
beautify = {
beautify: false,
bracketize: true,
screw_ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_beautify_bracketize: {
beautify = {
beautify: true,
bracketize: true,
screw_ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
}
issue_186_beautify_bracketize_ie8: {
beautify = {
beautify: true,
bracketize: true,
screw_ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
}
issue_1648: {
options = {
join_vars: true,
loops: true,
passes: 2,
sequences: true,
unused: true,
}
input: {
function f() {
x();
var b = 1;
while (1);
}
}
expect_exact: "function f(){for(x();1;);}"
} }

View File

@@ -0,0 +1,39 @@
too_short: {
beautify = {
max_line_len: 10,
}
input: {
function f(a) {
return { c: 42, d: a(), e: "foo"};
}
}
expect_exact: [
'function f(a){',
'return{',
'c:42,',
'd:a(),',
'e:"foo"}}',
]
expect_warnings: [
"WARN: Output exceeds 10 characters"
]
}
just_enough: {
beautify = {
max_line_len: 14,
}
input: {
function f(a) {
return { c: 42, d: a(), e: "foo"};
}
}
expect_exact: [
'function f(a){',
'return{c:42,',
'd:a(),e:"foo"}',
'}',
]
expect_warnings: [
]
}

View File

@@ -10,6 +10,16 @@ negate_iife_1: {
} }
} }
negate_iife_1_off: {
options = {
negate_iife: false,
};
input: {
(function(){ stuff() })();
}
expect_exact: '(function(){stuff()})();'
}
negate_iife_2: { negate_iife_2: {
options = { options = {
negate_iife: true negate_iife: true
@@ -22,44 +32,151 @@ negate_iife_2: {
} }
} }
negate_iife_3: { negate_iife_2_side_effects: {
options = { options = {
negate_iife: true, negate_iife: true,
}; side_effects: true,
}
input: { input: {
(function(){ return true })() ? console.log(true) : console.log(false); (function(){ return {} })().x = 10; // should not transform this one
} }
expect: { expect: {
!function(){ return true }() ? console.log(false) : console.log(true); (function(){ return {} })().x = 10;
} }
} }
negate_iife_3: { negate_iife_3: {
options = { options = {
negate_iife: true, negate_iife: true,
conditionals: true
};
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
!function(){ return t }() ? console.log(false) : console.log(true);
}
}
negate_iife_3_evaluate: {
options = {
conditionals: true,
evaluate: true,
negate_iife: true,
}
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
}
expect_stdout: true
}
negate_iife_3_side_effects: {
options = {
conditionals: true,
negate_iife: true,
side_effects: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
!function(){ return t }() ? console.log(false) : console.log(true);
}
}
negate_iife_3_off: {
options = {
negate_iife: false,
conditionals: true,
};
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
!function(){ return t }() ? console.log(false) : console.log(true);
}
}
negate_iife_3_off_evaluate: {
options = {
conditionals: true,
evaluate: true,
negate_iife: false,
}
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
}
expect_stdout: true
}
negate_iife_4: {
options = {
negate_iife: true,
conditionals: true,
sequences: true sequences: true
}; };
input: { input: {
(function(){ return true })() ? console.log(true) : console.log(false); (function(){ return t })() ? console.log(true) : console.log(false);
(function(){ (function(){
console.log("something"); console.log("something");
})(); })();
} }
expect: { expect: {
!function(){ return true }() ? console.log(false) : console.log(true), function(){ !function(){ return t }() ? console.log(false) : console.log(true), function(){
console.log("something"); console.log("something");
}(); }();
} }
} }
negate_iife_4: { sequence_off: {
options = {
negate_iife: false,
conditionals: true,
sequences: true,
passes: 2,
};
input: {
function f() {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
function g() {
(function(){
console.log("something");
})();
(function(){ return t })() ? console.log(true) : console.log(false);
}
}
expect: {
function f() {
!function(){ return t }() ? console.log(false) : console.log(true), function(){
console.log("something");
}();
}
function g() {
(function(){
console.log("something");
})(), function(){ return t }() ? console.log(true) : console.log(false);
}
}
}
negate_iife_5: {
options = { options = {
negate_iife: true, negate_iife: true,
sequences: true, sequences: true,
conditionals: true, conditionals: true,
}; };
input: { input: {
if ((function(){ return true })()) { if ((function(){ return t })()) {
foo(true); foo(true);
} else { } else {
bar(false); bar(false);
@@ -69,7 +186,30 @@ negate_iife_4: {
})(); })();
} }
expect: { expect: {
!function(){ return true }() ? bar(false) : foo(true), function(){ !function(){ return t }() ? bar(false) : foo(true), function(){
console.log("something");
}();
}
}
negate_iife_5_off: {
options = {
negate_iife: false,
sequences: true,
conditionals: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){
console.log("something"); console.log("something");
}(); }();
} }
@@ -105,6 +245,40 @@ negate_iife_nested: {
}(7); }(7);
}).f(); }).f();
} }
expect_stdout: true
}
negate_iife_nested_off: {
options = {
negate_iife: false,
sequences: true,
conditionals: true,
};
input: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
(function(x) {
(function(y) {
console.log(y);
})(x);
})(7);
}).f();
}
expect: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
(function(x) {
(function(y) {
console.log(y);
})(x);
})(7);
}).f();
}
expect_stdout: true
} }
negate_iife_issue_1073: { negate_iife_issue_1073: {
@@ -129,6 +303,7 @@ negate_iife_issue_1073: {
}; };
}(7))(); }(7))();
} }
expect_stdout: true
} }
issue_1254_negate_iife_false: { issue_1254_negate_iife_false: {
@@ -143,6 +318,7 @@ issue_1254_negate_iife_false: {
})()(); })()();
} }
expect_exact: '(function(){return function(){console.log("test")}})()();' expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
} }
issue_1254_negate_iife_true: { issue_1254_negate_iife_true: {
@@ -157,6 +333,7 @@ issue_1254_negate_iife_true: {
})()(); })()();
} }
expect_exact: '!function(){return function(){console.log("test")}}()();' expect_exact: '!function(){return function(){console.log("test")}}()();'
expect_stdout: true
} }
issue_1254_negate_iife_nested: { issue_1254_negate_iife_nested: {
@@ -171,4 +348,70 @@ issue_1254_negate_iife_nested: {
})()()()()(); })()()()()();
} }
expect_exact: '!function(){return function(){console.log("test")}}()()()()();' expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
expect_stdout: true
}
issue_1288: {
options = {
negate_iife: true,
conditionals: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w || function f() {}();
x || function() {
x = {};
}();
y ? function() {}() : function(z) {
return z;
}(0);
}
}
issue_1288_side_effects: {
options = {
conditionals: true,
negate_iife: true,
side_effects: true,
}
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || function() {
x = {};
}();
y;
}
} }

View File

@@ -17,3 +17,139 @@ hex_numbers_in_parentheses_for_prototype_functions: {
} }
expect_exact: "-2;(-2).toFixed(0);2;2..toFixed(0);.2;.2.toFixed(0);2e-8;2e-8.toFixed(0);0xde0b6b3a7640080;(0xde0b6b3a7640080).toFixed(0);" expect_exact: "-2;(-2).toFixed(0);2;2..toFixed(0);.2;.2.toFixed(0);2e-8;2e-8.toFixed(0);0xde0b6b3a7640080;(0xde0b6b3a7640080).toFixed(0);"
} }
comparisons: {
options = {
comparisons: true,
}
input: {
console.log(
~x === 42,
x % n === 42
);
}
expect: {
console.log(
42 == ~x,
x % n == 42
);
}
}
evaluate_1: {
options = {
evaluate: true,
unsafe_math: false,
}
input: {
console.log(
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3)
);
}
expect: {
console.log(
x + 1 + 2,
1 * x * 2,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
1 + (2 + x + 3),
2 + ~x + 3 + 1,
-y + (2 + ~x + 3),
0 & x,
2 + (x |= 0) + 3 + 1
);
}
}
evaluate_2: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
console.log(
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3)
);
}
expect: {
console.log(
x + 1 + 2,
2 * x,
3 + +x,
1 + x + 2 + 3,
3 | x,
6 + x--,
6 + x*y,
1 + (2 + x + 3),
0 & x,
6 + (x |= 0)
);
}
}
evaluate_3: {
options = {
evaluate: true,
unsafe: true,
unsafe_math: true,
}
input: {
console.log(1 + Number(x) + 2);
}
expect: {
console.log(3 + +x);
}
}
evaluate_4: {
options = {
evaluate: true,
}
input: {
console.log(
1+ +a,
+a+1,
1+-a,
-a+1,
+a+ +b,
+a+-b,
-a+ +b,
-a+-b
);
}
expect: {
console.log(
+a+1,
+a+1,
1-a,
1-a,
+a+ +b,
+a-b,
-a+ +b,
-a-b
);
}
}

View File

@@ -54,7 +54,56 @@ dot_properties_es5: {
} }
} }
evaluate_length: { sub_properties: {
options = {
evaluate: true,
properties: true
};
input: {
a[0] = 0;
a["0"] = 1;
a[3.14] = 2;
a["3" + ".14"] = 3;
a["i" + "f"] = 4;
a["foo" + " bar"] = 5;
a[0 / 0] = 6;
a[null] = 7;
a[undefined] = 8;
}
expect: {
a[0] = 0;
a[0] = 1;
a[3.14] = 2;
a[3.14] = 3;
a.if = 4;
a["foo bar"] = 5;
a[NaN] = 6;
a[null] = 7;
a[void 0] = 8;
}
}
evaluate_array_length: {
options = {
properties: true,
unsafe: true,
evaluate: true
};
input: {
a = [1, 2, 3].length;
a = [1, 2, 3].join()["len" + "gth"];
a = [1, 2, b].length;
a = [1, 2, 3].join(b).length;
}
expect: {
a = 3;
a = 5;
a = [1, 2, b].length;
a = [1, 2, 3].join(b).length;
}
}
evaluate_string_length: {
options = { options = {
properties: true, properties: true,
unsafe: true, unsafe: true,
@@ -490,3 +539,19 @@ first_256_hex_chars_as_properties: {
}; };
} }
} }
native_prototype: {
options = {
unsafe_proto: true,
}
input: {
Array.prototype.splice.apply(a, [1, 2, b, c]);
Object.prototype.hasOwnProperty.call(d, "foo");
String.prototype.indexOf.call(e, "bar");
}
expect: {
[].splice.apply(a, [1, 2, b, c]);
({}).hasOwnProperty.call(d, "foo");
"".indexOf.call(e, "bar");
}
}

295
test/compress/pure_funcs.js Normal file
View File

@@ -0,0 +1,295 @@
array: {
options = {
pure_funcs: [ "Math.floor" ],
side_effects: true,
}
input: {
var a;
function f(b) {
Math.floor(a / b);
Math.floor(c / b);
}
}
expect: {
var a;
function f(b) {
c;
}
}
}
func: {
options = {
pure_funcs: function(node) {
return !~node.args[0].print_to_string().indexOf("a");
},
side_effects: true,
}
input: {
function f(a, b) {
Math.floor(a / b);
Math.floor(c / b);
}
}
expect: {
function f(a, b) {
Math.floor(c / b);
}
}
}
side_effects: {
options = {
pure_funcs: [ "console.log" ],
side_effects: true,
}
input: {
function f(a, b) {
console.log(a());
console.log(b);
}
}
expect: {
function f(a, b) {
a();
}
}
}
unused: {
options = {
pure_funcs: [ "pure" ],
side_effects: true,
unused: true,
}
input: {
function foo() {
var u = pure(1);
var x = pure(2);
var y = pure(x);
var z = pure(pure(side_effects()));
return pure(3);
}
}
expect: {
function foo() {
side_effects();
return pure(3);
}
}
}
babel: {
options = {
pure_funcs: [ "_classCallCheck" ],
side_effects: true,
unused: true,
}
input: {
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor))
throw new TypeError("Cannot call a class as a function");
}
var Foo = function Foo() {
_classCallCheck(this, Foo);
};
}
expect: {
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor))
throw new TypeError("Cannot call a class as a function");
}
var Foo = function() {
};
}
}
conditional: {
options = {
pure_funcs: [ "pure" ],
side_effects: true,
}
input: {
pure(1 | a() ? 2 & b() : 7 ^ c());
pure(1 | a() ? 2 & b() : 5);
pure(1 | a() ? 4 : 7 ^ c());
pure(1 | a() ? 4 : 5);
pure(3 ? 2 & b() : 7 ^ c());
pure(3 ? 2 & b() : 5);
pure(3 ? 4 : 7 ^ c());
pure(3 ? 4 : 5);
}
expect: {
1 | a() ? b() : c();
1 | a() && b();
1 | a() || c();
a();
3 ? b() : c();
3 && b();
3 || c();
}
}
relational: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() in foo();
foo() instanceof bar();
foo() < "bar";
bar() > foo();
bar() != bar();
bar() !== "bar";
"bar" == foo();
"bar" === bar();
"bar" >= "bar";
}
expect: {
bar();
bar();
bar(), bar();
bar();
bar();
}
}
arithmetic: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() + foo();
foo() - bar();
foo() * "bar";
bar() / foo();
bar() & bar();
bar() | "bar";
"bar" >> foo();
"bar" << bar();
"bar" >>> "bar";
}
expect: {
bar();
bar();
bar(), bar();
bar();
bar();
}
}
boolean_and: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() && foo();
foo() && bar();
foo() && "bar";
bar() && foo();
bar() && bar();
bar() && "bar";
"bar" && foo();
"bar" && bar();
"bar" && "bar";
}
expect: {
foo() && bar();
bar();
bar() && bar();
bar();
"bar" && bar();
}
}
boolean_or: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() || foo();
foo() || bar();
foo() || "bar";
bar() || foo();
bar() || bar();
bar() || "bar";
"bar" || foo();
"bar" || bar();
"bar" || "bar";
}
expect: {
foo() || bar();
bar();
bar() || bar();
bar();
"bar" || bar();
}
}
assign: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
var a;
function f(b) {
a = foo();
b *= 4 + foo();
c >>= 0 | foo();
}
}
expect: {
var a;
function f(b) {
a = foo();
b *= 4 + foo();
c >>= 0 | foo();
}
}
}
unary: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
typeof foo();
typeof bar();
typeof "bar";
void foo();
void bar();
void "bar";
delete a[foo()];
delete a[bar()];
delete a["bar"];
a[foo()]++;
a[bar()]++;
a["bar"]++;
--a[foo()];
--a[bar()];
--a["bar"];
~foo();
~bar();
~"bar";
}
expect: {
bar();
bar();
delete a[foo()];
delete a[bar()];
delete a["bar"];
a[foo()]++;
a[bar()]++;
a["bar"]++;
--a[foo()];
--a[bar()];
--a["bar"];
bar();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,49 @@
do_screw: { do_screw: {
options = { screw_ie8: true }; options = {
screw_ie8: true,
}
beautify = { beautify = {
screw_ie8: true, screw_ie8: true,
ascii_only: true ascii_only: true,
}; }
input: {
input: f("\v"); f("\v");
expect_exact: 'f("\\v");'; }
expect_exact: 'f("\\v");'
} }
dont_screw: { dont_screw: {
options = { screw_ie8: false }; options = {
beautify = { screw_ie8: false, ascii_only: true }; screw_ie8: false,
}
beautify = {
screw_ie8: false,
ascii_only: true,
}
input: {
f("\v");
}
expect_exact: 'f("\\x0B");'
}
input: f("\v"); do_screw_constants: {
expect_exact: 'f("\\x0B");'; options = {
screw_ie8: true,
}
input: {
f(undefined, Infinity);
}
expect_exact: "f(void 0,1/0);"
}
dont_screw_constants: {
options = {
screw_ie8: false,
}
input: {
f(undefined, Infinity);
}
expect_exact: "f(undefined,Infinity);"
} }
do_screw_try_catch: { do_screw_try_catch: {
@@ -62,11 +91,11 @@ dont_screw_try_catch: {
} }
expect: { expect: {
bad = function(n){ bad = function(n){
return function(n){ return function(t){
try{ try{
t() n()
} catch(t) { } catch(n) {
n(t) t(n)
} }
} }
}; };
@@ -99,6 +128,7 @@ do_screw_try_catch_undefined: {
return void 0===o return void 0===o
} }
} }
expect_stdout: true
} }
dont_screw_try_catch_undefined: { dont_screw_try_catch_undefined: {
@@ -117,14 +147,87 @@ dont_screw_try_catch_undefined: {
}; };
} }
expect: { expect: {
function a(o){ function a(n){
try{ try{
throw "Stuff" throw "Stuff"
} catch (n) { } catch (undefined) {
console.log("caught: "+n) console.log("caught: " + undefined)
} }
console.log("undefined is " + void 0); console.log("undefined is " + undefined);
return void 0===o return n === undefined
}
}
expect_stdout: true
}
reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
screw_ie8: false,
unused: true,
}
mangle = {
screw_ie8: false,
}
input: {
function f() {
var a;
try {
x();
} catch (a) {
y();
}
alert(a);
}
}
expect: {
function f() {
var t;
try {
x();
} catch (t) {
y();
}
alert(t);
} }
} }
} }
issue_1586_1: {
options = {
screw_ie8: false,
}
mangle = {
screw_ie8: false,
}
input: {
function f() {
try {
} catch (err) {
console.log(err.message);
}
}
}
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
expect_stdout: true
}
issue_1586_2: {
options = {
screw_ie8: true,
}
mangle = {
screw_ie8: true,
}
input: {
function f() {
try {
} catch (err) {
console.log(err.message);
}
}
}
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
expect_stdout: true
}

View File

@@ -86,6 +86,7 @@ make_sequences_4: {
switch (x = 5, y) {} switch (x = 5, y) {}
with (x = 5, obj); with (x = 5, obj);
} }
expect_stdout: true
} }
lift_sequences_1: { lift_sequences_1: {
@@ -103,15 +104,18 @@ lift_sequences_1: {
lift_sequences_2: { lift_sequences_2: {
options = { sequences: true, evaluate: true }; options = { sequences: true, evaluate: true };
input: { input: {
var foo, bar; var foo = 1, bar;
foo.x = (foo = {}, 10); foo.x = (foo = {}, 10);
bar = (bar = {}, 10); bar = (bar = {}, 10);
console.log(foo, bar);
} }
expect: { expect: {
var foo, bar; var foo = 1, bar;
foo.x = (foo = {}, 10), foo.x = (foo = {}, 10),
bar = {}, bar = 10; bar = {}, bar = 10,
console.log(foo, bar);
} }
expect_stdout: true
} }
lift_sequences_3: { lift_sequences_3: {
@@ -138,6 +142,23 @@ lift_sequences_4: {
} }
} }
lift_sequences_5: {
options = {
sequences: true,
}
input: {
var a = 2, b;
a *= (b, a = 4, 3);
console.log(a);
}
expect: {
var a = 2, b;
b, a *= (a = 4, 3),
console.log(a);
}
expect_stdout: "6"
}
for_sequences: { for_sequences: {
options = { sequences: true }; options = { sequences: true };
input: { input: {
@@ -169,3 +190,119 @@ for_sequences: {
for (y = 5; false;); for (y = 5; false;);
} }
} }
limit_1: {
options = {
sequences: 3,
};
input: {
a;
b;
c;
d;
e;
f;
g;
h;
i;
j;
k;
}
expect: {
a, b, c;
d, e, f;
g, h, i;
j, k;
}
}
limit_2: {
options = {
sequences: 3,
};
input: {
a, b;
c, d;
e, f;
g, h;
i, j;
k;
}
expect: {
a, b, c, d;
e, f, g, h;
i, j, k;
}
}
negate_iife_for: {
options = {
sequences: true,
negate_iife: true,
};
input: {
(function() {})();
for (i = 0; i < 5; i++) console.log(i);
(function() {})();
for (; i < 5; i++) console.log(i);
}
expect: {
for (!function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 5; i++) console.log(i);
}
expect_stdout: true
}
iife: {
options = {
sequences: true,
};
input: {
x = 42;
(function a() {})();
!function b() {}();
~function c() {}();
+function d() {}();
-function e() {}();
void function f() {}();
typeof function g() {}();
}
expect: {
x = 42, function a() {}(), function b() {}(), function c() {}(),
function d() {}(), function e() {}(), function f() {}(), function g() {}();
}
}
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
unsafe: true,
}
input: {
function f(undefined) {
if (a)
return b;
if (c)
return d;
}
function g(undefined) {
if (a)
return b;
if (c)
return d;
e();
}
}
expect: {
function f(undefined) {
return a ? b : c ? d : undefined;
}
function g(undefined) {
return a ? b : c ? d : void e();
}
}
}

130
test/compress/transform.js Normal file
View File

@@ -0,0 +1,130 @@
booleans_evaluate: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(typeof void 0 != "undefined");
console.log(1 == 1, 1 === 1)
console.log(1 != 1, 1 !== 1)
}
expect: {
console.log(!1);
console.log(!0, !0);
console.log(!1, !1);
}
expect_stdout: true
}
booleans_global_defs: {
options = {
booleans: true,
evaluate: true,
global_defs: {
A: true,
},
}
input: {
console.log(A == 1);
}
expect: {
console.log(!0);
}
}
condition_evaluate: {
options = {
booleans: true,
dead_code: false,
evaluate: true,
loops: false,
}
input: {
while (1 === 2);
for (; 1 == true;);
if (void 0 == null);
}
expect: {
while (!1);
for (; !0;);
if (!0);
}
}
if_else_empty: {
options = {
conditionals: true,
}
input: {
if ({} ? a : b); else {}
}
expect: {
!{} ? b : a;
}
}
label_if_break: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
L: if (true) {
a;
break L;
}
}
expect: {
a;
}
}
while_if_break: {
options = {
conditionals: true,
loops: true,
sequences: true,
}
input: {
while (a) {
if (b) if(c) d;
if (e) break;
}
}
expect: {
for(; a && (b && c && d, !e););
}
}
if_return: {
options = {
booleans: true,
conditionals: true,
if_return: true,
sequences: true,
}
input: {
function f(w, x, y, z) {
if (x) return;
if (w) {
if (y) return;
} else if (z) return;
if (x == y) return true;
if (x) w();
if (y) z();
return true;
}
}
expect: {
function f(w, x, y, z) {
if (!x) {
if (w) {
if (y) return;
} else if (z) return;
return x == y || (x && w(), y && z(), !0);
}
}
}
}

View File

@@ -29,6 +29,7 @@ typeof_in_boolean_context: {
booleans : true, booleans : true,
evaluate : true, evaluate : true,
conditionals : true, conditionals : true,
side_effects : true,
}; };
input: { input: {
function f1(x) { return typeof x ? "yes" : "no"; } function f1(x) { return typeof x ? "yes" : "no"; }
@@ -36,12 +37,14 @@ typeof_in_boolean_context: {
typeof 0 ? foo() : bar(); typeof 0 ? foo() : bar();
!typeof console.log(1); !typeof console.log(1);
var a = !typeof console.log(2); var a = !typeof console.log(2);
if (typeof (1 + foo()));
} }
expect: { expect: {
function f1(x) { return "yes"; } function f1(x) { return "yes"; }
function f2() { return g(), "Yes"; } function f2() { return g(), "Yes"; }
foo(); foo();
!(console.log(1), !0); console.log(1);
var a = !(console.log(2), !0); var a = !(console.log(2), !0);
foo();
} }
} }

View File

@@ -0,0 +1 @@
console.log(C.V, C.D);

View File

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

View File

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

View File

@@ -0,0 +1 @@
console.log(2 || (Math.random() /= 2));

View File

@@ -0,0 +1 @@
console.log(3 || ++this);

View File

@@ -0,0 +1 @@
foo, bar(

View File

@@ -0,0 +1 @@
for (var i = 0; i < 1; i++)

View File

@@ -0,0 +1 @@
function f(a{}

View File

@@ -0,0 +1 @@
foo( xyz, 0abc);

View File

@@ -0,0 +1,14 @@
function f(x) {
return function() {
function n(a) {
return a * a;
}
return x(n);
};
}
function g(op) {
return op(1) + op(2);
}
console.log(f(g)() == 5);

View File

@@ -0,0 +1,73 @@
if (x) {
foo();
}
if (x) {
foo();
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else {
baz();
}
if (x) {
if (y) {
foo();
} else {
bar();
}
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else if (z) {
baz();
} else {
moo();
}
function f() {
if (x) {
foo();
}
if (x) {
foo();
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else {
baz();
}
if (x) {
if (y) {
foo();
} else {
bar();
}
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else if (z) {
baz();
} else {
moo();
}
}

View File

@@ -0,0 +1,17 @@
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
function f() {
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
}

View File

@@ -0,0 +1,12 @@
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
function f() {
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
}

View File

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

View File

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

View File

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

87
test/jetstream.js Normal file
View File

@@ -0,0 +1,87 @@
#! /usr/bin/env node
// -*- js -*-
"use strict";
var site = "http://browserbench.org/JetStream/";
if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc", "warnings=false");
}
args.push("--stats");
var child_process = require("child_process");
try {
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var http = require("http");
var server = http.createServer(function(request, response) {
request.resume();
var url = decodeURIComponent(request.url.slice(1));
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
http.get(url, function(res) {
res.pipe(uglifyjs.stdin);
});
}).listen().on("listening", function() {
var phantomjs = require("phantomjs-prebuilt");
var program = phantomjs.exec(process.argv[1], server.address().port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
});
});
server.timeout = 0;
} else {
var page = require("webpage").create();
page.onError = function(msg, trace) {
var body = [ msg ];
if (trace) trace.forEach(function(t) {
body.push(" " + (t.function || "Anonymous function") + " (" + t.file + ":" + t.line + ")");
});
console.error(body.join("\n"));
phantom.exit(1);
};
var url = "http://localhost:" + require("system").args[1] + "/";
page.onResourceRequested = function(requestData, networkRequest) {
if (/\.js$/.test(requestData.url))
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
}
page.onConsoleMessage = function(msg) {
if (/Error:/i.test(msg)) {
console.error(msg);
phantom.exit(1);
}
console.log(msg);
if (~msg.indexOf("Raw results:")) {
phantom.exit();
}
};
page.open(site, function(status) {
if (status != "success") phantomjs.exit(1);
page.evaluate(function() {
JetStream.switchToQuick();
JetStream.start();
});
});
}

View File

@@ -0,0 +1,32 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("Accessor tokens", function() {
it("Should fill the token information for accessors (issue #1492)", function() {
// location 0 1 2 3 4
// 01234567890123456789012345678901234567890123456789
var ast = UglifyJS.parse("var obj = { get latest() { return undefined; } }");
// test all AST_ObjectProperty tokens are set as expected
var checkedAST_ObjectProperty = false;
var checkWalker = new UglifyJS.TreeWalker(function(node, descend) {
if (node instanceof UglifyJS.AST_ObjectProperty) {
checkedAST_ObjectProperty = true;
assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolRef);
assert.equal(node.key.start.pos, 16);
assert.equal(node.key.end.endpos, 22);
assert(node.value instanceof UglifyJS.AST_Accessor);
assert.equal(node.value.start.pos, 22);
assert.equal(node.value.end.endpos, 46);
}
});
ast.walk(checkWalker);
assert(checkedAST_ObjectProperty, "AST_ObjectProperty not found");
});
});

View File

@@ -1,10 +1,11 @@
var assert = require("assert"); var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var readFileSync = require("fs").readFileSync;
describe("bin/uglifyjs", function () { describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) { it("should produce a functional build when using --self", function (done) {
this.timeout(5000); this.timeout(15000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS'; var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
@@ -55,7 +56,7 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n"); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
done(); done();
}); });
@@ -70,4 +71,239 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --keep-fnames (mangle & compress)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m -c unused=false';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n");
done();
});
});
it("Should work with keep_fnames under mangler options", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --define (simple)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(5);\n");
done();
});
});
it("Should work with --define (nested)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(3,5);\n");
done();
});
});
it("Should work with --define (AST_Node)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "stdout.println(D);\n");
done();
});
});
it("Should work with `--beautify`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/default.js", "utf8"));
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/bracketize.js", "utf8"));
done();
});
});
it("Should process inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js -mc toplevel --in-source-map inline --source-map-inline';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-520/output.js", "utf8"));
done();
});
});
it("Should warn for missing inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js --in-source-map inline';
exec(command, function (err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
assert.strictEqual(stderr, "WARN: inline source map not found\n");
done();
});
});
it("Should fail with multiple input and inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js test/input/issue-520/output.js --in-source-map inline --source-map-inline';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: Inline source map only works with singular input\n");
done();
});
});
it("Should fail with acorn and inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --acorn';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done();
});
});
it("Should fail with SpiderMonkey and inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --spidermonkey';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done();
});
});
it("Should fail with invalid syntax", function(done) {
var command = uglifyjscmd + ' test/input/invalid/simple.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12");
assert.strictEqual(lines[1], "function f(a{}");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»");
done();
});
});
it("Should fail with correct marking of tabs", function(done) {
var command = uglifyjscmd + ' test/input/invalid/tab.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12");
assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);");
assert.strictEqual(lines[2], "\t\t \t ^");
assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc");
done();
});
});
it("Should fail with correct marking at start of line", function(done) {
var command = uglifyjscmd + ' test/input/invalid/eof.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0");
assert.strictEqual(lines[1], "foo, bar(");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
it("Should fail with a missing loop body", function(done) {
var command = uglifyjscmd + ' test/input/invalid/loop-no-body.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0");
assert.strictEqual(lines[1], "for (var i = 0; i < 1; i++) ");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
it("Should support hyphen as shorthand", function(done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep-fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_1.js:1,18",
"console.log(1 || 5--);",
" ^",
"SyntaxError: Invalid use of -- operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));",
" ^",
"SyntaxError: Invalid assignment"
].join("\n"));
done();
});
});
it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_3.js:1,18",
"console.log(3 || ++this);",
" ^",
"SyntaxError: Invalid use of ++ operator"
].join("\n"));
done();
});
});
}); });

View File

@@ -72,4 +72,12 @@ describe("comment filters", function() {
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}"); assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}"); assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
}); });
it("Should handle shebang and preamble correctly", function() {
var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", {
fromString: true,
output: { preamble: "/* Build */" }
}).code;
assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;");
})
}); });

View File

@@ -13,7 +13,7 @@ describe("Comment", function() {
var fail = function(e) { var fail = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: operator (>)" && e.message === "Unexpected token: operator (>)" &&
e.line === 2 && e.line === 2 &&
e.col === 0; e.col === 0;
} }
@@ -36,7 +36,7 @@ describe("Comment", function() {
var fail = function(e) { var fail = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: operator (>)" && e.message === "Unexpected token: operator (>)" &&
e.line === 5 && e.line === 5 &&
e.col === 0; e.col === 0;
} }

View File

@@ -7,7 +7,7 @@ describe("comment before constant", function() {
it("Should test comment before constant is retained and output after mangle.", function() { it("Should test comment before constant is retained and output after mangle.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true, fromString: true,
compress: { collapse_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {}, mangle: {},
output: { comments: true }, output: { comments: true },
}); });
@@ -17,9 +17,9 @@ describe("comment before constant", function() {
it("Should test code works when comments disabled.", function() { it("Should test code works when comments disabled.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true, fromString: true,
compress: { collapse_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {}, mangle: {},
output: {}, output: { comments: false },
}); });
assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); assert.strictEqual(result.code, 'function f(){var n=!1;return n}');
}); });

View File

@@ -168,7 +168,7 @@ describe("Directives", function() {
throw new Error("Expected parser to fail"); throw new Error("Expected parser to fail");
} catch (e) { } catch (e) {
assert.strictEqual(e instanceof uglify.JS_Parse_Error, true); assert.strictEqual(e instanceof uglify.JS_Parse_Error, true);
assert.strictEqual(e.message, "SyntaxError: Unexpected token: punc (])"); assert.strictEqual(e.message, "Unexpected token: punc (])");
} }
test_directive(tokenizer, tests[i]); test_directive(tokenizer, tests[i]);

View File

@@ -71,7 +71,7 @@ describe("Getters and setters", function() {
var fail = function(data) { var fail = function(data) {
return function (e) { return function (e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Invalid getter/setter name: " + data.operator; e.message === "Invalid getter/setter name: " + data.operator;
}; };
}; };

View File

@@ -1,19 +1,16 @@
var Uglify = require('../../'); var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var path = require("path");
describe("minify() with input file globs", function() { describe("minify() with input file globs", function() {
it("minify() with one input file glob string.", function() { it("minify() with one input file glob string.", function() {
var result = Uglify.minify("test/input/issue-1242/foo.*", { var result = Uglify.minify("test/input/issue-1242/foo.*");
compress: { collapse_vars: true }
});
assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);'); assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);');
}); });
it("minify() with an array of one input file glob.", function() { it("minify() with an array of one input file glob.", function() {
var result = Uglify.minify([ var result = Uglify.minify([
"test/input/issue-1242/b*.es5", "test/input/issue-1242/b*.es5",
], { ]);
compress: { collapse_vars: true }
});
assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}'); assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}');
}); });
it("minify() with an array of multiple input file globs.", function() { it("minify() with an array of multiple input file globs.", function() {
@@ -21,8 +18,41 @@ describe("minify() with input file globs", function() {
"test/input/issue-1242/???.es5", "test/input/issue-1242/???.es5",
"test/input/issue-1242/*.js", "test/input/issue-1242/*.js",
], { ], {
compress: { collapse_vars: true } compress: { toplevel: true }
}); });
assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}function foo(n){print("Foo:",2*n)}var print=console.log.bind(console);print("qux",bar(3),baz(12)),foo(11);'); assert.strictEqual(result.code, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);');
});
it("should throw with non-matching glob string", function() {
var glob = "test/input/issue-1242/blah.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.throws(function() {
Uglify.minify(glob);
}, "should throw file not found");
});
it('"?" in glob string should not match "/"', function() {
var glob = "test/input?issue-1242/foo.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.throws(function() {
Uglify.minify(glob);
}, "should throw file not found");
});
it("should handle special characters in glob string", function() {
var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
assert.strictEqual(result.code, "console.log(x);");
});
it("should handle array of glob strings - matching and otherwise", function() {
var dir = "test/input/issue-1242";
var matches = Uglify.simple_glob([
path.join(dir, "b*.es5"),
path.join(dir, "z*.es5"),
path.join(dir, "*.js"),
]);
assert.strictEqual(matches.length, 4);
assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
assert.strictEqual(matches[3], path.join(dir, "qux.js"));
}); });
}); });

View File

@@ -50,7 +50,7 @@ describe("line-endings", function() {
} }
var fail = function(e) { var fail = function(e) {
return e instanceof Uglify.JS_Parse_Error && return e instanceof Uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected line terminator"; e.message === "Unexpected line terminator";
} }
for (var i = 0; i < inputs.length; i++) { for (var i = 0; i < inputs.length; i++) {
assert.throws(test(inputs[i]), fail); assert.throws(test(inputs[i]), fail);

View File

@@ -1,5 +1,6 @@
var Uglify = require('../../'); var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync;
describe("minify", function() { describe("minify", function() {
it("Should test basic sanity of minify with default options", function() { it("Should test basic sanity of minify with default options", function() {
@@ -75,6 +76,52 @@ describe("minify", function() {
assert.equal(map.sourcesContent[0], assert.equal(map.sourcesContent[0],
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
}); });
it("Should process inline source map", function() {
var code = Uglify.minify("./test/input/issue-520/input.js", {
compress: { toplevel: true },
inSourceMap: "inline",
sourceMapInline: true
}).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
});
it("Should warn for missing inline source map", function() {
var warn_function = Uglify.AST_Node.warn_function;
var warnings = [];
Uglify.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = Uglify.minify("./test/input/issue-1323/sample.js", {
inSourceMap: "inline",
mangle: false,
});
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
Uglify.AST_Node.warn_function = warn_function;
}
});
it("Should fail with multiple input and inline source map", function() {
assert.throws(function() {
Uglify.minify([
"./test/input/issue-520/input.js",
"./test/input/issue-520/output.js"
], {
inSourceMap: "inline",
sourceMapInline: true
});
});
});
it("Should fail with SpiderMonkey and inline source map", function() {
assert.throws(function() {
Uglify.minify("./test/input/issue-520/input.js", {
inSourceMap: "inline",
sourceMapInline: true,
spidermonkey: true
});
});
});
}); });
describe("sourceMapInline", function() { describe("sourceMapInline", function() {
@@ -95,4 +142,54 @@ describe("minify", function() {
assert.strictEqual(code, "var a=function(n){return n};"); assert.strictEqual(code, "var a=function(n){return n};");
}); });
}); });
describe("#__PURE__", function() {
it("should drop #__PURE__ hint after use", function() {
var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', {
fromString: true,
output: {
comments: "all",
beautify: false,
}
});
var code = result.code;
assert.strictEqual(code, "// comment1 comment2\nbar();");
});
it("should not drop #__PURE__ hint if function is retained", function() {
var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", {
fromString: true,
output: {
comments: "all",
beautify: false,
}
});
var code = result.code;
assert.strictEqual(code, "var a=/*#__PURE__*/function(){foo()}();");
})
});
describe("JS_Parse_Error", function() {
it("should throw syntax error", function() {
assert.throws(function() {
Uglify.minify("function f(a{}", { fromString: true });
}, function(err) {
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»");
assert.strictEqual(err.filename, 0);
assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12);
return true;
});
});
});
describe("Compressor", function() {
it("should be backward compatible with ast.transform(compressor)", function() {
var ast = Uglify.parse("function f(a){for(var i=0;i<a;i++)console.log(i)}");
ast.figure_out_scope();
ast = ast.transform(Uglify.Compressor());
assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i<a;i++)console.log(i)}");
});
})
}); });

View File

@@ -15,7 +15,7 @@ describe("Number literals", function () {
} }
var error = function(e) { var error = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Legacy octal literals are not allowed in strict mode"; e.message === "Legacy octal literals are not allowed in strict mode";
} }
for (var i = 0; i < inputs.length; i++) { for (var i = 0; i < inputs.length; i++) {
assert.throws(test(inputs[i]), error, inputs[i]); assert.throws(test(inputs[i]), error, inputs[i]);

489
test/mocha/operator.js Normal file
View File

@@ -0,0 +1,489 @@
var UglifyJS = require("../../");
var assert = require("assert");
describe("operator", function() {
it("Should handle mixing of ++/+/--/- correctly", function() {
function evaluate(exp) {
return new Function("var a=1,b=2,c=" + exp + ";return{a:a,b:b,c:c}")();
}
[ "", "+", "-" ].forEach(function(p) {
[ "++a", "--a", "a", "a--", "a++" ].forEach(function(a) {
[ "+", "-" ].forEach(function(o) {
[ "", "+", "-" ].forEach(function(q) {
[ "++b", "--b", "b", "b--", "b++" ].forEach(function(b) {
var exp = [p, a, o, q, b].join(" ");
var orig = evaluate(exp);
var uglify = evaluate(UglifyJS.parse(exp).print_to_string());
assert.strictEqual(orig.a, uglify.a);
assert.strictEqual(orig.b, uglify.b);
assert.strictEqual(orig.c, uglify.c);
var beautify = evaluate(UglifyJS.parse(exp).print_to_string({
beautify: true
}));
assert.strictEqual(orig.a, beautify.a);
assert.strictEqual(orig.b, beautify.b);
assert.strictEqual(orig.c, beautify.c);
});
});
});
});
});
});
it("Should remove extraneous spaces", function() {
[
[ "++a + ++b", "++a+ ++b" ],
[ "++a + --b", "++a+--b" ],
[ "++a + b", "++a+b" ],
[ "++a + b--", "++a+b--" ],
[ "++a + b++", "++a+b++" ],
[ "++a + + ++b", "++a+ + ++b" ],
[ "++a + + --b", "++a+ +--b" ],
[ "++a + + b", "++a+ +b" ],
[ "++a + + b--", "++a+ +b--" ],
[ "++a + + b++", "++a+ +b++" ],
[ "++a + - ++b", "++a+-++b" ],
[ "++a + - --b", "++a+- --b" ],
[ "++a + - b", "++a+-b" ],
[ "++a + - b--", "++a+-b--" ],
[ "++a + - b++", "++a+-b++" ],
[ "++a - ++b", "++a-++b" ],
[ "++a - --b", "++a- --b" ],
[ "++a - b", "++a-b" ],
[ "++a - b--", "++a-b--" ],
[ "++a - b++", "++a-b++" ],
[ "++a - + ++b", "++a-+ ++b" ],
[ "++a - + --b", "++a-+--b" ],
[ "++a - + b", "++a-+b" ],
[ "++a - + b--", "++a-+b--" ],
[ "++a - + b++", "++a-+b++" ],
[ "++a - - ++b", "++a- -++b" ],
[ "++a - - --b", "++a- - --b" ],
[ "++a - - b", "++a- -b" ],
[ "++a - - b--", "++a- -b--" ],
[ "++a - - b++", "++a- -b++" ],
[ "--a + ++b", "--a+ ++b" ],
[ "--a + --b", "--a+--b" ],
[ "--a + b", "--a+b" ],
[ "--a + b--", "--a+b--" ],
[ "--a + b++", "--a+b++" ],
[ "--a + + ++b", "--a+ + ++b" ],
[ "--a + + --b", "--a+ +--b" ],
[ "--a + + b", "--a+ +b" ],
[ "--a + + b--", "--a+ +b--" ],
[ "--a + + b++", "--a+ +b++" ],
[ "--a + - ++b", "--a+-++b" ],
[ "--a + - --b", "--a+- --b" ],
[ "--a + - b", "--a+-b" ],
[ "--a + - b--", "--a+-b--" ],
[ "--a + - b++", "--a+-b++" ],
[ "--a - ++b", "--a-++b" ],
[ "--a - --b", "--a- --b" ],
[ "--a - b", "--a-b" ],
[ "--a - b--", "--a-b--" ],
[ "--a - b++", "--a-b++" ],
[ "--a - + ++b", "--a-+ ++b" ],
[ "--a - + --b", "--a-+--b" ],
[ "--a - + b", "--a-+b" ],
[ "--a - + b--", "--a-+b--" ],
[ "--a - + b++", "--a-+b++" ],
[ "--a - - ++b", "--a- -++b" ],
[ "--a - - --b", "--a- - --b" ],
[ "--a - - b", "--a- -b" ],
[ "--a - - b--", "--a- -b--" ],
[ "--a - - b++", "--a- -b++" ],
[ "a + ++b", "a+ ++b" ],
[ "a + --b", "a+--b" ],
[ "a + b", "a+b" ],
[ "a + b--", "a+b--" ],
[ "a + b++", "a+b++" ],
[ "a + + ++b", "a+ + ++b" ],
[ "a + + --b", "a+ +--b" ],
[ "a + + b", "a+ +b" ],
[ "a + + b--", "a+ +b--" ],
[ "a + + b++", "a+ +b++" ],
[ "a + - ++b", "a+-++b" ],
[ "a + - --b", "a+- --b" ],
[ "a + - b", "a+-b" ],
[ "a + - b--", "a+-b--" ],
[ "a + - b++", "a+-b++" ],
[ "a - ++b", "a-++b" ],
[ "a - --b", "a- --b" ],
[ "a - b", "a-b" ],
[ "a - b--", "a-b--" ],
[ "a - b++", "a-b++" ],
[ "a - + ++b", "a-+ ++b" ],
[ "a - + --b", "a-+--b" ],
[ "a - + b", "a-+b" ],
[ "a - + b--", "a-+b--" ],
[ "a - + b++", "a-+b++" ],
[ "a - - ++b", "a- -++b" ],
[ "a - - --b", "a- - --b" ],
[ "a - - b", "a- -b" ],
[ "a - - b--", "a- -b--" ],
[ "a - - b++", "a- -b++" ],
[ "a-- + ++b", "a--+ ++b" ],
[ "a-- + --b", "a--+--b" ],
[ "a-- + b", "a--+b" ],
[ "a-- + b--", "a--+b--" ],
[ "a-- + b++", "a--+b++" ],
[ "a-- + + ++b", "a--+ + ++b" ],
[ "a-- + + --b", "a--+ +--b" ],
[ "a-- + + b", "a--+ +b" ],
[ "a-- + + b--", "a--+ +b--" ],
[ "a-- + + b++", "a--+ +b++" ],
[ "a-- + - ++b", "a--+-++b" ],
[ "a-- + - --b", "a--+- --b" ],
[ "a-- + - b", "a--+-b" ],
[ "a-- + - b--", "a--+-b--" ],
[ "a-- + - b++", "a--+-b++" ],
[ "a-- - ++b", "a---++b" ],
[ "a-- - --b", "a--- --b" ],
[ "a-- - b", "a---b" ],
[ "a-- - b--", "a---b--" ],
[ "a-- - b++", "a---b++" ],
[ "a-- - + ++b", "a---+ ++b" ],
[ "a-- - + --b", "a---+--b" ],
[ "a-- - + b", "a---+b" ],
[ "a-- - + b--", "a---+b--" ],
[ "a-- - + b++", "a---+b++" ],
[ "a-- - - ++b", "a--- -++b" ],
[ "a-- - - --b", "a--- - --b" ],
[ "a-- - - b", "a--- -b" ],
[ "a-- - - b--", "a--- -b--" ],
[ "a-- - - b++", "a--- -b++" ],
[ "a++ + ++b", "a+++ ++b" ],
[ "a++ + --b", "a+++--b" ],
[ "a++ + b", "a+++b" ],
[ "a++ + b--", "a+++b--" ],
[ "a++ + b++", "a+++b++" ],
[ "a++ + + ++b", "a+++ + ++b" ],
[ "a++ + + --b", "a+++ +--b" ],
[ "a++ + + b", "a+++ +b" ],
[ "a++ + + b--", "a+++ +b--" ],
[ "a++ + + b++", "a+++ +b++" ],
[ "a++ + - ++b", "a+++-++b" ],
[ "a++ + - --b", "a+++- --b" ],
[ "a++ + - b", "a+++-b" ],
[ "a++ + - b--", "a+++-b--" ],
[ "a++ + - b++", "a+++-b++" ],
[ "a++ - ++b", "a++-++b" ],
[ "a++ - --b", "a++- --b" ],
[ "a++ - b", "a++-b" ],
[ "a++ - b--", "a++-b--" ],
[ "a++ - b++", "a++-b++" ],
[ "a++ - + ++b", "a++-+ ++b" ],
[ "a++ - + --b", "a++-+--b" ],
[ "a++ - + b", "a++-+b" ],
[ "a++ - + b--", "a++-+b--" ],
[ "a++ - + b++", "a++-+b++" ],
[ "a++ - - ++b", "a++- -++b" ],
[ "a++ - - --b", "a++- - --b" ],
[ "a++ - - b", "a++- -b" ],
[ "a++ - - b--", "a++- -b--" ],
[ "a++ - - b++", "a++- -b++" ],
[ "+ ++a + ++b", "+ ++a+ ++b" ],
[ "+ ++a + --b", "+ ++a+--b" ],
[ "+ ++a + b", "+ ++a+b" ],
[ "+ ++a + b--", "+ ++a+b--" ],
[ "+ ++a + b++", "+ ++a+b++" ],
[ "+ ++a + + ++b", "+ ++a+ + ++b" ],
[ "+ ++a + + --b", "+ ++a+ +--b" ],
[ "+ ++a + + b", "+ ++a+ +b" ],
[ "+ ++a + + b--", "+ ++a+ +b--" ],
[ "+ ++a + + b++", "+ ++a+ +b++" ],
[ "+ ++a + - ++b", "+ ++a+-++b" ],
[ "+ ++a + - --b", "+ ++a+- --b" ],
[ "+ ++a + - b", "+ ++a+-b" ],
[ "+ ++a + - b--", "+ ++a+-b--" ],
[ "+ ++a + - b++", "+ ++a+-b++" ],
[ "+ ++a - ++b", "+ ++a-++b" ],
[ "+ ++a - --b", "+ ++a- --b" ],
[ "+ ++a - b", "+ ++a-b" ],
[ "+ ++a - b--", "+ ++a-b--" ],
[ "+ ++a - b++", "+ ++a-b++" ],
[ "+ ++a - + ++b", "+ ++a-+ ++b" ],
[ "+ ++a - + --b", "+ ++a-+--b" ],
[ "+ ++a - + b", "+ ++a-+b" ],
[ "+ ++a - + b--", "+ ++a-+b--" ],
[ "+ ++a - + b++", "+ ++a-+b++" ],
[ "+ ++a - - ++b", "+ ++a- -++b" ],
[ "+ ++a - - --b", "+ ++a- - --b" ],
[ "+ ++a - - b", "+ ++a- -b" ],
[ "+ ++a - - b--", "+ ++a- -b--" ],
[ "+ ++a - - b++", "+ ++a- -b++" ],
[ "+ --a + ++b", "+--a+ ++b" ],
[ "+ --a + --b", "+--a+--b" ],
[ "+ --a + b", "+--a+b" ],
[ "+ --a + b--", "+--a+b--" ],
[ "+ --a + b++", "+--a+b++" ],
[ "+ --a + + ++b", "+--a+ + ++b" ],
[ "+ --a + + --b", "+--a+ +--b" ],
[ "+ --a + + b", "+--a+ +b" ],
[ "+ --a + + b--", "+--a+ +b--" ],
[ "+ --a + + b++", "+--a+ +b++" ],
[ "+ --a + - ++b", "+--a+-++b" ],
[ "+ --a + - --b", "+--a+- --b" ],
[ "+ --a + - b", "+--a+-b" ],
[ "+ --a + - b--", "+--a+-b--" ],
[ "+ --a + - b++", "+--a+-b++" ],
[ "+ --a - ++b", "+--a-++b" ],
[ "+ --a - --b", "+--a- --b" ],
[ "+ --a - b", "+--a-b" ],
[ "+ --a - b--", "+--a-b--" ],
[ "+ --a - b++", "+--a-b++" ],
[ "+ --a - + ++b", "+--a-+ ++b" ],
[ "+ --a - + --b", "+--a-+--b" ],
[ "+ --a - + b", "+--a-+b" ],
[ "+ --a - + b--", "+--a-+b--" ],
[ "+ --a - + b++", "+--a-+b++" ],
[ "+ --a - - ++b", "+--a- -++b" ],
[ "+ --a - - --b", "+--a- - --b" ],
[ "+ --a - - b", "+--a- -b" ],
[ "+ --a - - b--", "+--a- -b--" ],
[ "+ --a - - b++", "+--a- -b++" ],
[ "+ a + ++b", "+a+ ++b" ],
[ "+ a + --b", "+a+--b" ],
[ "+ a + b", "+a+b" ],
[ "+ a + b--", "+a+b--" ],
[ "+ a + b++", "+a+b++" ],
[ "+ a + + ++b", "+a+ + ++b" ],
[ "+ a + + --b", "+a+ +--b" ],
[ "+ a + + b", "+a+ +b" ],
[ "+ a + + b--", "+a+ +b--" ],
[ "+ a + + b++", "+a+ +b++" ],
[ "+ a + - ++b", "+a+-++b" ],
[ "+ a + - --b", "+a+- --b" ],
[ "+ a + - b", "+a+-b" ],
[ "+ a + - b--", "+a+-b--" ],
[ "+ a + - b++", "+a+-b++" ],
[ "+ a - ++b", "+a-++b" ],
[ "+ a - --b", "+a- --b" ],
[ "+ a - b", "+a-b" ],
[ "+ a - b--", "+a-b--" ],
[ "+ a - b++", "+a-b++" ],
[ "+ a - + ++b", "+a-+ ++b" ],
[ "+ a - + --b", "+a-+--b" ],
[ "+ a - + b", "+a-+b" ],
[ "+ a - + b--", "+a-+b--" ],
[ "+ a - + b++", "+a-+b++" ],
[ "+ a - - ++b", "+a- -++b" ],
[ "+ a - - --b", "+a- - --b" ],
[ "+ a - - b", "+a- -b" ],
[ "+ a - - b--", "+a- -b--" ],
[ "+ a - - b++", "+a- -b++" ],
[ "+ a-- + ++b", "+a--+ ++b" ],
[ "+ a-- + --b", "+a--+--b" ],
[ "+ a-- + b", "+a--+b" ],
[ "+ a-- + b--", "+a--+b--" ],
[ "+ a-- + b++", "+a--+b++" ],
[ "+ a-- + + ++b", "+a--+ + ++b" ],
[ "+ a-- + + --b", "+a--+ +--b" ],
[ "+ a-- + + b", "+a--+ +b" ],
[ "+ a-- + + b--", "+a--+ +b--" ],
[ "+ a-- + + b++", "+a--+ +b++" ],
[ "+ a-- + - ++b", "+a--+-++b" ],
[ "+ a-- + - --b", "+a--+- --b" ],
[ "+ a-- + - b", "+a--+-b" ],
[ "+ a-- + - b--", "+a--+-b--" ],
[ "+ a-- + - b++", "+a--+-b++" ],
[ "+ a-- - ++b", "+a---++b" ],
[ "+ a-- - --b", "+a--- --b" ],
[ "+ a-- - b", "+a---b" ],
[ "+ a-- - b--", "+a---b--" ],
[ "+ a-- - b++", "+a---b++" ],
[ "+ a-- - + ++b", "+a---+ ++b" ],
[ "+ a-- - + --b", "+a---+--b" ],
[ "+ a-- - + b", "+a---+b" ],
[ "+ a-- - + b--", "+a---+b--" ],
[ "+ a-- - + b++", "+a---+b++" ],
[ "+ a-- - - ++b", "+a--- -++b" ],
[ "+ a-- - - --b", "+a--- - --b" ],
[ "+ a-- - - b", "+a--- -b" ],
[ "+ a-- - - b--", "+a--- -b--" ],
[ "+ a-- - - b++", "+a--- -b++" ],
[ "+ a++ + ++b", "+a+++ ++b" ],
[ "+ a++ + --b", "+a+++--b" ],
[ "+ a++ + b", "+a+++b" ],
[ "+ a++ + b--", "+a+++b--" ],
[ "+ a++ + b++", "+a+++b++" ],
[ "+ a++ + + ++b", "+a+++ + ++b" ],
[ "+ a++ + + --b", "+a+++ +--b" ],
[ "+ a++ + + b", "+a+++ +b" ],
[ "+ a++ + + b--", "+a+++ +b--" ],
[ "+ a++ + + b++", "+a+++ +b++" ],
[ "+ a++ + - ++b", "+a+++-++b" ],
[ "+ a++ + - --b", "+a+++- --b" ],
[ "+ a++ + - b", "+a+++-b" ],
[ "+ a++ + - b--", "+a+++-b--" ],
[ "+ a++ + - b++", "+a+++-b++" ],
[ "+ a++ - ++b", "+a++-++b" ],
[ "+ a++ - --b", "+a++- --b" ],
[ "+ a++ - b", "+a++-b" ],
[ "+ a++ - b--", "+a++-b--" ],
[ "+ a++ - b++", "+a++-b++" ],
[ "+ a++ - + ++b", "+a++-+ ++b" ],
[ "+ a++ - + --b", "+a++-+--b" ],
[ "+ a++ - + b", "+a++-+b" ],
[ "+ a++ - + b--", "+a++-+b--" ],
[ "+ a++ - + b++", "+a++-+b++" ],
[ "+ a++ - - ++b", "+a++- -++b" ],
[ "+ a++ - - --b", "+a++- - --b" ],
[ "+ a++ - - b", "+a++- -b" ],
[ "+ a++ - - b--", "+a++- -b--" ],
[ "+ a++ - - b++", "+a++- -b++" ],
[ "- ++a + ++b", "-++a+ ++b" ],
[ "- ++a + --b", "-++a+--b" ],
[ "- ++a + b", "-++a+b" ],
[ "- ++a + b--", "-++a+b--" ],
[ "- ++a + b++", "-++a+b++" ],
[ "- ++a + + ++b", "-++a+ + ++b" ],
[ "- ++a + + --b", "-++a+ +--b" ],
[ "- ++a + + b", "-++a+ +b" ],
[ "- ++a + + b--", "-++a+ +b--" ],
[ "- ++a + + b++", "-++a+ +b++" ],
[ "- ++a + - ++b", "-++a+-++b" ],
[ "- ++a + - --b", "-++a+- --b" ],
[ "- ++a + - b", "-++a+-b" ],
[ "- ++a + - b--", "-++a+-b--" ],
[ "- ++a + - b++", "-++a+-b++" ],
[ "- ++a - ++b", "-++a-++b" ],
[ "- ++a - --b", "-++a- --b" ],
[ "- ++a - b", "-++a-b" ],
[ "- ++a - b--", "-++a-b--" ],
[ "- ++a - b++", "-++a-b++" ],
[ "- ++a - + ++b", "-++a-+ ++b" ],
[ "- ++a - + --b", "-++a-+--b" ],
[ "- ++a - + b", "-++a-+b" ],
[ "- ++a - + b--", "-++a-+b--" ],
[ "- ++a - + b++", "-++a-+b++" ],
[ "- ++a - - ++b", "-++a- -++b" ],
[ "- ++a - - --b", "-++a- - --b" ],
[ "- ++a - - b", "-++a- -b" ],
[ "- ++a - - b--", "-++a- -b--" ],
[ "- ++a - - b++", "-++a- -b++" ],
[ "- --a + ++b", "- --a+ ++b" ],
[ "- --a + --b", "- --a+--b" ],
[ "- --a + b", "- --a+b" ],
[ "- --a + b--", "- --a+b--" ],
[ "- --a + b++", "- --a+b++" ],
[ "- --a + + ++b", "- --a+ + ++b" ],
[ "- --a + + --b", "- --a+ +--b" ],
[ "- --a + + b", "- --a+ +b" ],
[ "- --a + + b--", "- --a+ +b--" ],
[ "- --a + + b++", "- --a+ +b++" ],
[ "- --a + - ++b", "- --a+-++b" ],
[ "- --a + - --b", "- --a+- --b" ],
[ "- --a + - b", "- --a+-b" ],
[ "- --a + - b--", "- --a+-b--" ],
[ "- --a + - b++", "- --a+-b++" ],
[ "- --a - ++b", "- --a-++b" ],
[ "- --a - --b", "- --a- --b" ],
[ "- --a - b", "- --a-b" ],
[ "- --a - b--", "- --a-b--" ],
[ "- --a - b++", "- --a-b++" ],
[ "- --a - + ++b", "- --a-+ ++b" ],
[ "- --a - + --b", "- --a-+--b" ],
[ "- --a - + b", "- --a-+b" ],
[ "- --a - + b--", "- --a-+b--" ],
[ "- --a - + b++", "- --a-+b++" ],
[ "- --a - - ++b", "- --a- -++b" ],
[ "- --a - - --b", "- --a- - --b" ],
[ "- --a - - b", "- --a- -b" ],
[ "- --a - - b--", "- --a- -b--" ],
[ "- --a - - b++", "- --a- -b++" ],
[ "- a + ++b", "-a+ ++b" ],
[ "- a + --b", "-a+--b" ],
[ "- a + b", "-a+b" ],
[ "- a + b--", "-a+b--" ],
[ "- a + b++", "-a+b++" ],
[ "- a + + ++b", "-a+ + ++b" ],
[ "- a + + --b", "-a+ +--b" ],
[ "- a + + b", "-a+ +b" ],
[ "- a + + b--", "-a+ +b--" ],
[ "- a + + b++", "-a+ +b++" ],
[ "- a + - ++b", "-a+-++b" ],
[ "- a + - --b", "-a+- --b" ],
[ "- a + - b", "-a+-b" ],
[ "- a + - b--", "-a+-b--" ],
[ "- a + - b++", "-a+-b++" ],
[ "- a - ++b", "-a-++b" ],
[ "- a - --b", "-a- --b" ],
[ "- a - b", "-a-b" ],
[ "- a - b--", "-a-b--" ],
[ "- a - b++", "-a-b++" ],
[ "- a - + ++b", "-a-+ ++b" ],
[ "- a - + --b", "-a-+--b" ],
[ "- a - + b", "-a-+b" ],
[ "- a - + b--", "-a-+b--" ],
[ "- a - + b++", "-a-+b++" ],
[ "- a - - ++b", "-a- -++b" ],
[ "- a - - --b", "-a- - --b" ],
[ "- a - - b", "-a- -b" ],
[ "- a - - b--", "-a- -b--" ],
[ "- a - - b++", "-a- -b++" ],
[ "- a-- + ++b", "-a--+ ++b" ],
[ "- a-- + --b", "-a--+--b" ],
[ "- a-- + b", "-a--+b" ],
[ "- a-- + b--", "-a--+b--" ],
[ "- a-- + b++", "-a--+b++" ],
[ "- a-- + + ++b", "-a--+ + ++b" ],
[ "- a-- + + --b", "-a--+ +--b" ],
[ "- a-- + + b", "-a--+ +b" ],
[ "- a-- + + b--", "-a--+ +b--" ],
[ "- a-- + + b++", "-a--+ +b++" ],
[ "- a-- + - ++b", "-a--+-++b" ],
[ "- a-- + - --b", "-a--+- --b" ],
[ "- a-- + - b", "-a--+-b" ],
[ "- a-- + - b--", "-a--+-b--" ],
[ "- a-- + - b++", "-a--+-b++" ],
[ "- a-- - ++b", "-a---++b" ],
[ "- a-- - --b", "-a--- --b" ],
[ "- a-- - b", "-a---b" ],
[ "- a-- - b--", "-a---b--" ],
[ "- a-- - b++", "-a---b++" ],
[ "- a-- - + ++b", "-a---+ ++b" ],
[ "- a-- - + --b", "-a---+--b" ],
[ "- a-- - + b", "-a---+b" ],
[ "- a-- - + b--", "-a---+b--" ],
[ "- a-- - + b++", "-a---+b++" ],
[ "- a-- - - ++b", "-a--- -++b" ],
[ "- a-- - - --b", "-a--- - --b" ],
[ "- a-- - - b", "-a--- -b" ],
[ "- a-- - - b--", "-a--- -b--" ],
[ "- a-- - - b++", "-a--- -b++" ],
[ "- a++ + ++b", "-a+++ ++b" ],
[ "- a++ + --b", "-a+++--b" ],
[ "- a++ + b", "-a+++b" ],
[ "- a++ + b--", "-a+++b--" ],
[ "- a++ + b++", "-a+++b++" ],
[ "- a++ + + ++b", "-a+++ + ++b" ],
[ "- a++ + + --b", "-a+++ +--b" ],
[ "- a++ + + b", "-a+++ +b" ],
[ "- a++ + + b--", "-a+++ +b--" ],
[ "- a++ + + b++", "-a+++ +b++" ],
[ "- a++ + - ++b", "-a+++-++b" ],
[ "- a++ + - --b", "-a+++- --b" ],
[ "- a++ + - b", "-a+++-b" ],
[ "- a++ + - b--", "-a+++-b--" ],
[ "- a++ + - b++", "-a+++-b++" ],
[ "- a++ - ++b", "-a++-++b" ],
[ "- a++ - --b", "-a++- --b" ],
[ "- a++ - b", "-a++-b" ],
[ "- a++ - b--", "-a++-b--" ],
[ "- a++ - b++", "-a++-b++" ],
[ "- a++ - + ++b", "-a++-+ ++b" ],
[ "- a++ - + --b", "-a++-+--b" ],
[ "- a++ - + b", "-a++-+b" ],
[ "- a++ - + b--", "-a++-+b--" ],
[ "- a++ - + b++", "-a++-+b++" ],
[ "- a++ - - ++b", "-a++- -++b" ],
[ "- a++ - - --b", "-a++- - --b" ],
[ "- a++ - - b", "-a++- -b" ],
[ "- a++ - - b--", "-a++- -b--" ],
[ "- a++ - - b++", "-a++- -b++" ],
].forEach(function(exp) {
assert.strictEqual(UglifyJS.parse(exp[0]).print_to_string(), exp[1] + ";");
});
});
});

54
test/mocha/release.js Normal file
View File

@@ -0,0 +1,54 @@
var assert = require("assert");
var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) {
var id = setInterval(function() {
process.stdout.write("\0");
}, 5 * 60 * 1000);
spawn(command, args, {
stdio: "ignore"
}).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(5 * 60 * 1000);
[
"-b",
"-b bracketize",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",
"-mc passes=3,unsafe",
"-mc keep_fargs=false,passes=3",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].forEach(function(options) {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
run(process.argv[0], args, done);
});
});
});
describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000);
it("Should install phantomjs-prebuilt", function(done) {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
});
[
"-mc warnings=false",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto,warnings=false",
].forEach(function(options) {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
run(process.argv[0], args, done);
});
});
});

View File

@@ -19,7 +19,7 @@ describe("String literals", function() {
var error = function(e) { var error = function(e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Unterminated string constant"; e.message === "Unterminated string constant";
}; };
for (var input in inputs) { for (var input in inputs) {
@@ -49,7 +49,7 @@ describe("String literals", function() {
var error = function(e) { var error = function(e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Legacy octal escape sequences are not allowed in strict mode"; e.message === "Legacy octal escape sequences are not allowed in strict mode";
} }
for (var input in inputs) { for (var input in inputs) {

View File

@@ -9,7 +9,7 @@ describe("With", function() {
} }
var error = function(e) { var error = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Strict mode may not include a with statement"; e.message === "Strict mode may not include a with statement";
} }
assert.throws(test, error); assert.throws(test, error);
}); });

View File

@@ -5,7 +5,7 @@ var UglifyJS = require(".."),
escodegen = require("escodegen"), escodegen = require("escodegen"),
esfuzz = require("esfuzz"), esfuzz = require("esfuzz"),
estraverse = require("estraverse"), estraverse = require("estraverse"),
prefix = Array(20).join("\b") + " "; prefix = "\r ";
// Normalizes input AST for UglifyJS in order to get correct comparison. // Normalizes input AST for UglifyJS in order to get correct comparison.
@@ -62,7 +62,7 @@ module.exports = function(options) {
var ast1 = normalizeInput(esfuzz.generate({ var ast1 = normalizeInput(esfuzz.generate({
maxDepth: options.maxDepth maxDepth: options.maxDepth
})); }));
var ast2 = var ast2 =
UglifyJS UglifyJS
.AST_Node .AST_Node

View File

@@ -6,10 +6,22 @@ var U = require("../tools/node");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
var vm = require("vm");
var tests_dir = path.dirname(module.filename); var tests_dir = path.dirname(module.filename);
var failures = 0; var failures = 0;
var failed_files = {}; var failed_files = {};
var same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected != "string") {
if (expected.name != actual.name) return false;
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
}
return expected == actual;
} : function(expected, actual) {
return typeof expected == typeof actual && expected.toString() == actual.toString();
};
run_compress_tests(); run_compress_tests();
if (failures) { if (failures) {
@@ -70,12 +82,17 @@ function test_directory(dir) {
return path.resolve(tests_dir, dir); return path.resolve(tests_dir, dir);
} }
function as_toplevel(input) { function as_toplevel(input, mangle_options) {
if (input instanceof U.AST_BlockStatement) input = input.body; if (!(input instanceof U.AST_BlockStatement))
else if (input instanceof U.AST_Statement) input = [ input ]; throw new Error("Unsupported input syntax");
else throw new Error("Unsupported input syntax"); for (var i = 0; i < input.body.length; i++) {
var toplevel = new U.AST_Toplevel({ body: input }); var stat = input.body[i];
toplevel.figure_out_scope(); if (stat instanceof U.AST_SimpleStatement && stat.body instanceof U.AST_String)
input.body[i] = new U.AST_Directive(stat.body);
else break;
}
var toplevel = new U.AST_Toplevel(input);
toplevel.figure_out_scope(mangle_options);
return toplevel; return toplevel;
} }
@@ -88,6 +105,23 @@ function run_compress_tests() {
function test_case(test) { function test_case(test) {
log_test(test.name); log_test(test.name);
U.base54.reset(); U.base54.reset();
var output_options = test.beautify || {};
var expect;
if (test.expect) {
expect = make_code(as_toplevel(test.expect, test.mangle), output_options);
} else {
expect = test.expect_exact;
}
var input = as_toplevel(test.input, test.mangle);
var input_code = make_code(input, output_options);
var input_formatted = make_code(test.input, {
beautify: true,
quote_style: 3,
keep_quoted_props: true
});
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var options = U.defaults(test.options, { var options = U.defaults(test.options, {
warnings: false warnings: false
}); });
@@ -97,25 +131,9 @@ function run_compress_tests() {
U.AST_Node.warn_function = function(text) { U.AST_Node.warn_function = function(text) {
warnings_emitted.push("WARN: " + text); warnings_emitted.push("WARN: " + text);
}; };
options.warnings = true; if (!options.warnings) options.warnings = true;
} }
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output_options = test.beautify || {};
var expect;
if (test.expect) {
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, {
beautify: true,
quote_style: 3,
keep_quoted_props: true
});
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);
if (test.mangle) { if (test.mangle) {
@@ -125,7 +143,7 @@ function run_compress_tests() {
output = make_code(output, output_options); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
input: input_code, input: input_formatted,
output: output, output: output,
expected: expect expected: expect
}); });
@@ -138,7 +156,7 @@ function run_compress_tests() {
var reparsed_ast = U.parse(output); var reparsed_ast = U.parse(output);
} catch (ex) { } catch (ex) {
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", { log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
input: input_code, input: input_formatted,
output: output, output: output,
error: ex.toString(), error: ex.toString(),
}); });
@@ -157,7 +175,7 @@ function run_compress_tests() {
var actual_warnings = JSON.stringify(warnings_emitted); var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) { if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
input: input_code, input: input_formatted,
expected_warnings: expected_warnings, expected_warnings: expected_warnings,
actual_warnings: actual_warnings, actual_warnings: actual_warnings,
}); });
@@ -165,6 +183,36 @@ function run_compress_tests() {
failed_files[file] = 1; failed_files[file] = 1;
} }
} }
if (test.expect_stdout) {
var stdout = run_code(input_code);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
}
if (!same_stdout(test.expect_stdout, stdout)) {
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
} else {
stdout = run_code(output);
if (!same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
}
}
}
} }
} }
var tests = parse_test(path.resolve(dir, file)); var tests = parse_test(path.resolve(dir, file));
@@ -194,6 +242,9 @@ function parse_test(file) {
if (node instanceof U.AST_LabeledStatement if (node instanceof U.AST_LabeledStatement
&& tw.parent() instanceof U.AST_Toplevel) { && tw.parent() instanceof U.AST_Toplevel) {
var name = node.label.name; var name = node.label.name;
if (name in tests) {
throw new Error('Duplicated test name "' + name + '" in ' + file);
}
tests[name] = get_one_test(name, node.body); tests[name] = get_one_test(name, node.body);
return true; return true;
} }
@@ -211,6 +262,23 @@ function parse_test(file) {
})); }));
} }
function read_string(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
switch(body.TYPE) {
case "String":
return body.value;
case "Array":
return body.elements.map(function(element) {
if (element.TYPE !== "String")
throw new Error("Should be array of strings");
return element.value;
}).join("\n");
}
}
throw new Error("Should be string or array of strings");
}
function get_one_test(name, block) { function get_one_test(name, block) {
var test = { name: name, options: {} }; var test = { name: name, options: {} };
var tw = new U.TreeWalker(function(node, descend){ var tw = new U.TreeWalker(function(node, descend){
@@ -223,28 +291,26 @@ function parse_test(file) {
return true; return true;
} }
if (node instanceof U.AST_LabeledStatement) { if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
assert.ok( assert.ok(
["input", "expect", "expect_exact", "expect_warnings"].indexOf(node.label.name) >= 0, ["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", { tmpl("Unsupported label {name} [{line},{col}]", {
name: node.label.name, name: label.name,
line: node.label.start.line, line: label.start.line,
col: node.label.start.col col: label.start.col
}) })
); );
var stat = node.body; var stat = node.body;
if (stat instanceof U.AST_BlockStatement) { if (label.name == "expect_exact") {
if (stat.body.length == 1) stat = stat.body[0]; test[label.name] = read_string(stat);
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement(); } else if (label.name == "expect_stdout") {
} if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
if (node.label.name === "expect_exact") { test[label.name] = stat.body.value;
if (!(stat.TYPE === "SimpleStatement" && stat.body.TYPE === "String")) { } else {
throw new Error( test[label.name] = read_string(stat) + "\n";
"The value of the expect_exact clause should be a string, " +
"like `expect_exact: \"some.exact.javascript;\"`");
} }
test[node.label.name] = stat.body.start.value
} else { } else {
test[node.label.name] = stat; test[label.name] = stat;
} }
return true; return true;
} }
@@ -266,3 +332,19 @@ function evaluate(code) {
code = make_code(code, { beautify: true }); code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")(); return new Function("return(" + code + ")")();
} }
function run_code(code) {
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
}
}

225
test/ufuzz.js Normal file
View File

@@ -0,0 +1,225 @@
// ufuzz.js
// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
"use strict";
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var vm = require("vm");
var minify = require("..").minify;
function run_code(code) {
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
}
}
function rng(max) {
return Math.floor(max * Math.random());
}
function createFunctionDecls(n, recurmax) {
if (--recurmax < 0) { return ';'; }
var s = '';
while (--n > 0) {
s += createFunctionDecl(recurmax) + '\n';
}
return s;
}
var funcs = 0;
function createFunctionDecl(recurmax) {
if (--recurmax < 0) { return ';'; }
var func = funcs++;
return 'function f' + func + '(){' + createStatements(3, recurmax) + '}\nf' + func + '();';
}
function createStatements(n, recurmax) {
if (--recurmax < 0) { return ';'; }
var s = '';
while (--n > 0) {
s += createStatement(recurmax);
}
return s;
}
var loops = 0;
function createStatement(recurmax) {
var loop = ++loops;
if (--recurmax < 0) { return ';'; }
switch (rng(7)) {
case 0:
return '{' + createStatement(recurmax) + '}';
case 1:
return 'if (' + createExpression(recurmax) + ')' + createStatement(recurmax);
case 2:
return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax) + '} while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0);}';
case 3:
return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax) + '}';
case 4:
return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax);
case 5:
return ';';
case 6:
return createExpression() + ';';
}
}
function createExpression(recurmax) {
if (--recurmax < 0) { return '0'; }
switch (rng(8)) {
case 0:
return '(' + createUnaryOp() + 'a)';
case 1:
return '(a' + (Math.random() > 0.5 ? '++' : '--') + ')';
case 2:
return '(b ' + createAssignment() + ' a)';
case 3:
return '(' + Math.random() + ' > 0.5 ? a : b)';
case 4:
return createExpression(recurmax) + createBinaryOp() + createExpression(recurmax);
case 5:
return createValue();
case 6:
return '(' + createExpression(recurmax) + ')';
case 7:
return createExpression(recurmax) + '?(' + createExpression(recurmax) + '):(' + createExpression(recurmax) + ')';
}
}
function createValue() {
var values = [
'true',
'false',
'22',
'0',
'(-1)',
'NaN',
'undefined',
'null',
'"foo"',
'"bar"' ];
return values[rng(values.length)];
}
function createBinaryOp() {
switch (rng(6)) {
case 0:
return '+';
case 1:
return '-';
case 2:
return ',';
case 3:
return '&&';
case 4:
return '||';
case 5:
return '^';
}
}
function createAssignment() {
switch (rng(4)) {
case 0:
return '=';
case 1:
return '-=';
case 2:
return '^=';
case 3:
return '+=';
}
}
function createUnaryOp() {
switch (rng(4)) {
case 0:
return '--';
case 1:
return '++';
case 2:
return '~';
case 3:
return '!';
}
}
function log() {
console.log("//=============================================================");
console.log("// original code");
console.log("//");
console.log(original_code);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("// original code (beautify'd)");
console.log("//");
console.log(beautify_code);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("// uglified code");
console.log("//");
console.log(uglify_code);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log("beautified result:");
console.log(beautify_result);
console.log("uglified result:");
console.log(uglify_result);
}
var num_iterations = +process.argv[2] || 1/0;
var verbose = !!process.argv[3];
for (var round = 0; round < num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
var original_code = [
"var a = 100, b = 10;",
createFunctionDecls(rng(3) + 1, 10),
"console.log(a, b);"
].join("\n");
var beautify_code = minify(original_code, {
fromString: true,
mangle: false,
compress: false,
output: {
beautify: true,
bracketize: true,
},
}).code;
var uglify_code = minify(beautify_code, {
fromString: true,
mangle: false,
compress: {
passes: 3,
},
output: {
beautify: true,
bracketize: true,
},
}).code;
var original_result = run_code(original_code);
var beautify_result = run_code(beautify_code);
var uglify_result = run_code(uglify_code);
var ok = original_result == beautify_result && original_result == uglify_result;
if (verbose || !ok) log();
if (!ok) process.exit(1);
}

View File

@@ -18,6 +18,6 @@ exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier; exports["is_identifier"] = is_identifier;
exports["SymbolDef"] = SymbolDef; exports["SymbolDef"] = SymbolDef;
if (typeof DEBUG !== "undefined" && DEBUG) { if (global.UGLIFY_DEBUG) {
exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE; exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE;
} }

Some files were not shown because too many files have changed in this diff Show More