Compare commits

...

179 Commits

Author SHA1 Message Date
Alex Lam S.L
b37a68c84f v3.2.0 2017-11-26 04:08:35 +08:00
Alex Lam S.L
c141ae6f8d fix argument/atom collision by properties (#2514)
fixes #2513
2017-11-25 22:52:46 +08:00
Alex Lam S.L
97c464dbf5 fix wording and formatting (#2512) 2017-11-25 19:07:46 +08:00
Alex Lam S.L
3b28b915eb extend escape analysis on constant expression properties (#2509)
fixes #2508
2017-11-24 14:07:39 +08:00
Alex Lam S.L
eb001dc1d9 fix argument/atom collision by collapse_vars (#2507)
fixes #2506
2017-11-24 07:26:22 +08:00
Alex Lam S.L
aa9bdf416e make AST_Lambda.contains_this() less magical (#2505) 2017-11-24 07:03:37 +08:00
Alex Lam S.L
8987780db6 eliminate invalid state caching in collapse_vars (#2502)
fixes #2497
2017-11-24 04:12:37 +08:00
Alex Lam S.L
30cfea2e7a fix rename (#2501)
- suppress spurious `rename` from `commander`
- handle `AST_SymbolCatch` correctly
2017-11-24 03:05:43 +08:00
Alex Lam S.L
f4e2fb9864 expand symbol space to improve compression (#2460)
- give globally distinct names to distinct variables
- improve ability to compress cross-scoped
- introduce `options.rename` to `minify()`
- default `true` if both `compress` & `mangle`
2017-11-19 19:29:51 +08:00
Alex Lam S.L
b80062c490 enable hoist_props by default (#2492) 2017-11-19 14:56:23 +08:00
Alex Lam S.L
667fc4d08b v3.1.10 2017-11-18 23:56:33 +08:00
kzc
6142117cdd document the webkit output option (#2490)
- workaround for WebKit bugs
- PhantomJS users should enable this output option

closes #2489
2017-11-17 13:46:49 +08:00
Alex Lam S.L
ae28a24c7f fix cross-scope inlining of AST_Functions (#2486)
fixes #2485
2017-11-16 10:04:30 +08:00
Alex Lam S.L
ebe761cad0 minor consolidations (#2484)
- unique symbol generation
- `unsafe` on `AST_Call`
2017-11-16 04:37:37 +08:00
kzc
fa7a7c5c5a Update ISSUE_TEMPLATE.md (#2481) 2017-11-15 06:00:51 +08:00
kzc
557636f3b7 update documentation for reduce_funcs (#2478) 2017-11-14 16:03:25 +08:00
Alex Lam S.L
49fbe9c5ac fix replacement logic in collapse_vars (#2475) 2017-11-13 07:37:42 +08:00
Alex Lam S.L
2ac5086831 fix top_retain on hoist_props (#2474)
fixes #2473
2017-11-13 00:59:41 +08:00
Alex Lam S.L
c6cfa04d10 allow symbol replacement on multiple occurrences (#2472)
- all-or-nothing replacement
- avoid unmangleable names

fixes #2436
2017-11-12 22:31:47 +08:00
Alex Lam S.L
346fa12e0e v3.1.9 2017-11-11 15:31:13 +08:00
Alex Lam S.L
cda27b0970 extend reduce_funcs to cover cross-scope substitutions (#2469)
fixes #2468
2017-11-11 15:30:17 +08:00
Alex Lam S.L
3c74047368 implement compress option reduce_funcs (#2466)
- inline single-use function declarations as expressions when permissible
- depend on `reduce_vars`
- enabled by default
- disable for speed critical code

fixes #2464
2017-11-11 05:59:35 +08:00
Alex Lam S.L
94525d859f fix object literal tracing in reduce_vars (#2461) 2017-11-10 05:47:10 +08:00
Alex Lam S.L
1127a2caf3 fix multiple nested function substitutions (#2458)
fixes #2449
2017-11-09 23:30:00 +08:00
Alex Lam S.L
246d9d4e83 remove hack in collapse_vars (#2457)
fixes #2456
2017-11-09 20:00:58 +08:00
Alex Lam S.L
4c0b0177b6 preserve function identity in reduce_vars (#2451)
fixes #2450
2017-11-08 03:28:46 +08:00
Alex Lam S.L
38bfb73f06 v3.1.8 2017-11-07 03:55:16 +08:00
Alex Lam S.L
bbedbf4ea0 handle circular function reference gracefully (#2446)
fixes #2442
2017-11-07 02:37:23 +08:00
Alex Lam S.L
2cfb5aa7da account for eval & with in reduce_vars (#2441)
fixes #2440
2017-11-06 16:10:57 +08:00
Alex Lam S.L
6c45101870 consolidate & enhance unused (#2439)
- defer declaration removal in `collapse_vars`
- account for `AST_SymbolFunarg` in deduplication
- private accounting for `collapse_vars`
- avoid issues with identity reference due to deep cloning

fixes #2437
2017-11-06 14:25:10 +08:00
Alex Lam S.L
2c2fd89e34 inline single-use functions that are not constant expressions (#2434)
fixes #2428
2017-11-05 22:14:11 +08:00
Alex Lam S.L
f46281e2b7 v3.1.7 2017-11-05 15:03:19 +08:00
Alex Lam S.L
25a18883f5 tweak #2424 (#2432) 2017-11-05 12:49:14 +08:00
Alex Lam S.L
5b4b07e9a7 extend function inlining safety checks (#2430) 2017-11-05 06:18:45 +08:00
Alex Lam S.L
a8aa28a7a6 consolidate single-use function reduction (#2427)
fixes #2423
2017-11-05 04:27:01 +08:00
Alex Lam S.L
fe5a68f9d5 maintain call argument order in collapse_vars (#2426)
fixes #2425
2017-11-05 00:00:18 +08:00
Alex Lam S.L
71e61153b1 improve variations on call arguments for ufuzz (#2424) 2017-11-04 16:29:42 +08:00
Alex Lam S.L
c8b6f4733d reduce this within functions (#2421)
- only replace same-scope usages
- augment `test/ufuzz.js` to test for `this`


fixes #2420
2017-11-04 00:31:37 +08:00
Alex Lam S.L
a48f87abf2 compress new function containing this (#2417) 2017-10-30 23:19:27 +08:00
Alex Lam S.L
2fd927a7cc v3.1.6 2017-10-29 12:38:10 +08:00
Alex Lam S.L
8428326ea1 enhance properties (#2412)
- trim array items only if `side_effects`
- extend to non-identifier properties
2017-10-29 04:11:26 +08:00
Alex Lam S.L
31f8209193 remove dead code (#2405) 2017-10-27 14:28:09 +08:00
Alex Lam S.L
9b0f86f5a1 fix reduce_vars on AST_Array.length (#2404) 2017-10-27 02:33:37 +08:00
Alex Lam S.L
ee082ace1b compress self comparisons (#2398) 2017-10-26 01:16:12 +08:00
kzc
ae67a49850 document compress option hoist_props (#2399) 2017-10-25 14:03:43 +08:00
Alex Lam S.L
4178289c38 implement hoist_props (#2396)
fixes #2377
2017-10-25 03:38:11 +08:00
Alex Lam S.L
74ae16f9f8 fix unsafe reduce_vars on arrays & objects (#2397) 2017-10-24 22:10:36 +08:00
Tom MacWright
1968203d83 docs: Fix spelling and style (#2395) 2017-10-24 04:59:12 +08:00
Alex Lam S.L
86ea38a259 enhance unsafe evaluate of arrays & objects (#2394) 2017-10-24 02:58:30 +08:00
Alex Lam S.L
8a713e449f deduplicate declarations regardless of toplevel (#2393) 2017-10-23 01:00:50 +08:00
Alex Lam S.L
24aa07855b safer properties transform (#2391)
`{ a: x, b: y }.a` => `[ x, y ][0]`
- `x` cannot be function containing `this`

`[ x, y, z ][1]` => `(x, z, y)`
- only if `z` is side-effect-free
2017-10-22 20:10:13 +08:00
Alex Lam S.L
5fd723f143 fix unsafe expansion of object literals (#2390) 2017-10-22 15:00:36 +08:00
Alex Lam S.L
516eaef50c fix unsafe evaluation of AST_Sub (#2389) 2017-10-22 13:14:15 +08:00
Alex Lam S.L
4ae1fb3ed8 fix unsafe evaluation of objects (#2388) 2017-10-22 04:19:40 +08:00
Alex Lam S.L
011123223b fix unsafe escape analysis in reduce_vars (#2387) 2017-10-22 03:23:31 +08:00
Alex Lam S.L
96439ca246 v3.1.5 2017-10-22 00:27:26 +08:00
Alex Lam S.L
c927cea632 unsafe fix-ups for #2351 (#2379) 2017-10-21 04:08:26 +08:00
Alex Lam S.L
9f4b98f8e4 backport #2374 (#2376) 2017-10-19 23:02:27 +08:00
Alex Lam S.L
0f2ef3367c enhance collapse_vars around lazy operations (#2369) 2017-10-19 04:52:00 +08:00
Alex Lam S.L
7e5b5cac97 fix AST_PropAccess in collapse_vars (take 3) (#2375)
Suppress scanning beyond assignment to `a.b`
2017-10-18 02:54:51 +08:00
Alex Lam S.L
c1346e06b7 clean up lazy operator detection (#2373) 2017-10-17 23:25:45 +08:00
Alex Lam S.L
0d2fe8e3ef fix AST_PropAccess in collapse_vars (take 2) (#2372)
fixes #2364
2017-10-17 22:59:15 +08:00
Alex Lam S.L
f2b9c11e2a fix AST_PropAccess in collapse_vars (#2370)
fixes #2364
2017-10-17 18:33:03 +08:00
Alex Lam S.L
fe647b083e account for side-effects from AST_This in collapse_vars (#2365) 2017-10-17 01:18:55 +08:00
Alex Lam S.L
dfe4f6c6de v3.1.4 2017-10-16 02:44:17 +08:00
Alex Lam S.L
a09c8ad666 update dependency (#2362)
- source-map@0.6.1
2017-10-16 02:41:22 +08:00
Alex Lam S.L
ec598c351b fix-ups for #2356 (#2360) 2017-10-15 22:33:55 +08:00
Alex Lam S.L
eba0f93bc0 more tests for #2351 (#2357) 2017-10-12 02:58:25 +08:00
Roger Peppe
99800d4aa9 update README to include defaults (#2356)
fixes #2353
2017-10-12 02:56:02 +08:00
Tim Malone
70d56c951a Update README.md - sourceMappingURL directive note (#2355)
Moves this README note to underneath the 'url' rather than 'root' option.
2017-10-11 19:48:43 +08:00
Alex Lam S.L
b810e2f8da perform reduce_vars on safe literals (#2351)
- constant expression
- single reference
- same scope
- not across loop body
2017-10-09 12:25:06 +08:00
Alex Lam S.L
1abe14296e collapse a.b whenever safe (#2350) 2017-10-08 13:17:48 +08:00
Alex Lam S.L
6920e898d1 v3.1.3 2017-10-01 12:36:07 +08:00
Alex Lam S.L
dd71639264 enhance reduce_vars for AST_Accessor (#2339)
fixes #2336
2017-10-01 03:01:50 +08:00
Alex Lam S.L
2dcc552ce0 trap invalid use of reserved words (#2338)
fixes #2337
2017-10-01 02:10:41 +08:00
Alex Lam S.L
55387e8fd0 v3.1.2 2017-09-24 02:02:04 +08:00
kzc
7e3e9da860 fix "use asm" numeric output (#2328)
fixes #2324
2017-09-21 04:42:40 +08:00
Alex Lam S.L
00f509405b suppress collapse_vars of this into "use strict" (#2326)
fixes #2319
2017-09-20 05:23:20 +08:00
Alex Lam S.L
aceb0af36b v3.1.1 2017-09-17 04:36:27 +08:00
Alex Lam S.L
4f0953f7e9 handle LHS side-effects on cascade & collapse_vars (#2314)
fixes #2313
2017-09-16 11:45:19 +08:00
Alex Lam S.L
182a47bfb1 improve source mapping (#2312)
fixes #2310
2017-09-15 12:46:48 +08:00
Alex Lam S.L
cd27f4ec38 v3.1.0 2017-09-10 15:17:24 +08:00
Mateusz Burzyński
8158b1bdcf Testing all leading comments against being PURE comments (#2305) 2017-09-10 02:08:15 +08:00
Alex Lam S.L
aacf3edc68 extend unsafe on pure global functions (#2303) 2017-09-07 22:08:34 +08:00
kzc
8b89072190 add Date and other known globals to unsafe compress option (#2302) 2017-09-07 02:44:26 +08:00
Alex Lam S.L
395a17ccda fix collapse_vars on default function argument (#2299)
Avoid collision with local variable `undefined` under certain corner cases.

fixes #2298
2017-09-04 02:32:33 +08:00
Alex Lam S.L
3f355866cf correctly count declarations after hoist_vars (#2297)
fixes #2295
2017-09-03 17:23:31 +08:00
David Šanda
71d52f147d Fix CLI example for mangle reserved list of names (#2294) 2017-08-31 00:55:32 +08:00
David Šanda
eb7adaa6fc Fix CLI source-maps examples (#2291)
fixes #2284
2017-08-29 23:49:20 +08:00
Alex Lam S.L
e5cf7972ea fix unused patching of AST_For.init blocks (#2289)
fixes #2288
2017-08-29 01:10:04 +08:00
Alex Lam S.L
f81ff10a9b v3.0.28 2017-08-20 00:27:01 +08:00
Erik Desjardins
16d40915b4 don't escape null characters as \0 when followed by any digit (#2273)
fixes #2272
2017-08-14 12:30:08 +08:00
Alex Lam S.L
e7c21e87e3 fix ie8 mangling of top-level AST_SymbolCatch (#2263)
fixes #2254
2017-08-01 02:38:32 +08:00
Alex Lam S.L
c4c2ef44d0 v3.0.27 2017-07-30 01:50:42 +08:00
Alex Lam S.L
a845897758 improve mangle.properties (#2261)
- include dead code when `keep_quoted`
- unify `keep_quoted` & `reserved`
- make `test/run-tests.js` consistent with `minify()`

fixes #2256
2017-07-29 23:02:04 +08:00
kzc
32ea2c5530 issue template: describe acceptable JS input (#2255) 2017-07-27 21:38:36 +08:00
Alex Lam S.L
bc61deeca9 v3.0.26 2017-07-23 12:39:36 +08:00
Alex Lam S.L
6a5e74b44e unescape surrogate pairs only (#2246)
fixes #2242
2017-07-23 12:38:21 +08:00
Alex Lam S.L
54446341ee update dependencies (#2241)
- acorn@5.1.1
- commander@2.11.0
- mocha@3.4.2
2017-07-16 16:20:40 +08:00
Alex Lam S.L
4e12a6f740 v3.0.25 2017-07-16 11:05:53 +08:00
Alex Lam S.L
b35dfc2599 reject malformed CLI parameters (#2239)
fixes #2237
2017-07-15 23:50:27 +08:00
Alex Lam S.L
9e1da9235e ensure ie8 works with mangled properties (#2238)
fixes #2234
2017-07-15 22:50:59 +08:00
Alex Lam S.L
a5ffe2c23f drop unused builtin globals under unsafe (#2236)
fixes #2233
2017-07-15 15:16:11 +08:00
Alex Lam S.L
9282e7b0c6 fix unsafe evaluate of Object static methods (#2232)
fixes #2231
2017-07-14 19:52:01 +08:00
Alex Lam S.L
5229cb2b1b drop unused compound assignments (#2230)
fixes #2226
2017-07-14 00:39:34 +08:00
Alex Lam S.L
458e3e15f0 enhance passes (#2229)
- remove hardcoded upper limit
- continue based on node count reduction
- emit verbose statistics

fixes #2226
2017-07-13 02:18:59 +08:00
Alex Lam S.L
c615a1e80a fix gzip stream in test/benchmark.js (#2228) 2017-07-12 02:55:57 +08:00
Alex Lam S.L
10a938cb79 enhance source mapping on IIFEs (#2224)
fixes #2213
2017-07-11 02:34:28 +08:00
Alex Lam S.L
4956ad311b benchmark gzipped output (#2220) 2017-07-09 01:44:59 +08:00
kzc
145874e504 docs: update benchmarks using node 8, add babili (#2218) 2017-07-09 01:06:15 +08:00
Alex Lam S.L
bd7be07c38 v3.0.24 2017-07-08 12:53:20 +08:00
Alex Lam S.L
71ee91e716 handle duplicate argument names in collapse_vars (#2215) 2017-07-08 04:42:35 +08:00
Alex Lam S.L
4f70d2e28c inlining of static methods & constants (#2211)
- guard by `unsafe`
- support `Array`, `Math`, `Number`, `Object` & `String`

fixes #2207
2017-07-07 05:35:32 +08:00
Alex Lam S.L
4b6ca5e742 inline property access of object literal (#2209)
- only if property value is side-effect-free
- guard by `unsafe`

fixes #2208
2017-07-06 21:51:58 +08:00
Alex Lam S.L
9306da3c58 suppress collapse_vars of this as call argument (#2204)
fixes #2203
2017-07-06 01:03:52 +08:00
Alex Lam S.L
1ac25fc032 improve compress granularity through typeofs (#2201)
fixes #2198
2017-07-05 19:20:33 +08:00
Alex Lam S.L
5f046c724b minor clean-ups to evaluate (#2197) 2017-07-03 18:52:39 +08:00
Alex Lam S.L
af0262b7e5 improve parenthesis emission (#2196)
- eliminate `throw` usages
- suppress extraneous parenthesis
  - `new function() {foo.bar()}.baz`
  - `for (function() { "foo" in bar; };;);`
2017-07-03 04:17:37 +08:00
Alex Lam S.L
6b3aeff1d8 clean up TreeWalker.pop() (#2195)
Remove superfluous parameter.
2017-07-03 03:23:38 +08:00
Alex Lam S.L
20e4f8277f refactor throw usage within compress (#2193)
Eliminate exceptional constructs from normal control flow.
2017-07-03 02:10:56 +08:00
kzc
f3a487a368 document fast mangle-only minify mode (#2194) 2017-07-03 01:37:04 +08:00
Alex Lam S.L
2dde41615a v3.0.23 2017-07-02 17:24:22 +08:00
Alex Lam S.L
8b69a3d18e drop argument value after collapse_vars (#2190) 2017-07-02 04:28:11 +08:00
Alex Lam S.L
d40950b741 improve inline efficiency (#2188)
... by teaching `collapse_vars` some new tricks.

fixes #2187
2017-07-02 01:05:14 +08:00
Alex Lam S.L
7659ea1d2e v3.0.22 2017-06-30 11:18:34 +08:00
Alex Lam S.L
bdeadffbf5 improve usability of name cache under minify() (#2176)
fixes #2174
2017-06-29 12:48:34 +08:00
Alex Lam S.L
5e6f26445f v3.0.21 2017-06-29 00:49:06 +08:00
Alex Lam S.L
f0a99125ee improve unsafe_Func (#2171)
- minimise disturbance to `compute_char_frequency()`
- remove extraneous quotation marks
2017-06-27 23:53:42 +08:00
Alex Lam S.L
1e4de2e6d3 parse @global_defs as expressions (#2169)
- let parser rejects non-conformant input
- eliminate need for extraneous parenthesis
2017-06-27 10:31:19 +08:00
Alex Lam S.L
8b4dcd8f3e v3.0.20 2017-06-25 15:05:05 +08:00
Alex Lam S.L
285401ced8 more tests for #2158 (#2160) 2017-06-25 14:21:48 +08:00
Alex Lam S.L
9db4c42380 fix cascade & collapse on property access of constants (#2158) 2017-06-24 21:30:06 +08:00
Alex Lam S.L
94e5e00c03 refactor compute_char_frequency() (#2152)
- minimise maintenance when updating AST
- maximise code sharing between `master` & `harmony`
2017-06-23 20:05:31 +08:00
Alex Lam S.L
dc6bcaa18e synchronise mangle.properties for minify() & test/compress (#2151) 2017-06-23 15:53:13 +08:00
Alex Lam S.L
d58b184835 refactor Compressor.toplevel (#2149) 2017-06-23 13:11:40 +08:00
Alex Lam S.L
b3a57ff019 minimise reduce_vars cloning overhead (#2148) 2017-06-23 06:59:53 +08:00
Alex Lam S.L
3d5bc08185 fix reduce_vars on this (#2145)
fixes #2140
2017-06-23 04:44:57 +08:00
Alex Lam S.L
0692435f01 fix for-in loop parsing (#2144) 2017-06-23 04:14:30 +08:00
Alex Lam S.L
f67a6b0e43 v3.0.19 2017-06-22 03:24:22 +08:00
Alex Lam S.L
343ea326c2 ensure mangling works if catch reuses a scope variable (#2123)
fixes #2120
2017-06-20 02:14:05 +08:00
Alex Lam S.L
1c150c632f v3.0.18 2017-06-18 15:01:20 +08:00
Alex Lam S.L
0a0f4f5591 make defensive copies when inline (#2116)
fixes #2114
2017-06-17 14:32:37 +08:00
Alex Lam S.L
931daa85bf fix loss of context in collapse_vars & cascade (#2112)
fixes #2110
2017-06-16 21:18:43 +08:00
Alex Lam S.L
00e4f7b3c1 in-place tigten_body() (#2111)
By reducing copies of `AST_Node` arrays, we should be able to reduce:
- memory pressure
- potential bugs caused by multiple references in AST
- duplicated executions of `OPT()`
2017-06-16 19:19:54 +08:00
Alex Lam S.L
11e63bc335 correctly determine scope of AST_This (#2109)
fixes #2107
2017-06-16 14:54:46 +08:00
Alex Lam S.L
33405bb24b enforce inline scope restriction (#2106)
fixes #2105
2017-06-16 03:21:38 +08:00
Alex Lam S.L
57dc4fb32f v3.0.17 2017-06-15 18:59:37 +08:00
Alex Lam S.L
b85a358deb suppress inline of this (#2103)
fixes #2101
2017-06-15 12:14:16 +08:00
Alex Lam S.L
43697958f3 avoid intermittent test time-out failures (#2100) 2017-06-15 04:47:57 +08:00
Alex Lam S.L
3f961bbba0 compute uses_arguments correctly in figure_out_scope() (#2099)
fixes #2097
2017-06-15 03:28:26 +08:00
Alex Lam S.L
0a1e523cd5 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:00:03 +08:00
Alex Lam S.L
4231f7323e v3.0.16 2017-06-14 16:45:09 +08:00
kzc
da2de350c3 add comment about quote_style and gzip (#2092) 2017-06-14 12:23:03 +08:00
Alex Lam S.L
41beae4dd7 cache web assets between CI runs (#2089)
- skip `test/jetstream.js` for `node@0.12`
2017-06-14 11:53:10 +08:00
Ziad El Khoury Hanna
82db9188ac fix CLI parsing of --source-map content (#2088)
fixes #2082
2017-06-13 16:30:46 +08:00
Alex Lam S.L
3dc9e140e4 add Node.js 8 to Travis CI (#2086)
- explicitly terminate `test/jetstream.js` upon completion
- log verbose output from `test/benchmark.js` & `test/jetstream.js`
- remove obsolete workaround for Travis CI
2017-06-13 06:21:16 +08:00
Alex Lam S.L
fed0096556 allow expect_stdout to specify Error (#2087) 2017-06-13 04:57:26 +08:00
Alex Lam S.L
2bdc8802dd fix variable accounting in inline (#2085)
fixes #2084
2017-06-13 01:40:14 +08:00
Alex Lam S.L
5ef7cb372a suppress false positives for-in loops (#2080)
fixes #2079
2017-06-10 13:55:17 +08:00
Alex Lam S.L
4ad7b1dae4 fix portability of sandbox.run_code() on Node.js 0.1x (#2078) 2017-06-10 01:08:58 +08:00
Alex Lam S.L
9186859cb7 fix non-string parameters (#2076)
`Stream.write()` is not as versatile as `console.log()`
2017-06-10 00:11:40 +08:00
Alex Lam S.L
47c0713747 report test/ufuzz.js failures in process.stderr (#2074) 2017-06-09 15:56:28 +08:00
Alex Lam S.L
293c566d6c marshal mangle[.properties].reserved from non-Array values (#2072) 2017-06-09 04:29:12 +08:00
Alex Lam S.L
9c306406f1 fix iteration over object with inherited properties (#2068)
fixes #2055
2017-06-08 03:27:03 +08:00
Alex Lam S.L
9db0695b10 fix cascade on multi-branch evaluations (#2067)
Partially reverts #2059 as this has better coverage and performance.

fixes #2062
2017-06-07 19:52:01 +08:00
Alex Lam S.L
f2af093402 fix CLI output corruption (#2061)
Using `console.error()` & `console.log()` result in inconsistent formatting across Node.js versions.

Avoid this issue by directly writing to `process.stderr` & `process.stdout` instead.

Miscellaneous
- prettify invalid option listing
2017-06-07 04:25:32 +08:00
Alex Lam S.L
b9ad53d1ab fix inline handling of AST_Call.args (#2059) 2017-06-06 22:55:25 +08:00
Alex Lam S.L
b0eab71470 implement test/jetstream.js --debug (#2058) 2017-06-06 19:28:12 +08:00
Alex Lam S.L
3493a182b2 implement function inlining (#2053)
- empty body
- single `AST_Return`
- single `AST_SimpleStatement`
- avoid `/*#__PURE__*/`

Miscellaneous
- enhance single-use function substitution

fixes #281
2017-06-06 05:49:53 +08:00
Alex Lam S.L
27c5284d3d workaround webkit parsing error (#2056)
apply `webkit` to jetstream tests
2017-06-06 04:06:42 +08:00
Alex Lam S.L
540220b91b fix AST_Function scope invariance (#2052)
improve function name hack in `run_code()`
2017-06-04 19:27:43 +08:00
Alex Lam S.L
84634da4b5 add tests for AST_SymbolAccessor (#2049) 2017-06-03 16:08:10 +08:00
Alex Lam S.L
1743621889 clean up lib/parse.js (#2047)
- remove unused definitions
- replace `array_to_hash()`
2017-06-03 14:00:59 +08:00
kzc
f330ab743a better document behavior of unsafe_Func (#2043) 2017-06-02 12:07:17 +08:00
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
74 changed files with 10515 additions and 1698 deletions

View File

@@ -8,7 +8,16 @@
**Uglify version (`uglifyjs -V`)**
**JavaScript input** <!-- ideally as small as possible -->
**JavaScript input**
<!--
A complete parsable JS program exhibiting the issue with
UglifyJS alone - without third party tools or libraries.
Ideally the input should be as small as possible.
Post a link to a gist if necessary.
Issues without a reproducible test case will be closed.
-->
**The `uglifyjs` CLI command executed or `minify()` options used.**

View File

@@ -4,8 +4,11 @@ node_js:
- "0.12"
- "4"
- "6"
- "8"
env:
- UGLIFYJS_TEST_ALL=1
matrix:
fast_finish: true
sudo: false
cache:
directories: tmp

376
README.md
View File

@@ -111,7 +111,7 @@ a double dash to prevent input files being used as option arguments:
By default UglifyJS will not try to be IE-proof.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--name-cache File to hold mangled name mappings.
--name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
`base` Path to compute relative paths from input files.
@@ -150,19 +150,19 @@ debugging your compressed JavaScript. To get a source map, pass
Additional options:
- `--source-map filename=<NAME>` to specify the name of the source map.
- `--source-map "filename='<NAME>'"` to specify the name of the source map.
- `--source-map root=<URL>` to pass the URL where the original files can be found.
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
- `--source-map "url='<URL>'"` to specify the URL where the source map can be found.
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
`//# sourceMappingURL=` directive.
- `--source-map url=<URL>` to specify the URL where the source map can be found.
For example:
uglifyjs js/file1.js js/file2.js \
-o foo.min.js -c -m \
--source-map root="http://foo.com/src",url=foo.min.js.map
--source-map "root='http://foo.com/src',url='foo.min.js.map'"
The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
@@ -181,8 +181,8 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
compressed JS by mapping every token in the compiled JS to its original
location.
To use this feature pass `--source-map content="/path/to/input/source.map"`
or `--source-map content=inline` if the source map is included inline with
To use this feature pass `--source-map "content='/path/to/input/source.map'"`
or `--source-map "content=inline"` if the source map is included inline with
the sources.
## CLI compress options
@@ -203,17 +203,15 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `toplevel` mangle names declared in the top level scope (disabled by
default).
- `toplevel` (default `false`) -- mangle names declared in the top level scope.
- `eval` mangle names visible in scopes where `eval` or `with` are used
(disabled by default).
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used.
When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=[$,require,exports]
uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed.
@@ -223,7 +221,7 @@ to prevent the `require`, `exports` and `$` names from being changed.
is a separate step, different from variable name mangling. Pass
`--mangle-props` to enable it. It will mangle all properties in the
input code with the exception of built in DOM properties and properties
in core javascript classes. For example:
in core JavaScript classes. For example:
```javascript
// example.js
@@ -238,7 +236,7 @@ x.bar_ = 2;
x["baz_"] = 3;
console.log(x.calc());
```
Mangle all properties (except for javascript `builtins`):
Mangle all properties (except for JavaScript `builtins`):
```bash
$ uglifyjs example.js -c -m --mangle-props
```
@@ -383,7 +381,47 @@ var code = {
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
console.log(result.code);
// console.log(function(n,o){return n+o}(3,7));
// console.log(3+7);
```
The `nameCache` option:
```javascript
var options = {
mangle: {
toplevel: true,
},
nameCache: {}
};
var result1 = UglifyJS.minify({
"file1.js": "function add(first, second) { return first + second; }"
}, options);
var result2 = UglifyJS.minify({
"file2.js": "console.log(add(1 + 2, 3 + 4));"
}, options);
console.log(result1.code);
// function n(n,r){return n+r}
console.log(result2.code);
// console.log(n(3,7));
```
You may persist the name cache to the file system in the following way:
```javascript
var cacheFileName = "/tmp/cache.json";
var options = {
mangle: {
properties: true,
},
nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
};
fs.writeFileSync("part1.js", UglifyJS.minify({
"file1.js": fs.readFileSync("file1.js", "utf8"),
"file2.js": fs.readFileSync("file2.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync("part2.js", UglifyJS.minify({
"file3.js": fs.readFileSync("file3.js", "utf8"),
"file4.js": fs.readFileSync("file4.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");
```
An example of a combination of `minify()` options:
@@ -461,13 +499,22 @@ if (result.error) throw result.error;
- `toplevel` (default `false`) - set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `ie8` (default `false`) - set to `true` to support IE8.
- `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`.
## Minify options structure
```javascript
{
warnings: false,
parse: {
// parse options
},
@@ -487,8 +534,10 @@ if (result.error) throw result.error;
sourceMap: {
// source map options
},
nameCache: null, // or specify a name cache object
toplevel: false,
ie8: false,
warnings: false,
}
```
@@ -542,104 +591,87 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements
- `html5_comments` (default `true`)
- `shebang` (default `true`) -- support `#!command` as the first line
## Compress options
- `sequences` (default: true) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `booleans` (default: `true`) -- various optimizations for boolean context,
for example `!!a ? b : c → a ? b : c`
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `cascade` (default: `true`) -- small optimization for sequences, transform
`x, x` into `x` and `x = something(), x` into `x = something()`
- `dead_code` -- remove unreachable code
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables,
side effects permitting.
- `drop_debugger` -- remove `debugger;` statements
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: false) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`.
- `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)`
- `unsafe_regexp` (default: false) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
- `comparisons` -- apply certain optimizations to binary nodes, for example:
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
e.g. `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `evaluate` -- attempt to evaluate constant expressions
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
expressions
- `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c`
- `dead_code` (default: `true`) -- remove unreachable code
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `drop_console` (default: `false`) -- Pass `true` to discard calls to
`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.
- `unused` -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the top level scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `hoist_funs` -- hoist function declarations
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
- `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
and the `compress` option `toplevel` enabled.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
- `if_return` -- optimizations for if/return and if/continue
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `join_vars` -- join consecutive `var` statements
- `inline` (default: `true`) -- embed simple functions
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `collapse_vars` -- Collapse single-use non-constant variables - side
effects permitting.
- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused
function arguments. You need this for code which relies on `Function.length`.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `negate_iife` -- negate "Immediately-Called Function Expressions"
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
- `pure_getters` -- the default is `false`. If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `passes` (default: `1`) -- The maximum number of times to run compress.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `pure_funcs` -- default `null`. You can pass an array of names and
- `properties` (default: `true`) -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `pure_funcs` (default: `null`) -- You can pass an array of names and
UglifyJS will assume that those functions do not produce side
effects. DANGER: will not check if the name is redefined in scope.
An example case here, for instance `var q = Math.floor(a/b)`. If
@@ -650,47 +682,90 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
statement would get discarded. The current implementation adds some
overhead (compression will be slower).
- `drop_console` -- default `false`. Pass `true` to discard calls to
`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.
- `pure_getters` (default: `"strict"`) -- If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `expression` -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `reduce_funcs` (default: `true`) -- Allows single-use functions to be
inlined as function expressions when permissible allowing further
optimization. Enabled by default. Option depends on `reduce_vars`
being enabled. Some code runs faster in the Chrome V8 engine if this
option is disabled. Does not negatively impact other major browsers.
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
used as constant values.
- `keep_fnames` -- default `false`. Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `sequences` (default: `true`) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `passes` -- default `1`. Number of times to run compress with a maximum of 3.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `side_effects` -- default `true`. Pass `false` to disable potentially dropping
- `side_effects` (default: `true`) -- Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
both unreferenced functions and variables)
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`)
- `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `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)`
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`)
- `warnings` (default: `false`) -- display warnings when dropping unreachable
code or unused declarations etc.
## Mangle options
- `reserved` - pass an array of identifiers that should be excluded from mangling
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
- `toplevel` — mangle names declared in the top level scope (disabled by
default).
- `keep_fnames` (default `false`) -- Pass `true` to not mangle function names.
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options).
- `eval` — mangle names visible in scopes where eval or with are used
(disabled by default).
- `reserved` (default `[]`) -- Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `keep_fnames` -- default `false`. Pass `true` to not mangle
function names. Useful for code relying on `Function.prototype.name`.
See also: the `keep_fnames` [compress option](#compress-options).
- `toplevel` (default `false`) -- Pass `true` to mangle names declared in the
top level scope.
Examples:
@@ -716,16 +791,20 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
`reserved` array.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
names matching the regular expression.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `debug` (default: `false`) -— Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
- `debug` (default: `false`) -— Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
names matching the regular expression.
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
`reserved` array.
## Output options
The code generator tries to output shortest code possible by default. In
@@ -734,48 +813,67 @@ can pass additional arguments that control the code output:
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `beautify` (default `true`) -- whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all
comments, `"some"` to preserve some comments, a regular expression string
(e.g. `/^!/`) or a function.
- `indent_level` (default 4)
- `indent_start` (default 0) -- prefix all lines by that many spaces
- `indent_level` (default `4`)
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `inline_script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `preamble` (default `null`) -- when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `preserve_line` (default `false`) -- pass `true` to preserve lines, but it
only works if `beautify` is set to `false`.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
more double quotes in the string itself.
more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `semicolons` (default `true`) -- separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `width` (default 80) -- only takes effect when beautification is on, this
- `webkit` (default `false`) -- enable workarounds for WebKit bugs.
PhantomJS users should set this option to `true`.
- `width` (default `80`) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details.
@@ -821,7 +919,6 @@ when this flag is on:
- `new Object()``{}`
- `String(exp)` or `exp.toString()``"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `typeof foo == "undefined"` → `foo === void 0`
- `void 0``undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
@@ -974,3 +1071,30 @@ in total it's a bit more than just using UglifyJS's own parser.
[acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
### Uglify Fast Minify Mode
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most JavaScript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.24 mangle=false, compress=false | 316,600 | 85,245 | 0.70 |
| uglify-js@3.0.24 mangle=true, compress=false | 220,216 | 72,730 | 1.13 |
| butternut@0.4.6 | 217,568 | 72,738 | 1.41 |
| uglify-js@3.0.24 mangle=true, compress=true | 212,511 | 71,560 | 3.36 |
| babili@0.1.4 | 210,713 | 72,140 | 12.64 |
To enable fast minify mode from the CLI use:
```
uglifyjs file.js -m
```
To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```

View File

@@ -15,7 +15,7 @@ var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "enclosed", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
var skip_keys = [ "cname", "enclosed", "inlined", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
var files = {};
var options = {
compress: false,
@@ -30,23 +30,16 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = options[option];
var padding = "";
Object.keys(defs).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(defs[name]) ];
}).forEach(function(tokens) {
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
text.push(format_object(options[option]));
text.push("");
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true));
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true));
program.option("-p, --parse <options>", "Specify parser options.", parse_js());
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js());
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file.");
@@ -54,6 +47,7 @@ program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("d
program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--no-rename", "Disable symbol expansion.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--timings", "Display operations run time on STDERR.")
@@ -72,11 +66,13 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
"compress",
"ie8",
"mangle",
"rename",
"sourceMap",
"toplevel",
"wrap"
].forEach(function(name) {
if (name in program) {
if (name == "rename" && program[name]) return;
options[name] = program[name];
}
});
@@ -113,17 +109,8 @@ if (program.mangleProps) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
}
var cache;
if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
}
if (program.output == "ast") {
options.output = {
@@ -157,7 +144,7 @@ if (program.verbose) {
}
if (program.self) {
if (program.args.length) {
console.error("WARN: Ignoring input files since --self was passed");
print_error("WARN: Ignoring input files since --self was passed");
}
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) {
@@ -187,7 +174,7 @@ function convert_ast(fn) {
function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
print_error("WARN: " + msg);
};
if (program.timings) options.timings = true;
try {
@@ -216,7 +203,7 @@ function run() {
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1];
@@ -230,17 +217,17 @@ function run() {
line = line.slice(col - limit);
col = limit;
}
console.error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
}
if (ex.defs) {
console.error("Supported options:");
console.error(ex.defs);
print_error("Supported options:");
print_error(format_object(ex.defs));
}
fatal(ex);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
print(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
@@ -256,7 +243,7 @@ function run() {
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
@@ -270,21 +257,19 @@ function run() {
fs.writeFileSync(program.output + ".map", result.map);
}
} else {
console.log(result.code);
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
}
if (result.timings) for (var phase in result.timings) {
console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
}
function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message);
print_error(message);
process.exit(1);
}
@@ -328,7 +313,7 @@ function read_file(path, default_value) {
}
}
function parse_js(flag, constants) {
function parse_js(flag) {
return function(value, options) {
options = options || {};
try {
@@ -346,7 +331,7 @@ function parse_js(flag, constants) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (!constants) {
if (flag) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
@@ -369,37 +354,51 @@ function parse_js(flag, constants) {
}
}));
} catch(ex) {
options[value] = null;
if (flag) {
fatal("Error parsing arguments for '" + flag + "': " + value);
} else {
options[value] = null;
}
}
return options;
}
}
function parse_source_map() {
var parse = parse_js("sourceMap", true);
var parse = parse_js();
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var hasContent = options && "content" in options;
var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content);
print_error("INFO: Using input source map: " + settings.content);
settings.content = read_file(settings.content, settings.content);
}
return settings;
}
}
function to_cache(key) {
if (cache[key]) {
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
} else {
cache[key] = {
cname: -1,
props: new UglifyJS.Dictionary()
};
}
return cache[key];
}
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}
function format_object(obj) {
var lines = [];
var padding = "";
Object.keys(obj).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(obj[name]) ];
}).forEach(function(tokens) {
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
return lines.join("\n");
}
function print_error(msg) {
process.stderr.write(msg);
process.stderr.write("\n");
}
function print(txt) {
process.stdout.write(txt);
process.stdout.write("\n");
}

View File

@@ -134,11 +134,10 @@ var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
}, AST_Statement);
var AST_Directive = DEFNODE("Directive", "value scope quote", {
var AST_Directive = DEFNODE("Directive", "value quote", {
$documentation: "Represents a directive, like \"use strict\";",
$propdoc: {
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
scope: "[AST_Scope/S] The scope that this directive affects",
quote: "[string] the original quote character"
},
}, AST_Statement);
@@ -299,10 +298,9 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
directives: "[string*/S] an array of directives declared in this scope",
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
functions: "[Object/S] like `variables`, but only lists function declarations",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
@@ -354,11 +352,11 @@ var AST_Accessor = DEFNODE("Accessor", null, {
$documentation: "A setter/getter function. The `name` property is always null."
}, AST_Lambda);
var AST_Function = DEFNODE("Function", null, {
var AST_Function = DEFNODE("Function", "inlined", {
$documentation: "A function expression"
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", null, {
var AST_Defun = DEFNODE("Defun", "inlined", {
$documentation: "A function definition"
}, AST_Lambda);
@@ -859,7 +857,7 @@ TreeWalker.prototype = {
if (!ret && descend) {
descend.call(node);
}
this.pop(node);
this.pop();
return ret;
},
parent: function(n) {
@@ -873,9 +871,8 @@ TreeWalker.prototype = {
}
this.stack.push(node);
},
pop: function(node) {
this.stack.pop();
if (node instanceof AST_Lambda) {
pop: function() {
if (this.stack.pop() instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,23 @@ function set_shorthand(name, options, keys) {
}
}
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
cache.props = Dictionary.fromObject(cache.props);
}
}
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
@@ -35,8 +52,10 @@ function minify(files, options) {
ie8: false,
keep_fnames: false,
mangle: {},
nameCache: null,
output: {},
parse: {},
rename: undefined,
sourceMap: false,
timings: false,
toplevel: false,
@@ -46,13 +65,17 @@ function minify(files, options) {
var timings = options.timings && {
start: Date.now()
};
if (options.rename === undefined) {
options.rename = options.compress && options.mangle;
}
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
cache: options.nameCache && (options.nameCache.vars || {}),
eval: false,
ie8: false,
keep_fnames: false,
@@ -60,6 +83,21 @@ function minify(files, options) {
reserved: [],
toplevel: false,
}, true);
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") {
options.mangle.properties = {};
}
if (options.mangle.properties.keep_quoted) {
quoted_props = options.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
options.mangle.properties.reserved = quoted_props;
}
if (options.nameCache && !("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {};
}
}
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
@@ -86,7 +124,7 @@ function minify(files, options) {
}
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
for (var name in files) if (HOP(files, name)) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
@@ -97,14 +135,20 @@ function minify(files, options) {
}
toplevel = options.parse.toplevel;
}
if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props);
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.rename = Date.now();
if (options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
}
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
@@ -134,7 +178,7 @@ function minify(files, options) {
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) {
} else for (var name in files) if (HOP(files, name)) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}
@@ -153,12 +197,19 @@ function minify(files, options) {
}
}
}
if (options.nameCache && options.mangle) {
if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache);
if (options.mangle.properties && options.mangle.properties.cache) {
options.nameCache.props = to_json(options.mangle.properties.cache);
}
}
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
parse: 1e-3 * (timings.rename - timings.parse),
rename: 1e-3 * (timings.compress - timings.rename),
compress: 1e-3 * (timings.scope - timings.compress),
scope: 1e-3 * (timings.mangle - timings.scope),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),

View File

@@ -70,6 +70,7 @@ function OutputStream(options) {
semicolons : true,
shebang : true,
source_map : null,
webkit : false,
width : 80,
wrap_iife : false,
}, true);
@@ -108,7 +109,7 @@ function OutputStream(options) {
var current_pos = 0;
var OUTPUT = "";
function to_ascii(str, identifier) {
var to_utf8 = options.ascii_only ? function(str, identifier) {
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = ch.charCodeAt(0).toString(16);
if (code.length <= 2 && !identifier) {
@@ -119,6 +120,12 @@ function OutputStream(options) {
return "\\u" + code;
}
});
} : function(str) {
return str.replace(/[\ud800-\udbff](?![\udc00-\udfff])/g, function(ch) {
return "\\u" + ch.charCodeAt(0).toString(16);
}).replace(/(^|[^\ud800-\udbff])([\udc00-\udfff])/g, function(match, prefix, ch) {
return prefix + "\\u" + ch.charCodeAt(0).toString(16);
});
};
function make_string(str, quote) {
@@ -139,7 +146,7 @@ function OutputStream(options) {
case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff";
case "\0":
return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
return /[0-9]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
}
return s;
});
@@ -149,7 +156,7 @@ function OutputStream(options) {
function quote_double() {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
if (options.ascii_only) str = to_ascii(str);
str = to_utf8(str);
switch (options.quote_style) {
case 1:
return quote_single();
@@ -174,8 +181,7 @@ function OutputStream(options) {
function make_name(name) {
name = name.toString();
if (options.ascii_only)
name = to_ascii(name, true);
name = to_utf8(name, true);
return name;
};
@@ -432,7 +438,7 @@ function OutputStream(options) {
last : function() { return last },
semicolon : semicolon,
force_semicolon : force_semicolon,
to_ascii : to_ascii,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
@@ -476,13 +482,17 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
var in_directive = false;
var active_scope = null;
var use_asm = null;
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) {
use_asm = true;
var self = this, generator = self._codegen;
if (self instanceof AST_Scope) {
active_scope = self;
}
else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope;
}
function doit() {
self.add_comments(stream);
@@ -496,10 +506,11 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
if (self === use_asm) {
use_asm = null;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);
@@ -597,6 +608,13 @@ function OutputStream(options) {
return true;
}
if (output.option('webkit')) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
return true;
}
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
@@ -663,14 +681,15 @@ function OutputStream(options) {
// parens around it too, otherwise the call will be
// interpreted as passing the arguments to the upper New
// expression.
try {
this.walk(new TreeWalker(function(node){
if (node instanceof AST_Call) throw p;
}));
} catch(ex) {
if (ex !== p) throw ex;
return true;
}
var parens = false;
this.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Call) {
parens = true;
return true;
}
}));
return parens;
}
});
@@ -1064,19 +1083,17 @@ function OutputStream(options) {
});
function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output);
else try {
// need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Binary && node.operator == "in")
throw output;
}));
node.print(output);
} catch(ex) {
if (ex !== output) throw ex;
node.print(output, true);
}
var parens = false;
// need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60
if (noin) node.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
parens = true;
return true;
}
}));
node.print(output, parens);
};
DEFPRINT(AST_VarDef, function(self, output){
@@ -1096,6 +1113,9 @@ function OutputStream(options) {
self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
}
output.with_parens(function(){
self.args.forEach(function(expr, i){
if (i) output.comma();
@@ -1135,15 +1155,23 @@ function OutputStream(options) {
DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
var prop = self.property;
if (output.option("ie8") && RESERVED_WORDS(prop)) {
output.print("[");
output.add_mapping(self.end);
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(prop);
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(self.property);
});
DEFPRINT(AST_Sub, function(self, output){
self.expression.print(output);
@@ -1234,9 +1262,8 @@ function OutputStream(options) {
});
else output.print("{}");
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
var key = self.key;
var quote = self.quote;
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
@@ -1253,20 +1280,24 @@ function OutputStream(options) {
} else {
output.print_string(key, quote);
}
}
DEFPRINT(AST_ObjectKeyVal, function(self, output){
print_property_name(self.key, self.quote, output);
output.colon();
self.value.print(output);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
output.print("set");
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
output.print(type);
output.space();
self.key.print(output);
self.value._do_print(output, true);
print_property_name(this.key.name, this.quote, output);
this.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", output);
});
DEFPRINT(AST_ObjectGetter, function(self, output){
output.print("get");
output.space();
self.key.print(output);
self.value._do_print(output, true);
self._print_getter_setter("get", output);
});
DEFPRINT(AST_Symbol, function(self, output){
var def = self.definition();
@@ -1296,9 +1327,7 @@ function OutputStream(options) {
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) {
str = output.to_ascii(str);
}
str = output.to_utf8(str);
output.print(str);
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)

View File

@@ -115,8 +115,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
/* -----[ Tokenizer ]----- */
// regexps adapted from http://xregexp.com/plugins/#unicode
@@ -684,9 +682,7 @@ var PRECEDENCE = (function(a, ret){
{}
);
var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
/* -----[ Parser ]----- */
@@ -1014,8 +1010,12 @@ function parse($TEXT, options) {
? (next(), var_(true))
: expression(true, true);
if (is("operator", "in")) {
if (init instanceof AST_Var && init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop");
if (init instanceof AST_Var) {
if (init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
} else if (!is_assignable(init)) {
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
}
next();
return for_in(init);
}
@@ -1054,6 +1054,8 @@ function parse($TEXT, options) {
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
if (in_statement && !name)
unexpected();
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
unexpected(prev());
expect("(");
var argnames = [];
for (var first = true; !is("punc", ")");) {
@@ -1222,7 +1224,6 @@ function parse($TEXT, options) {
var tok = S.token, ret;
switch (tok.type) {
case "name":
case "keyword":
ret = _make_symbol(AST_SymbolRef);
break;
case "num":
@@ -1252,13 +1253,6 @@ function parse($TEXT, options) {
break;
}
break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
}
next();
return ret;
@@ -1292,7 +1286,7 @@ function parse($TEXT, options) {
func.end = prev();
return subscripts(func, allow_calls);
}
if (ATOMIC_START_TOKEN[S.token.type]) {
if (ATOMIC_START_TOKEN(S.token.type)) {
return subscripts(as_atom_node(), allow_calls);
}
unexpected();

View File

@@ -67,6 +67,34 @@ function find_builtins(reserved) {
}
}
function reserve_quoted_keys(ast, reserved) {
function add(name) {
push_uniq(reserved, name);
}
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
}));
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
}
function mangle_properties(ast, options) {
options = defaults(options, {
builtins: false,
@@ -76,9 +104,10 @@ function mangle_properties(ast, options) {
only_cache: false,
regex: null,
reserved: null,
});
}, true);
var reserved = options.reserved || [];
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var cache = options.cache;
@@ -90,7 +119,6 @@ function mangle_properties(ast, options) {
}
var regex = options.regex;
var keep_quoted = options.keep_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
@@ -103,12 +131,11 @@ function mangle_properties(ast, options) {
var names_to_mangle = [];
var unmangleable = [];
var to_keep = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote);
add(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
@@ -118,15 +145,14 @@ function mangle_properties(ast, options) {
add(node.property);
}
else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted);
addStrings(node.property, add);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote))
node.key = mangle(node.key);
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
@@ -135,22 +161,9 @@ function mangle_properties(ast, options) {
else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
if (!keep_quoted)
node.property = mangleStrings(node.property);
else if (!options.keep_quoted && node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
// file : node.start.file,
// line : node.start.line,
// col : node.start.col,
// prop : node.value
// }
// );
// }
// }
}));
// only function declarations after this line
@@ -166,19 +179,13 @@ function mangle_properties(ast, options) {
}
function should_mangle(name) {
if (keep_quoted && name in to_keep) return false;
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
function add(name, keep) {
if (keep) {
to_keep[name] = true;
return;
}
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
@@ -198,19 +205,16 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) {
if (can_mangle(debug_mangled)) {
mangled = debug_mangled;
}
}
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
// check so we don't collide with a quoted name.
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep);
} while (!can_mangle(mangled));
}
cache.props.set(name, mangled);
@@ -218,32 +222,6 @@ function mangle_properties(ast, options) {
return mangled;
}
function addStrings(node, keep) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) {
walk(node.expressions[node.expressions.length - 1]);
return true;
}
if (node instanceof AST_String) {
add(node.value, keep);
return true;
}
if (node instanceof AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) {

View File

@@ -43,15 +43,16 @@
"use strict";
function SymbolDef(scope, index, orig) {
function SymbolDef(scope, orig) {
this.name = orig.name;
this.orig = [ orig ];
this.eliminated = 0;
this.scope = scope;
this.references = [];
this.replaced = 0;
this.global = false;
this.mangled_name = null;
this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++;
};
@@ -79,7 +80,7 @@ SymbolDef.prototype = {
if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
@@ -87,6 +88,9 @@ SymbolDef.prototype = {
cache.set(this.name, this.mangled_name);
}
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
};
@@ -197,16 +201,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
}
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
}
node.thedef = sym;
node.reference(options);
return true;
}
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
});
self.walk(tw);
@@ -223,6 +236,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
ref.reference(options);
});
node.thedef = def;
node.reference(options);
return true;
}
}));
@@ -238,7 +252,7 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, globals.size(), node);
var g = new SymbolDef(this, node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
@@ -299,7 +313,7 @@ AST_Scope.DEFMETHOD("def_function", function(symbol){
AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol);
def = new SymbolDef(this, symbol);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
} else {
@@ -317,7 +331,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling.
if (options.reserved.indexOf(m) >= 0) continue;
if (member(m, options.reserved)) continue;
// we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in
@@ -349,7 +363,8 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
var def = this.definition();
return !def || def.unmangleable(options);
});
// labels are always mangleable
@@ -360,14 +375,6 @@ AST_Symbol.DEFMETHOD("unreferenced", function(){
&& !(this.scope.uses_eval || this.scope.uses_with);
});
AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
AST_LabelRef.DEFMETHOD("undeclared", return_false);
AST_Label.DEFMETHOD("undeclared", return_false);
AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;
});
@@ -376,22 +383,23 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
options = defaults(options, {
eval : false,
ie8 : false,
keep_fnames : false,
reserved : [],
toplevel : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments
push_uniq(options.reserved, "arguments");
return options;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.reserved.push('arguments');
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
@@ -400,11 +408,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var to_mangle = [];
if (options.cache) {
this.globals.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
}
});
this.globals.each(collect);
}
var tw = new TreeWalker(function(node, descend){
@@ -416,13 +420,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Scope) {
var p = tw.parent(), a = [];
node.variables.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) {
a.push(symbol);
}
});
to_mangle.push.apply(to_mangle, a);
node.variables.each(collect);
return;
}
if (node instanceof AST_Label) {
@@ -442,107 +440,141 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (options.cache) {
options.cache.cname = this.cname;
}
function collect(symbol) {
if (!member(symbol.name, options.reserved)) {
to_mangle.push(symbol);
}
}
});
AST_Toplevel.DEFMETHOD("find_unique_prefix", function(options) {
var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
var cache = options.cache && options.cache.props;
var prefixes = Object.create(null);
options.reserved.forEach(add_prefix);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
}));
var prefix, i = 0;
do {
prefix = create_name(i++);
} while (prefixes[prefix]);
return prefix;
function add_prefix(name) {
if (/[0-9]$/.test(name)) {
prefixes[name.replace(/[0-9]+$/, "")] = true;
}
}
function add_def(def) {
var name = def.name;
if (def.global && cache && cache.has(name)) name = cache.get(name);
else if (!def.unmangleable(options)) return;
add_prefix(name);
}
function create_name(num) {
var name = "";
do {
name += letters[num % letters.length];
num = Math.floor(num / letters.length);
} while (num);
return name;
}
});
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
options = this._default_mangler_options(options);
var prefix = this.find_unique_prefix(options);
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
}));
function rename(def) {
if (def.global || def.unmangleable(options)) return;
if (member(def.name, options.reserved)) return;
var d = def.redefined();
def.name = d ? d.name : prefix + def.id;
def.orig.forEach(function(sym) {
sym.name = def.name;
});
def.references.forEach(function(sym) {
sym.name = def.name;
});
}
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());
else if (node instanceof AST_Return)
base54.consider("return");
else if (node instanceof AST_Throw)
base54.consider("throw");
else if (node instanceof AST_Continue)
base54.consider("continue");
else if (node instanceof AST_Break)
base54.consider("break");
else if (node instanceof AST_Debugger)
base54.consider("debugger");
else if (node instanceof AST_Directive)
base54.consider(node.value);
else if (node instanceof AST_While)
base54.consider("while");
else if (node instanceof AST_Do)
base54.consider("do while");
else if (node instanceof AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
}
else if (node instanceof AST_Var)
base54.consider("var");
else if (node instanceof AST_Lambda)
base54.consider("function");
else if (node instanceof AST_For)
base54.consider("for");
else if (node instanceof AST_ForIn)
base54.consider("for in");
else if (node instanceof AST_Switch)
base54.consider("switch");
else if (node instanceof AST_Case)
base54.consider("case");
else if (node instanceof AST_Default)
base54.consider("default");
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + node.key);
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + node.key);
else if (node instanceof AST_ObjectKeyVal)
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
};
base54.consider(this.print_to_string(), 1);
} finally {
AST_Node.prototype.print = AST_Node.prototype._print;
}
base54.sort();
function skip_string(node) {
if (node instanceof AST_String) {
base54.consider(node.value, -1);
} else if (node instanceof AST_Conditional) {
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
}
}
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
var digits = "0123456789".split("");
var chars, frequency;
function reset() {
frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
chars.forEach(function(ch){ frequency[ch] = 0 });
leading.forEach(function(ch) {
frequency[ch] = 0;
});
digits.forEach(function(ch) {
frequency[ch] = 0;
});
}
base54.consider = function(str){
base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i);
if (code in frequency) ++frequency[code];
frequency[str[i]] += delta;
}
};
function compare(a, b) {
return frequency[b] - frequency[a];
}
base54.sort = function() {
chars = mergeSort(chars, function(a, b){
if (is_digit(a) && !is_digit(b)) return 1;
if (is_digit(b) && !is_digit(a)) return -1;
return frequency[b] - frequency[a];
});
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
};
base54.reset = reset;
reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += String.fromCharCode(chars[num % base]);
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);

View File

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

View File

@@ -43,13 +43,6 @@
"use strict";
function array_to_hash(a) {
var ret = Object.create(null);
for (var i = 0; i < a.length; ++i)
ret[a[i]] = true;
return ret;
};
function slice(a, start) {
return Array.prototype.slice.call(a, start || 0);
};

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.0.14",
"version": "3.2.0",
"engines": {
"node": ">=0.8.0"
},
@@ -29,13 +29,13 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.9.0",
"source-map": "~0.5.1"
"commander": "~2.12.1",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.0.3",
"mocha": "~2.3.4",
"semver": "~5.3.0"
"acorn": "~5.2.1",
"mocha": "~3.5.1",
"semver": "~5.4.1"
},
"scripts": {
"test": "node test/run-tests.js"

View File

@@ -4,7 +4,9 @@
"use strict";
var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork;
var zlib = require("zlib");
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc");
@@ -32,6 +34,7 @@ function done() {
console.log(info.log);
console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes");
console.log("GZipped: ", info.gzip, "bytes");
console.log("SHA1 sum:", info.sha1);
if (info.code) {
failures.push(url);
@@ -50,15 +53,21 @@ urls.forEach(function(url) {
results[url] = {
input: 0,
output: 0,
gzip: 0,
log: ""
};
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
fetch(url, function(err, res) {
if (err) throw err;
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(zlib.createGzip({
level: zlib.Z_BEST_COMPRESSION
})).on("data", function(data) {
results[url].gzip += data.length;
}).pipe(createHash("sha1")).on("data", function(data) {
results[url].sha1 = data.toString("hex");
done();

View File

@@ -128,50 +128,114 @@ constant_join_3: {
for_loop: {
options = {
unsafe : true,
unused : true,
evaluate : true,
reduce_vars : true
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe: true,
unused: true,
};
input: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
var b = 0;
for (var i = 0; i < a.length; i++)
b += a[i];
return b;
}
function f1() {
var a = [1, 2, 3];
for (var i = 0, len = a.length; i < len; i++) {
console.log(a[i]);
}
var b = 0;
for (var i = 0, len = a.length; i < len; i++)
b += a[i];
return b;
}
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]++;
return a[2];
}
console.log(f0(), f1(), f2());
}
expect: {
function f0() {
var a = [1, 2, 3];
var b = 0;
for (var i = 0; i < 3; i++)
b += a[i];
return b;
}
function f1() {
var a = [1, 2, 3];
var b = 0;
for (var i = 0; i < 3; i++)
b += a[i];
return b;
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++)
a[i]++;
return a[2];
}
console.log(f0(), f1(), f2());
}
expect_stdout: "6 6 4"
}
index: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a[0], a[1]);
}
expect: {
console.log(1, 2);
}
expect_stdout: "1 2"
}
length: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a.length);
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
index_length: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a[0], a.length);
}
expect: {
console.log(1, 2);
}
expect_stdout: "1 2"
}

View File

@@ -13,7 +13,7 @@ ascii_only_true: {
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
}
}
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}'
expect_exact: 'function f(){return"\\x000\\x001\\x007\\x008\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}'
}
ascii_only_false: {
@@ -31,5 +31,5 @@ ascii_only_false: {
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
}
}
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
expect_exact: 'function f(){return"\\x000\\x001\\x007\\x008\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
}

View File

@@ -104,3 +104,65 @@ asm_mixed: {
}
}
asm_toplevel: {
options = {}
input: {
"use asm";
0.0;
function f() {
0.0;
(function(){
0.0;
});
}
0.0;
}
expect_exact: '"use asm";0.0;function f(){0.0;(function(){0.0})}0.0;'
}
asm_function_expression: {
options = {}
input: {
0.0;
var a = function() {
"use asm";
0.0;
}
function f() {
0.0;
return function(){
"use asm";
0.0;
}
0.0;
}
0.0;
}
expect_exact: '0;var a=function(){"use asm";0.0};function f(){0;return function(){"use asm";0.0};0}0;'
}
asm_nested_functions: {
options = {}
input: {
0.0;
function a() {
"use asm";
0.0;
}
0.0;
function b() {
0.0;
function c(){
"use asm";
0.0;
}
0.0;
function d(){
0.0;
}
0.0;
}
0.0;
}
expect_exact: '0;function a(){"use asm";0.0}0;function b(){0;function c(){"use asm";0.0}0;function d(){0}0}0;'
}

File diff suppressed because it is too large Load Diff

View File

@@ -73,4 +73,42 @@ dont_change_in_or_instanceof_expressions: {
1 instanceof 1;
null instanceof null;
}
}
}
self_comparison_1: {
options = {
comparisons: true,
}
input: {
a === a;
a !== b;
b.c === a.c;
b.c !== b.c;
}
expect: {
a == a;
a !== b;
b.c === a.c;
b.c != b.c;
}
}
self_comparison_2: {
options = {
comparisons: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
input: {
function f() {}
var o = {};
console.log(f != f, o === o);
}
expect: {
function f() {}
var o = {};
console.log(false, true);
}
expect_stdout: "false true"
}

View File

@@ -21,7 +21,7 @@ concat_1: {
var c = 1 + x() + 2 + "boo";
var d = 1 + x() + 2 + 3 + "boo";
var e = 1 + x() + 2 + "X3boo";
var f = "\x00360\08\0";
var f = "\x00360\x008\0";
}
}

View File

@@ -230,3 +230,188 @@ accessor: {
}
expect: {}
}
issue_2233_1: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
Array.isArray;
Boolean;
console.log;
Date;
decodeURI;
decodeURIComponent;
encodeURI;
encodeURIComponent;
Error.name;
escape;
eval;
EvalError;
Function.length;
isFinite;
isNaN;
JSON;
Math.random;
Number.isNaN;
parseFloat;
parseInt;
RegExp;
Object.defineProperty;
String.fromCharCode;
RangeError;
ReferenceError;
SyntaxError;
TypeError;
unescape;
URIError;
}
expect: {}
expect_stdout: true
}
global_timeout_and_interval_symbols: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
// These global symbols do not exist in the test sandbox
// and must be tested separately.
clearInterval;
clearTimeout;
setInterval;
setTimeout;
}
expect: {}
}
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
var RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
}
}
}
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
UndeclaredGlobal;
}
}
global_fns: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Boolean(1, 2);
decodeURI(1, 2);
decodeURIComponent(1, 2);
Date(1, 2);
encodeURI(1, 2);
encodeURIComponent(1, 2);
Error(1, 2);
escape(1, 2);
EvalError(1, 2);
isFinite(1, 2);
isNaN(1, 2);
Number(1, 2);
Object(1, 2);
parseFloat(1, 2);
parseInt(1, 2);
RangeError(1, 2);
ReferenceError(1, 2);
String(1, 2);
SyntaxError(1, 2);
TypeError(1, 2);
unescape(1, 2);
URIError(1, 2);
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect: {
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect_stdout: [
"SyntaxError",
"SyntaxError",
"RangeError",
]
}

View File

@@ -673,6 +673,7 @@ issue_1539: {
vardef_value: {
options = {
keep_fnames: false,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -734,6 +735,7 @@ assign_chain: {
issue_1583: {
options = {
keep_fargs: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -751,11 +753,11 @@ issue_1583: {
expect: {
function m(t) {
(function(e) {
t = (function() {
t = function() {
return (function(a) {
return a;
})(function(a) {});
})();
return function(a) {};
})();
}();
})();
}
}
@@ -1080,6 +1082,7 @@ var_catch_toplevel: {
options = {
conditionals: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -1090,6 +1093,7 @@ var_catch_toplevel: {
a--;
try {
a++;
x();
} catch(a) {
if (a) var a;
var a = 10;
@@ -1099,12 +1103,199 @@ var_catch_toplevel: {
}
expect: {
!function() {
a--;
try {
a++;
x();
} catch(a) {
var a;
}
}();
}
}
issue_2105_1: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
({
prop: function() {
console.log;
console.log("PASS");
}
}).prop();
}
expect_stdout: "PASS"
}
issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
properties: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_2226_1: {
options = {
side_effects: true,
unused: true,
}
input: {
function f1() {
var a = b;
a += c;
}
function f2(a) {
a <<= b;
}
function f3(a) {
--a;
}
function f4() {
var a = b;
return a *= c;
}
function f5(a) {
x(a /= b);
}
}
expect: {
function f1() {
b;
c;
}
function f2(a) {
b;
}
function f3(a) {
0;
}
function f4() {
var a = b;
return a *= c;
}
function f5(a) {
x(a /= b);
}
}
}
issue_2226_2: {
options = {
cascade: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(a, b) {
a += b;
return a;
}(1, 2));
}
expect: {
console.log(function(a, b) {
return a += b;
}(1, 2));
}
expect_stdout: "3"
}
issue_2226_3: {
options = {
collapse_vars: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(a, b) {
a += b;
return a;
}(1, 2));
}
expect: {
console.log(function(a, b) {
return a += 2;
}(1));
}
expect_stdout: "3"
}
issue_2288: {
options = {
unused: true,
}
beautify = {
beautify: true,
}
input: {
function foo(o) {
for (var j = o.a, i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
expect: {
function foo(o) {
o.a;
for (i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
}

View File

@@ -250,22 +250,27 @@ unsafe_constant: {
unsafe_object: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: 1 };
console.log(
({a:1}) + 1,
({a:1}).a + 1,
({a:1}).b + 1,
({a:1}).a.b + 1
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: 1 };
console.log(
({a:1}) + 1,
o + 1,
2,
({a:1}).b + 1,
o.b + 1,
1..b + 1
);
}
@@ -274,22 +279,27 @@ unsafe_object: {
unsafe_object_nested: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: { b: 1 } };
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
({a:{b:1}}).a.b + 1
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: { b: 1 } };
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
o + 1,
o.a + 1,
o.b + 1,
2
);
}
@@ -298,21 +308,26 @@ unsafe_object_nested: {
unsafe_object_complex: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: { b: 1 }, b: 1 };
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
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: { b: 1 }, b: 1 };
console.log(
({a:{b:1},b:1}) + 1,
({a:{b:1},b:1}).a + 1,
o + 1,
o.a + 1,
2,
2
);
@@ -322,22 +337,27 @@ unsafe_object_complex: {
unsafe_object_repeated: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: { b: 1 }, a: 1 };
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
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: { b: 1 }, a: 1 };
console.log(
({a:{b:1},a:1}) + 1,
o + 1,
2,
({a:{b:1},a:1}).b + 1,
o.b + 1,
1..b + 1
);
}
@@ -347,6 +367,7 @@ unsafe_object_repeated: {
unsafe_object_accessor: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe: true,
}
@@ -370,10 +391,11 @@ unsafe_object_accessor: {
}
}
unsafe_function: {
prop_function: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
properties: true,
side_effects: true,
}
input: {
console.log(
@@ -386,9 +408,9 @@ unsafe_function: {
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
({b:1}) + 1,
function(){} + 1,
2
);
}
expect_stdout: true
@@ -614,10 +636,11 @@ unsafe_string_bad_index: {
expect_stdout: true
}
unsafe_prototype_function: {
prototype_function: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
properties: true,
side_effects: true,
}
input: {
var a = ({valueOf: 0}) < 1;
@@ -636,14 +659,16 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf();
var h = "" + ({toString: 0});
var g = 0();
var h = 0();
}
}
call_args: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
@@ -665,7 +690,9 @@ call_args: {
call_args_drop_param: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -780,13 +807,15 @@ unsafe_charAt_noop: {
input: {
console.log(
s.charAt(0),
"string".charAt(x)
"string".charAt(x),
(typeof x).charAt()
);
}
expect: {
console.log(
s.charAt(0),
"string".charAt(x)
"string".charAt(x),
(typeof x)[0]
);
}
}
@@ -994,6 +1023,7 @@ Infinity_NaN_undefined_LHS: {
issue_1964_1: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
@@ -1001,6 +1031,7 @@ issue_1964_1: {
input: {
function f() {
var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1];
}
console.log(f());
@@ -1008,16 +1039,21 @@ issue_1964_1: {
expect: {
function f() {
var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
expect_stdout: [
"\\s",
"b",
]
}
issue_1964_2: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
@@ -1025,15 +1061,187 @@ issue_1964_2: {
input: {
function f() {
var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
console.log(/\s/.source);
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "b"
expect_stdout: [
"\\s",
"b",
]
}
array_slice_index: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([1,2,3].slice(1)[1]);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}
string_charCodeAt: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("foo".charCodeAt("bar".length));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}
issue_2207_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(String.fromCharCode(65));
console.log(Math.max(3, 6, 2, 7, 3, 4));
console.log(Math.cos(1.2345));
console.log(Math.cos(1.2345) - Math.sin(4.321));
console.log(Math.pow(Math.PI, Math.E - Math.LN10));
}
expect: {
console.log("A");
console.log(7);
console.log(Math.cos(1.2345));
console.log(1.2543732512566947);
console.log(1.6093984514472044);
}
expect_stdout: true
}
issue_2207_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect_stdout: true
}
issue_2207_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
console.log(Number.NaN);
console.log(Number.NEGATIVE_INFINITY);
console.log(Number.POSITIVE_INFINITY);
}
expect: {
console.log(Number.MAX_VALUE);
console.log(5e-324);
console.log(NaN);
console.log(-1/0);
console.log(1/0);
}
expect_stdout: true
}
issue_2231_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.keys(void 0));
}
expect: {
console.log(Object.keys(void 0));
}
expect_stdout: true
}
issue_2231_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.getOwnPropertyNames(null));
}
expect: {
console.log(Object.getOwnPropertyNames(null));
}
expect_stdout: true
}
self_comparison_1: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var o = { n: NaN };
console.log(o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n, typeof o.n);
}
expect: {
console.log(false, false, true, true, "number");
}
expect_stdout: "false false true true 'number'"
}
self_comparison_2: {
options = {
evaluate: true,
hoist_props: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { n: NaN };
console.log(o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n, typeof o.n);
}
expect: {
console.log(false, false, true, true, "number");
}
expect_stdout: "false false true true 'number'"
}

View File

@@ -19,8 +19,10 @@ iifes_returning_constants_keep_fargs_true: {
booleans : true,
if_return : true,
join_vars : true,
reduce_funcs : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -54,8 +56,10 @@ iifes_returning_constants_keep_fargs_false: {
booleans : true,
if_return : true,
join_vars : true,
reduce_funcs : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -82,6 +86,7 @@ issue_485_crashing_1530: {
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
}
input: {
(function(a) {
@@ -98,6 +103,7 @@ issue_1841_1: {
options = {
keep_fargs: false,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -124,6 +130,7 @@ issue_1841_2: {
options = {
keep_fargs: false,
pure_getters: false,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -148,11 +155,13 @@ issue_1841_2: {
function_returning_constant_literal: {
options = {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
unsafe: true,
side_effects: true,
toplevel: true,
evaluate: true,
cascade: true,
unused: true,
}
input: {
@@ -261,9 +270,287 @@ issue_203: {
}
expect: {
var m = {};
var fn = Function("a", "b", "b.exports=42");
var fn = Function("n,o", "o.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
expect_stdout: "42"
}
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
!function(c) {
c = 1 + c;
var c = 0;
function f14(a_1) {
if (c = 1 + c, 0 !== 23..toString())
c = 1 + c, a_1 && (a_1[0] = 0);
}
f14();
}(-1);
}();
console.log(c);
}
expect: {
var c = 0;
!function(c) {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(-1),
console.log(c);
}
expect_stdout: "0"
}
issue_2097: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}
f(1);
}
expect: {
!function() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}(1);
}
expect_stdout: "1"
}
issue_2101: {
options = {
inline: true,
}
input: {
a = {};
console.log(function() {
return function() {
return this.a;
}();
}() === function() {
return a;
}());
}
expect: {
a = {};
console.log(function() {
return this.a;
}() === a);
}
expect_stdout: "true"
}
inner_ref: {
options = {
inline: true,
unused: true,
}
input: {
console.log(function(a) {
return function() {
return a;
}();
}(1), function(a) {
return function(a) {
return a;
}();
}(2));
}
expect: {
console.log(function(a) {
return a;
}(1), function(a) {
return a;
}());
}
expect_stdout: "1 undefined"
}
issue_2107: {
options = {
cascade: true,
collapse_vars: true,
inline: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
c++;
}(c++ + new function() {
this.a = 0;
var a = (c = c + 1) + (c = 1 + c);
return c++ + a;
}());
console.log(c);
}
expect: {
var c = 0;
c++, new function() {
this.a = 0, c = 1 + (c += 1), c++;
}(), c++, console.log(c);
}
expect_stdout: "5"
}
issue_2114_1: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}
issue_2114_2: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
passes: 2,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}
issue_2428: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function bar(k) {
console.log(k);
}
function foo(x) {
return bar(x);
}
function baz(a) {
foo(a);
}
baz(42);
baz("PASS");
}
expect: {
function baz(a) {
console.log(a);
}
baz(42);
baz("PASS");
}
expect_stdout: [
"42",
"PASS",
]
}

View File

@@ -37,6 +37,7 @@ object: {
VALUE: 42,
},
},
side_effects: true,
unsafe: true,
}
input: {
@@ -140,9 +141,9 @@ mixed: {
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]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:128,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:130,8]',
]
}
@@ -174,3 +175,24 @@ issue_1986: {
console.log(42);
}
}
issue_2167: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
global_defs: {
"@isDevMode": "function(){}",
},
side_effects: true,
}
input: {
if (isDevMode()) {
greetOverlord();
}
doWork();
}
expect: {
doWork();
}
}

View File

@@ -0,0 +1,635 @@
issue_2377_1: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
var obj_foo = 1, obj_cube = function(x) {
return x * x * x;
};
console.log(obj_foo, obj_cube(3));
}
expect_stdout: "1 27"
}
issue_2377_2: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
console.log(1, function(x) {
return x * x * x;
}(3));
}
expect_stdout: "1 27"
}
issue_2377_3: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
console.log(1, 27);
}
expect_stdout: "1 27"
}
direct_access_1: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
var obj = {
a: 1,
b: 2,
};
for (var k in obj) a++;
console.log(a, obj.a);
}
expect: {
var a = 0;
var obj = {
a: 1,
b: 2,
};
for (var k in obj) a++;
console.log(a, obj.a);
}
expect_stdout: "2 1"
}
direct_access_2: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
var f = function(k) {
if (o[k]) return "PASS";
};
console.log(f("a"));
}
expect: {
var o = { a: 1 };
console.log(function(k) {
if (o[k]) return "PASS";
}("a"));
}
expect_stdout: "PASS"
}
direct_access_3: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
o.b;
console.log(o.a);
}
expect: {
var o = { a: 1 };
o.b;
console.log(o.a);
}
expect_stdout: "1"
}
single_use: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
bar: function() {
return 42;
},
};
console.log(obj.bar());
}
expect: {
console.log({
bar: function() {
return 42;
},
}.bar());
}
}
name_collision_1: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
input: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj = {
foo: 3,
bar: 4,
"b-r": 5,
"b+r": 6,
"b!r": 7,
};
console.log(obj_foo, obj.foo, obj.bar, obj["b-r"], obj["b+r"], obj["b!r"]);
}
f();
}
expect: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj_foo$0 = 3,
obj_bar = 4,
obj_b_r = 5,
obj_b_r$0 = 6,
obj_b_r$1 = 7;
console.log(obj_foo, obj_foo$0, obj_bar, obj_b_r, obj_b_r$0, obj_b_r$1);
}
f();
}
expect_stdout: "1 3 4 5 6 7"
}
name_collision_2: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
p: 1,
0: function(x) {
return x;
},
1: function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5), o__$0, o__$1);
}
expect: {
var o_p = 1,
o__ = function(x) {
return x;
},
o__$2 = function(x) {
return x + 1;
},
o__$0 = 2,
o__$1 = 3;
console.log(o_p === o_p, o__(4), o__$2(5), o__$0, o__$1);
}
expect_stdout: "true 4 6 2 3"
}
name_collision_3: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
p: 1,
0: function(x) {
return x;
},
1: function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5));
}
expect: {
var o_p = 1,
o__ = function(x) {
return x;
},
o__$2 = function(x) {
return x + 1;
},
o__$0 = 2,
o__$1 = 3;
console.log(o_p === o_p, o__(4), o__$2(5));
}
expect_stdout: "true 4 6"
}
contains_this_1: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p);
}
expect: {
console.log(1, 1);
}
expect_stdout: "1 1"
}
contains_this_2: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u);
}
expect: {
console.log(1, 1, function() {
return this === this;
});
}
expect_stdout: true
}
contains_this_3: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u());
}
expect: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u());
}
expect_stdout: "1 1 true"
}
new_this: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2,
f: function(a) {
this.b = a;
}
};
console.log(new o.f(o.a).b, o.b);
}
expect: {
console.log(new function(a) {
this.b = a;
}(1).b, 2);
}
expect_stdout: "1 2"
}
issue_2473_1: {
options = {
hoist_props: false,
reduce_vars: true,
top_retain: [ "x", "y" ],
toplevel: true,
unused: true,
}
input: {
var x = {};
var y = [];
var z = {};
}
expect: {
var x = {};
var y = [];
}
}
issue_2473_2: {
options = {
hoist_props: true,
reduce_vars: true,
top_retain: [ "x", "y" ],
toplevel: true,
unused: true,
}
input: {
var x = {};
var y = [];
var z = {};
}
expect: {
var x = {};
var y = [];
}
}
issue_2473_3: {
options = {
hoist_props: true,
reduce_vars: true,
top_retain: "o",
toplevel: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2,
};
console.log(o.a, o.b);
}
expect: {
var o = {
a: 1,
b: 2,
};
console.log(o.a, o.b);
}
expect_stdout: "1 2"
}
issue_2473_4: {
options = {
hoist_props: true,
reduce_vars: true,
top_retain: "o",
toplevel: true,
unused: true,
}
input: {
(function() {
var o = {
a: 1,
b: 2,
};
console.log(o.a, o.b);
})();
}
expect: {
(function() {
var o_a = 1, o_b = 2;
console.log(o_a, o_b);
})();
}
expect_stdout: "1 2"
}
issue_2508_1: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: [ 1 ],
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
(function(x) {
console.log(x);
})([ 1 ]);
}
expect_stdout: true
}
issue_2508_2: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: { b: 2 },
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
(function(x) {
console.log(x);
})({ b: 2 });
}
expect_stdout: true
}
issue_2508_3: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: [ o ],
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
var o = {
a: [ o ],
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect_stdout: true
}
issue_2508_4: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: { b: o },
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
var o = {
a: { b: o },
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect_stdout: true
}
issue_2508_5: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
f: function(x) {
console.log(x);
}
};
o.f(o.f);
}
expect: {
var o_f = function(x) {
console.log(x);
};
o_f(o_f);
}
expect_stdout: true
}

View File

@@ -88,3 +88,24 @@ sequences_funs: {
}
}
}
issue_2295: {
options = {
collapse_vars: true,
hoist_vars: true,
}
input: {
function foo(o) {
var a = o.a;
if (a) return a;
var a = 1;
}
}
expect: {
function foo(o) {
var a = o.a;
if (a) return a;
a = 1;
}
}
}

View File

@@ -71,11 +71,13 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]",
"WARN: pass 0: last_count: Infinity, count: 37",
"WARN: Dropping 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 unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
"WARN: pass 1: last_count: 37, count: 18",
]
}
@@ -109,11 +111,11 @@ non_hoisted_function_after_return_2b: {
}
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:95,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:95,16]",
"WARN: Dropping 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:97,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:99,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:99,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:103,12]",
]
}
@@ -151,10 +153,10 @@ non_hoisted_function_after_return_strict: {
}
expect_stdout: "8 7"
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]"
"WARN: Dropping unreachable code [test/compress/issue-1034.js:133,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:136,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:139,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:140,21]",
]
}
@@ -194,17 +196,19 @@ non_hoisted_function_after_return_2a_strict: {
}
expect_stdout: "5 6"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:175,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:175,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:175,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:182,21]",
"WARN: pass 0: last_count: Infinity, count: 48",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:180,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:180,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:183,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:178,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:180,16]",
"WARN: pass 1: last_count: 48, count: 29",
]
}
@@ -243,10 +247,10 @@ non_hoisted_function_after_return_2b_strict: {
expect_stdout: "5 6"
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:229,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:229,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:231,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:235,12]",
]
}

View File

@@ -1,6 +1,7 @@
const_pragma: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
};
@@ -16,6 +17,7 @@ const_pragma: {
not_const: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
};

View File

@@ -96,6 +96,13 @@ pure_function_calls_toplevel: {
})();
})();
// pure top-level calls will be dropped regardless of the leading comments position
var MyClass = /*#__PURE__*//*@class*/(function(){
function MyClass() {}
MyClass.prototype.method = function() {};
return MyClass;
})();
// comment #__PURE__ comment
bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g();
@@ -110,10 +117,12 @@ pure_function_calls_toplevel: {
"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:107,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:108,31]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:84,33]",
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,45]",
"WARN: Dropping unused variable MyClass [test/compress/issue-1261.js:100,12]",
]
}
@@ -148,29 +157,29 @@ should_warn: {
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: Condition left of || 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: Condition left of && 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]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:137,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:138,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:138,23]",
"WARN: Condition always true [test/compress/issue-1261.js:138,23]",
"WARN: Condition left of || always true [test/compress/issue-1261.js:139,8]",
"WARN: Condition always true [test/compress/issue-1261.js:139,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:140,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:140,23]",
"WARN: Condition always false [test/compress/issue-1261.js:140,23]",
"WARN: Condition left of && always false [test/compress/issue-1261.js:141,8]",
"WARN: Condition always false [test/compress/issue-1261.js:141,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:142,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
"WARN: Condition always true [test/compress/issue-1261.js:142,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,31]",
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:145,24]",
"WARN: Condition always true [test/compress/issue-1261.js:145,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:146,31]",
"WARN: Condition always false [test/compress/issue-1261.js:146,8]",
]
}

View File

@@ -1,6 +1,8 @@
issue_1321_no_debug: {
mangle_props = {
keep_quoted: true
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
var x = {};
@@ -10,17 +12,19 @@ issue_1321_no_debug: {
}
expect: {
var x = {};
x.b = 1;
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
x.x = 1;
x["a"] = 2 * x.x;
console.log(x.x, x["a"]);
}
expect_stdout: true
}
issue_1321_debug: {
mangle_props = {
keep_quoted: true,
debug: ""
mangle = {
properties: {
debug: "",
keep_quoted: true,
},
}
input: {
var x = {};
@@ -30,16 +34,18 @@ issue_1321_debug: {
}
expect: {
var x = {};
x.a = 1;
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
x.x = 1;
x["_$foo$_"] = 2 * x.x;
console.log(x.x, x["_$foo$_"]);
}
expect_stdout: true
}
issue_1321_with_quoted: {
mangle_props = {
keep_quoted: false
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
var x = {};
@@ -49,9 +55,9 @@ issue_1321_with_quoted: {
}
expect: {
var x = {};
x.a = 1;
x["b"] = 2 * x.a;
console.log(x.a, x["b"]);
x.x = 1;
x["o"] = 2 * x.x;
console.log(x.x, x["o"]);
}
expect_stdout: true
}

View File

@@ -1,6 +1,7 @@
typeof_eq_undefined: {
options = {
comparisons: true
comparisons: true,
typeofs: true,
}
input: {
var a = typeof b != "undefined";
@@ -24,6 +25,7 @@ typeof_eq_undefined_ie8: {
options = {
comparisons: true,
ie8: true,
typeofs: true,
}
input: {
var a = typeof b != "undefined";
@@ -45,7 +47,8 @@ typeof_eq_undefined_ie8: {
undefined_redefined: {
options = {
comparisons: true
comparisons: true,
typeofs: true,
}
input: {
function f(undefined) {
@@ -58,7 +61,8 @@ undefined_redefined: {
undefined_redefined_mangle: {
options = {
comparisons: true
comparisons: true,
typeofs: true,
}
mangle = {}
input: {

View File

@@ -2,6 +2,7 @@ chained_evaluation_1: {
options = {
collapse_vars: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -28,6 +29,7 @@ chained_evaluation_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@@ -15,6 +15,7 @@ f7: {
negate_iife: true,
passes: 3,
properties: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,

View File

@@ -1,5 +1,6 @@
side_effects_catch: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -34,6 +35,7 @@ side_effects_catch: {
side_effects_else: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -62,6 +64,7 @@ side_effects_else: {
side_effects_finally: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -98,6 +101,7 @@ side_effects_finally: {
side_effects_label: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -130,6 +134,7 @@ side_effects_label: {
side_effects_switch: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,

View File

@@ -1,5 +1,7 @@
mangle_props: {
mangle_props = {}
mangle = {
properties: true,
}
input: {
var obj = {
undefined: 1,
@@ -54,10 +56,12 @@ mangle_props: {
}
numeric_literal: {
mangle = {
properties: true,
}
beautify = {
beautify: true,
}
mangle_props = {}
input: {
var obj = {
0: 0,
@@ -82,7 +86,7 @@ numeric_literal: {
' 42: 2,',
' "42": 3,',
' 37: 4,',
' a: 5,',
' o: 5,',
' 1e42: 6,',
' b: 7,',
' "1e+42": 8',
@@ -92,7 +96,7 @@ numeric_literal: {
'',
'console.log(obj[42], obj["42"]);',
'',
'console.log(obj[37], obj["a"], obj[37], obj["37"]);',
'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
'',
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
]
@@ -105,7 +109,9 @@ numeric_literal: {
}
identifier: {
mangle_props = {}
mangle = {
properties: true,
}
input: {
var obj = {
abstract: 1,
@@ -173,32 +179,32 @@ identifier: {
}
expect: {
var obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
f: 6,
g: 7,
h: 8,
i: 9,
j: 10,
k: 11,
l: 12,
m: 13,
n: 14,
o: 15,
p: 16,
q: 17,
r: 18,
s: 19,
t: 20,
u: 21,
v: 22,
w: 23,
x: 24,
y: 25,
z: 26,
e: 1,
t: 2,
n: 3,
a: 4,
i: 5,
o: 6,
r: 7,
l: 8,
s: 9,
c: 10,
f: 11,
u: 12,
d: 13,
h: 14,
p: 15,
b: 16,
v: 17,
w: 18,
y: 19,
g: 20,
m: 21,
k: 22,
x: 23,
j: 24,
z: 25,
q: 26,
A: 27,
B: 28,
C: 29,
@@ -229,11 +235,11 @@ identifier: {
Z: 54,
$: 55,
_: 56,
aa: 57,
ba: 58,
ca: 59,
da: 60,
ea: 61,
ee: 57,
te: 58,
ne: 59,
ae: 60,
ie: 61,
};
}
}

View File

@@ -1,6 +1,8 @@
unary_prefix: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@@ -1,6 +1,7 @@
iife_for: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -26,6 +27,7 @@ iife_for: {
iife_for_in: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -51,6 +53,7 @@ iife_for_in: {
iife_do: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -80,6 +83,7 @@ iife_do: {
iife_while: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,

498
test/compress/issue-281.js Normal file
View File

@@ -0,0 +1,498 @@
collapse_vars_constants: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function f1(x) {
var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
return b + (function() { return d - a * e - c; })();
}
function f2(x) {
var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
return b + (function() { return -a * e - c; })();
}
}
expect: {
function f1(x) {
var b = x.prop, d = sideeffect1(), e = sideeffect2();
return b + (d - 4 * e - 5);
}
function f2(x) {
var b = x.prop;
sideeffect1();
return b + (-4 * sideeffect2() - 5);
}
}
}
modified: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f5(1));
}
expect: {
function f5(b) {
var a = b;
return b++ + a;
}
console.log(f5(1));
}
expect_stdout: "2"
}
ref_scope: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
console.log(function() {
var a = 1, b = 2, c = 3;
var a = c++, b = b /= a;
return function() {
return a;
}() + b;
}());
}
expect: {
console.log(function() {
var a = 1, b = 2, c = 3;
b = b /= a = c++;
return a + b;
}());
}
expect_stdout: true
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
inline: true,
unsafe: false,
unused: true,
}
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(a ? b : c ? d : void 0);
}
expect_stdout: true
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
t ? console.log(true) : console.log(false), void console.log("something");
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(void console.log("test"))()()();'
}
negate_iife_issue_1073: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
unused: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new function() {
this.x = 7,
console.log(this);
}();
}
expect_stdout: true
}
issue_1288_side_effects: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || (x = {});
y;
}
}
inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
for (b in (function() {
return x(a, b, c);
})()) {
var c = 3, d = 4;
x(a, b, c, d);
}
x(a, b, c, d);
}
}
expect: {
function f() {
var a = 1, b = 2;
for (b in x(1, b, c)) {
var c = 3, d = 4;
x(1, b, c, d);
}
x(1, b, c, d);
}
}
}
issue_1595_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
g(3);
}
}
issue_1758: {
options = {
inline: true,
sequences: true,
side_effects: true,
}
input: {
console.log(function(c) {
var undefined = 42;
return function() {
c--;
c--, c.toString();
return;
}();
}());
}
expect: {
console.log(function(c) {
var undefined = 42;
return c--, c--, void c.toString();
}());
}
expect_stdout: "undefined"
}
wrap_iife: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
}
wrap_iife_in_expression: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=bar();'
}
wrap_iife_in_return_call: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(void console.log("test"))();'
}
pure_annotation_1: {
options = {
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function() {
console.log("hello");
}());
}
expect_exact: ""
}
pure_annotation_2: {
options = {
collapse_vars: true,
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function(n) {
console.log("hello", n);
}(42));
}
expect_exact: ""
}
drop_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
}
keep_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: true,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
}

View File

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

View File

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

View File

@@ -436,3 +436,17 @@ do_switch: {
} while (false);
}
}
in_parenthesis_1: {
input: {
for (("foo" in {});0;);
}
expect_exact: 'for(("foo"in{});0;);'
}
in_parenthesis_2: {
input: {
for ((function(){ "foo" in {}; });0;);
}
expect_exact: 'for(function(){"foo"in{}};0;);'
}

View File

@@ -22,7 +22,8 @@ negate_iife_1_off: {
negate_iife_2: {
options = {
negate_iife: true
inline: true,
negate_iife: true,
};
input: {
(function(){ return {} })().x = 10;
@@ -32,6 +33,7 @@ negate_iife_2: {
negate_iife_2_side_effects: {
options = {
inline: true,
negate_iife: true,
side_effects: true,
}
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
}
input: {
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: false,
}
input: {

View File

@@ -82,3 +82,19 @@ new_with_unary_prefix: {
}
expect_exact: 'var bar=(+new Date).toString(32);';
}
dot_parenthesis_1: {
input: {
console.log(new (Math.random().constructor) instanceof Number);
}
expect_exact: "console.log(new(Math.random().constructor)instanceof Number);"
expect_stdout: "true"
}
dot_parenthesis_2: {
input: {
console.log(typeof new function(){Math.random()}.constructor);
}
expect_exact: "console.log(typeof new function(){Math.random()}.constructor);"
expect_stdout: "function"
}

View File

@@ -1,4 +1,4 @@
eval_let: {
eval_let_6: {
input: {
eval("let a;");
console.log();
@@ -10,3 +10,29 @@ eval_let: {
expect_stdout: ""
node_version: ">=6"
}
eval_let_4: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode")
node_version: "4"
}
eval_let_0: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Unexpected identifier")
node_version: "<=0.12"
}

View File

@@ -1,7 +1,8 @@
keep_properties: {
options = {
properties: false
};
evaluate: true,
properties: false,
}
input: {
a["foo"] = "bar";
}
@@ -12,9 +13,12 @@ keep_properties: {
dot_properties: {
options = {
evaluate: true,
properties: true,
}
beautify = {
ie8: true,
};
}
input: {
a["foo"] = "bar";
a["if"] = "if";
@@ -35,9 +39,12 @@ dot_properties: {
dot_properties_es5: {
options = {
evaluate: true,
properties: true,
}
beautify = {
ie8: false,
};
}
input: {
a["foo"] = "bar";
a["if"] = "if";
@@ -57,8 +64,8 @@ dot_properties_es5: {
sub_properties: {
options = {
evaluate: true,
properties: true
};
properties: true,
}
input: {
a[0] = 0;
a["0"] = 1;
@@ -77,18 +84,18 @@ sub_properties: {
a[3.14] = 3;
a.if = 4;
a["foo bar"] = 5;
a[NaN] = 6;
a[null] = 7;
a.NaN = 6;
a.null = 7;
a[void 0] = 8;
}
}
evaluate_array_length: {
options = {
evaluate: true,
properties: true,
unsafe: true,
evaluate: true
};
}
input: {
a = [1, 2, 3].length;
a = [1, 2, 3].join()["len" + "gth"];
@@ -105,10 +112,10 @@ evaluate_array_length: {
evaluate_string_length: {
options = {
evaluate: true,
properties: true,
unsafe: true,
evaluate: true
};
}
input: {
a = "foo".length;
a = ("foo" + "bar")["len" + "gth"];
@@ -124,9 +131,11 @@ evaluate_string_length: {
}
mangle_properties: {
mangle_props = {
keep_quoted: false
};
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
a["foo"] = "bar";
a.color = "red";
@@ -137,18 +146,21 @@ mangle_properties: {
expect: {
a["a"] = "bar";
a.b = "red";
x = {c: 10};
a.d(x.c, a.a);
a['d']({b: "blue", a: "baz"});
x = {o: 10};
a.r(x.o, a.a);
a['r']({b: "blue", a: "baz"});
}
}
mangle_unquoted_properties: {
options = {
properties: false
evaluate: true,
properties: false,
}
mangle_props = {
keep_quoted: true
mangle = {
properties: {
keep_quoted: true,
},
}
beautify = {
beautify: false,
@@ -177,24 +189,26 @@ mangle_unquoted_properties: {
function f1() {
a["foo"] = "bar";
a.color = "red";
a.b = 2;
x = {"bar": 10, c: 7};
a.c = 9;
a.r = 2;
x = {"bar": 10, b: 7};
a.b = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, c: 7};
a.c = 9;
a.b = 3;
x = {bar: 10, b: 7};
a.b = 9;
a.r = 3;
}
}
}
mangle_debug: {
mangle_props = {
debug: ""
};
mangle = {
properties: {
debug: "",
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -206,9 +220,11 @@ mangle_debug: {
}
mangle_debug_true: {
mangle_props = {
debug: true
};
mangle = {
properties: {
debug: true,
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -220,9 +236,11 @@ mangle_debug_true: {
}
mangle_debug_suffix: {
mangle_props = {
debug: "XYZ"
};
mangle = {
properties: {
debug: "XYZ",
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -235,12 +253,15 @@ mangle_debug_suffix: {
mangle_debug_suffix_keep_quoted: {
options = {
properties: false
evaluate: true,
properties: false,
}
mangle_props = {
keep_quoted: true,
debug: "XYZ",
reserved: []
mangle = {
properties: {
debug: "XYZ",
keep_quoted: true,
reserved: [],
},
}
beautify = {
beautify: false,
@@ -555,3 +576,481 @@ native_prototype: {
"".indexOf.call(e, "bar");
}
}
accessor_boolean: {
input: {
var a = 1;
var b = {
get true() {
return a;
},
set false(c) {
a = c;
}
};
console.log(b.true, b.false = 2, b.true);
}
expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);'
expect_stdout: "1 2 2"
}
accessor_get_set: {
input: {
var a = 1;
var b = {
get set() {
return a;
},
set get(c) {
a = c;
}
};
console.log(b.set, b.get = 2, b.set);
}
expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);'
expect_stdout: "1 2 2"
}
accessor_null_undefined: {
input: {
var a = 1;
var b = {
get null() {
return a;
},
set undefined(c) {
a = c;
}
};
console.log(b.null, b.undefined = 2, b.null);
}
expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);'
expect_stdout: "1 2 2"
}
accessor_number: {
input: {
var a = 1;
var b = {
get 42() {
return a;
},
set 42(c) {
a = c;
}
};
console.log(b[42], b[42] = 2, b[42]);
}
expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);'
expect_stdout: "1 2 2"
}
accessor_string: {
input: {
var a = 1;
var b = {
get "a-b"() {
return a;
},
set "a-b"(c) {
a = c;
}
};
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
}
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
expect_stdout: "1 2 2"
}
accessor_this: {
input: {
var a = 1;
var b = {
get this() {
return a;
},
set this(c) {
a = c;
}
};
console.log(b.this, b.this = 2, b.this);
}
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
expect_stdout: "1 2 2"
}
issue_2208_1: {
options = {
inline: true,
properties: true,
side_effects: true,
}
input: {
console.log({
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2208_2: {
options = {
inline: true,
properties: true,
side_effects: true,
}
input: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect_stdout: "42"
}
issue_2208_3: {
options = {
inline: true,
properties: true,
side_effects: true,
}
input: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log(function() {
return this.a;
}());
}
expect_stdout: "42"
}
issue_2208_4: {
options = {
inline: true,
properties: true,
side_effects: true,
}
input: {
function foo() {}
console.log({
a: foo(),
p: function() {
return 42;
}
}.p());
}
expect: {
function foo() {}
console.log((foo(), function() {
return 42;
})());
}
expect_stdout: "42"
}
issue_2208_5: {
options = {
inline: true,
properties: true,
side_effects: true,
}
input: {
console.log({
p: "FAIL",
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2256: {
options = {
side_effects: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
({ "keep": 1 });
g.keep = g.change;
}
expect: {
g.keep = g.g;
}
}
lhs_prop_1: {
options = {
evaluate: true,
properties: true,
}
input: {
console.log(++{
a: 1
}.a);
}
expect: {
console.log(++{
a: 1
}.a);
}
expect_stdout: "2"
}
lhs_prop_2: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
[1][0] = 42;
(function(a) {
a.b = "g";
})("abc");
(function(a) {
a[2] = "g";
})("def");
(function(a) {
a[""] = "g";
})("ghi");
}
expect: {
[1][0] = 42;
"abc".b = "g";
"def"[2] = "g";
"ghi"[""] = "g";
}
}
literal_duplicate_key_side_effects: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log({
a: "FAIL",
a: console.log ? "PASS" : "FAIL"
}.a);
}
expect: {
console.log(console.log ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
prop_side_effects_1: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
var obj = {
bar: function() {
return 2;
}
};
console.log(obj.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
"": function() {
return C + C;
}
};
console.log(obj[""]());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
accessor_1: {
options = {
properties: true,
}
input: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
accessor_2: {
options = {
properties: true,
}
input: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect_stdout: true
}
array_hole: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log(
[ 1, 2, , 3][1],
[ 1, 2, , 3][2],
[ 1, 2, , 3][3]
);
}
expect: {
console.log(2, void 0, 3);
}
expect_stdout: "2 undefined 3"
}
new_this: {
options = {
properties: true,
side_effects: true,
}
input: {
new {
f: function(a) {
this.a = a;
}
}.f(42);
}
expect: {
new function(a) {
this.a = a;
}(42);
}
}
issue_2513: {
options = {
evaluate: true,
properties: true,
}
input: {
!function(Infinity, NaN, undefined) {
console.log("a"[1/0], "b"["Infinity"]);
console.log("c"[0/0], "d"["NaN"]);
console.log("e"[void 0], "f"["undefined"]);
}(0, 0, 0);
}
expect: {
!function(Infinity, NaN, undefined) {
console.log("a"[1/0], "b"[1/0]);
console.log("c".NaN, "d".NaN);
console.log("e"[void 0], "f"[void 0]);
}(0, 0, 0);
}
expect_stdout: [
"undefined undefined",
"undefined undefined",
"undefined undefined",
]
}

View File

@@ -1,6 +1,7 @@
strict: {
options = {
pure_getters: "strict",
reduce_funcs: false,
reduce_vars: false,
side_effects: true,
toplevel: true,
@@ -30,6 +31,7 @@ strict: {
strict_reduce_vars: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -58,6 +60,7 @@ strict_reduce_vars: {
unsafe: {
options = {
pure_getters: true,
reduce_funcs: false,
reduce_vars: false,
side_effects: true,
toplevel: true,
@@ -84,6 +87,7 @@ unsafe: {
unsafe_reduce_vars: {
options = {
pure_getters: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -178,3 +182,432 @@ impure_getter_2: {
}
expect: {}
}
issue_2110_1: {
options = {
cascade: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
return f.g = function() {
return this;
}, f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
issue_2110_2: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
f.g = function() {
return this;
};
return f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
set_immutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_3: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: true
}
set_immutable_4: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
if (a.foo += "") console.log("PASS");
else console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
set_mutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
(a.foo += "") ? console.log("PASS") : console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
issue_2313_1: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: true,
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_3: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: "strict",
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_4: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_5: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x().y;
}
}
issue_2313_6: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x();
}
}

File diff suppressed because it is too large Load Diff

536
test/compress/rename.js Normal file
View File

@@ -0,0 +1,536 @@
mangle_catch: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(o){a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(args){a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_var: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(o){var a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_var_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(args){var a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_var_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_var_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_1: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_2: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}
issue_2120_1: {
rename = true
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
rename = true
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
function_iife_catch: {
rename = true
mangle = {
ie8: false,
}
input: {
function f(n) {
!function() {
try {
throw 0;
} catch (n) {
var a = 1;
console.log(n, a);
}
}();
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_stdout: "0 1"
}
function_iife_catch_ie8: {
rename = true
mangle = {
ie8: true,
}
input: {
function f(n) {
!function() {
try {
throw 0;
} catch (n) {
var a = 1;
console.log(n, a);
}
}();
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_stdout: "0 1"
}
function_catch_catch: {
rename = true
mangle = {
ie8: false,
}
input: {
var o = 0;
function f() {
try {
throw 1;
} catch (c) {
try {
throw 2;
} catch (o) {
var o = 3;
console.log(o);
}
}
console.log(o);
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}
function_catch_catch_ie8: {
rename = true
mangle = {
ie8: true,
}
input: {
var o = 0;
function f() {
try {
throw 1;
} catch (c) {
try {
throw 2;
} catch (o) {
var o = 3;
console.log(o);
}
}
console.log(o);
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}

14
test/compress/sandbox.js Normal file
View File

@@ -0,0 +1,14 @@
console_log: {
input: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect_stdout: [
"%% %s",
"% %s",
]
}

View File

@@ -187,6 +187,7 @@ dont_screw_try_catch_undefined: {
reduce_vars: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
ie8: true,
unused: true,
@@ -255,3 +256,139 @@ issue_1586_2: {
}
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
}
issue_2120_1: {
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2254_1: {
mangle = {
ie8: false,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(e) {
try {
throw "FAIL";
} catch (t) {
return e;
}
}
}
expect_stdout: "PASS"
}
issue_2254_2: {
mangle = {
ie8: true,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(t) {
try {
throw "FAIL";
} catch (e) {
return t;
}
}
}
expect_stdout: "PASS"
}

View File

@@ -176,6 +176,11 @@ for_sequences: {
// 4
x = (foo in bar);
for (y = 5; false;);
// 5
x = function() {
foo in bar;
};
for (y = 5; false;);
}
expect: {
// 1
@@ -188,6 +193,10 @@ for_sequences: {
// 4
x = (foo in bar);
for (y = 5; false;);
// 5
for (x = function() {
foo in bar;
}, y = 5; false;);
}
}
@@ -710,3 +719,64 @@ issue_27: {
})(jQuery);
}
}
issue_2062: {
options = {
booleans: true,
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
var a = 1;
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
console.log(a);
}
expect: {
var a = 1;
a || (a++, a--), a++, --a && a.var;
console.log(a);
}
expect_stdout: "1"
}
issue_2313: {
options = {
cascade: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
foo.d();
}
expect: {
var a = 0, b = 0;
var foo = {
get c() {
return a++, 42;
},
set c(c) {
b++;
},
d: function() {
if (this.c++, this.c) console.log(a, b);
}
}
foo.d();
}
expect_stdout: "2 1"
}

View File

@@ -714,6 +714,7 @@ issue_1705_2: {
options = {
dead_code: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,

View File

@@ -15,3 +15,43 @@ unicode_parse_variables: {
var l = 3;
}
}
issue_2242_1: {
beautify = {
ascii_only: false,
}
input: {
console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00");
}
expect_exact: 'console.log("\\ud83d","\\ude00","\ud83d\ude00","\\ud83d@\\ude00");'
}
issue_2242_2: {
beautify = {
ascii_only: true,
}
input: {
console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00");
}
expect_exact: 'console.log("\\ud83d","\\ude00","\\ud83d\\ude00","\\ud83d@\\ude00");'
}
issue_2242_3: {
options = {
evaluate: false,
}
input: {
console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00");
}
expect_exact: 'console.log("\\ud83d"+"\\ude00","\\ud83d"+"@"+"\\ude00");'
}
issue_2242_4: {
options = {
evaluate: true,
}
input: {
console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00");
}
expect_exact: 'console.log("\ud83d\ude00","\\ud83d@\\ude00");'
}

View File

@@ -8,6 +8,7 @@ exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
exports["parse"] = parse;
exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;

31
test/fetch.js Normal file
View File

@@ -0,0 +1,31 @@
var fs = require("fs");
var path = require("path");
try {
fs.mkdirSync("./tmp");
} catch (e) {
if (e.code != "EEXIST") throw e;
}
function local(url) {
return path.join("./tmp", encodeURIComponent(url));
}
function read(url) {
return fs.createReadStream(local(url));
}
module.exports = function(url, callback) {
var result = read(url);
result.on("error", function(e) {
if (e.code != "ENOENT") return callback(e);
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
if (res.statusCode !== 200) return callback(res);
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
callback(null, read(url));
}));
});
}).on("open", function() {
callback(null, result);
});
};

View File

@@ -0,0 +1,4 @@
var a, b = [1, 2];
for (1, 2, a in b) {
console.log(a, b[a]);
}

View File

@@ -0,0 +1,4 @@
var c = [1, 2];
for (var a, b in c) {
console.log(a, c[a]);
}

View File

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

View File

@@ -0,0 +1 @@
{"version": 3,"sources": ["index.js"],"mappings": ";"}

View File

@@ -0,0 +1,10 @@
function foo() {
return function() {
console.log("PASS");
};
}
(function() {
var f = foo();
f();
})();

View File

@@ -3,7 +3,7 @@
"use strict";
var site = "http://browserbench.org/JetStream/";
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){
@@ -11,45 +11,69 @@ if (typeof phantom == "undefined") {
stream._handle.setBlocking(true);
});
var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {
args.splice(debug, 1);
debug = true;
} else {
debug = false;
}
if (!args.length) {
args.push("-mc");
args.push("-mcb", "beautify=false,webkit");
}
args.push("--timings");
var child_process = require("child_process");
try {
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var fetch = require("./fetch");
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.");
var url = site + request.url;
fetch(url, function(err, res) {
if (err) throw err;
response.writeHead(200, {
"Content-Type": {
css: "text/css",
js: "application/javascript",
png: "image/png"
}[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
});
if (/\.js$/.test(url)) {
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), 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);
res.pipe(uglifyjs.stdin);
} else {
res.pipe(response);
}
});
}).listen();
server.on("listening", function() {
var port = server.address().port;
if (debug) {
console.log("http://localhost:" + port + "/");
} else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
if (error) throw error;
var program = require("phantomjs-prebuilt").exec(process.argv[1], 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.");
process.exit(0);
});
});
}
});
server.timeout = 0;
} else {
@@ -63,10 +87,6 @@ if (typeof phantom == "undefined") {
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);
@@ -77,8 +97,8 @@ if (typeof phantom == "undefined") {
phantom.exit();
}
};
page.open(site, function(status) {
if (status != "success") phantomjs.exit(1);
page.open(url, function(status) {
if (status != "success") phantom.exit(1);
page.evaluate(function() {
JetStream.switchToQuick();
JetStream.start();

View File

@@ -9,7 +9,7 @@ function read(path) {
describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) {
this.timeout(15000);
this.timeout(30000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
@@ -63,7 +63,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n");
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==\n");
done();
});
});
@@ -77,6 +77,23 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("should not consider source map file content as source map file name (issue #2082)", function (done) {
var command = [
uglifyjscmd,
"test/input/issue-2082/sample.js",
"--source-map", "content=test/input/issue-2082/sample.js.map",
"--source-map", "url=inline",
].join(" ");
exec(command, function (err, stdout, stderr) {
if (err) throw err;
var stderrLines = stderr.split('\n');
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map');
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
done();
});
});
it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
@@ -175,7 +192,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -501,6 +518,36 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_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/for-in_1.js:2,5",
"for (1, 2, a in b) {",
" ^",
"ERROR: Invalid left-hand side in for..in loop"
].join("\n"));
done();
});
});
it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_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/for-in_2.js:2,5",
"for (var a, b in c) {",
" ^",
"ERROR: Only one variable declaration allowed in for..in loop"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
uglifyjscmd,
@@ -526,6 +573,25 @@ describe("bin/uglifyjs", function () {
return JSON.stringify(map).replace(/"/g, '\\"');
}
});
it("Should include function calls in source map", function(done) {
var command = [
uglifyjscmd,
"test/input/issue-2310/input.js",
"-c",
"--source-map", "url=inline",
].join(" ");
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, [
'function foo(){return function(){console.log("PASS")}}foo()();',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMjMxMC9pbnB1dC5qcyJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwibG9nIiwiZiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsTUFDTCxPQUFPLFdBQ0hDLFFBQVFDLElBQUksU0FLUkYsS0FDUkcifQ==",
""
].join("\n"));
done();
});
});
it("Should dump AST as JSON", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
exec(command, function (err, stdout) {
@@ -542,7 +608,45 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should work with --mangle reserved=[]", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=[callback]";
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n');
done();
});
});
it("Should work with --mangle reserved=false", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=false";
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n');
done();
});
});
it("Should fail with --mangle-props reserved=[in]", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js --mangle-props reserved=[in]";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `reserved=\[in]` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should fail with --define a-b", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js --define a-b";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr, "Error parsing arguments for 'define': a-b\n");
done();
});
});

View File

@@ -26,12 +26,12 @@ describe("bin/uglifyjs with input file globs", function() {
});
});
it("bin/uglifyjs with multiple input file globs.", function(done) {
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel';
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=2';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, '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);\n');
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",22);\n');
done();
});
});

View File

@@ -2,29 +2,56 @@ var Uglify = require('../../');
var assert = require("assert");
describe("let", function() {
it("Should not produce `let` as a variable name in mangle", function(done) {
this.timeout(10000);
this.timeout(30000);
it("Should not produce reserved keywords as variable name in mangle", function() {
// Produce a lot of variables in a function and run it through mangle.
var s = '"use strict"; function foo() {';
for (var i = 0; i < 21000; ++i) {
var s = '"dddddeeeeelllllooooottttt"; function foo() {';
for (var i = 0; i < 18000; i++) {
s += "var v" + i + "=0;";
}
s += '}';
var result = Uglify.minify(s, {compress: false});
var result = Uglify.minify(s, {
compress: false
}).code;
// Verify that select keywords and reserved keywords not produced
assert.strictEqual(result.code.indexOf("var let="), -1);
assert.strictEqual(result.code.indexOf("var do="), -1);
assert.strictEqual(result.code.indexOf("var var="), -1);
[
"do",
"let",
"var",
].forEach(function(name) {
assert.strictEqual(result.indexOf("var " + name + "="), -1);
});
// Verify that the variable names that appeared immediately before
// and after the erroneously generated `let` variable name still exist
// and after the erroneously generated variable name still exist
// to show the test generated enough symbols.
assert(result.code.indexOf("var ket=") >= 0);
assert(result.code.indexOf("var met=") >= 0);
done();
[
"to", "eo",
"eet", "fet",
"rar", "oar",
].forEach(function(name) {
assert.notStrictEqual(result.indexOf("var " + name + "="), -1);
});
});
it("Should quote mangled properties that are reserved keywords", function() {
var s = '"rrrrrnnnnniiiiiaaaaa";';
for (var i = 0; i < 18000; i++) {
s += "v.b" + i + ";";
}
var result = Uglify.minify(s, {
compress: false,
ie8: true,
mangle: {
properties: true,
}
}).code;
[
"in",
"var",
].forEach(function(name) {
assert.notStrictEqual(result.indexOf(name), -1);
assert.notStrictEqual(result.indexOf('v["' + name + '"]'), -1);
});
});
});

View File

@@ -1,6 +1,7 @@
var Uglify = require('../../');
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
function read(path) {
return readFileSync(path, "utf8");
@@ -13,6 +14,71 @@ describe("minify", function() {
assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
});
it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)";
var result = Uglify.minify(files);
assert.strictEqual(result.code, "alert(2);");
});
it("Should work with mangle.cache", function() {
var cache = {};
var original = "";
var compressed = "";
[
"bar.es5",
"baz.es5",
"foo.es5",
"qux.js",
].forEach(function(file) {
var code = read("test/input/issue-1242/" + file);
var result = Uglify.minify(code, {
mangle: {
cache: cache,
toplevel: true
}
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 20), '{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
assert.strictEqual(run_code(compressed), run_code(original));
});
it("Should work with nameCache", function() {
var cache = {};
var original = "";
var compressed = "";
[
"bar.es5",
"baz.es5",
"foo.es5",
"qux.js",
].forEach(function(file) {
var code = read("test/input/issue-1242/" + file);
var result = Uglify.minify(code, {
mangle: {
toplevel: true
},
nameCache: cache
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 28), '{"vars":{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
assert.strictEqual(run_code(compressed), run_code(original));
});
it("should not parse invalid use of reserved words", function() {
assert.strictEqual(Uglify.minify("function enum(){}").error, undefined);
assert.strictEqual(Uglify.minify("function static(){}").error, undefined);
assert.strictEqual(Uglify.minify("function this(){}").error.message, "Unexpected token: name (this)");
});
describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -64,6 +130,17 @@ describe("minify", function() {
assert.strictEqual(result.code,
'a["foo"]="bar",a.a="red",x={"bar":10};');
});
it("Should not mangle quoted property within dead code", function() {
var result = Uglify.minify('({ "keep": 1 }); g.keep = g.change;', {
mangle: {
properties: {
keep_quoted: true
}
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "g.keep=g.g;");
});
});
describe("inSourceMap", function() {
@@ -106,7 +183,7 @@ describe("minify", function() {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
@@ -205,7 +282,52 @@ describe("minify", function() {
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token: keyword (debugger)");
});
it("should skip inherited properties", function() {
var foo = Object.create({ skip: this });
foo.bar = 42;
var result = Uglify.minify("alert(FOO);", {
compress: {
global_defs: {
FOO: foo
}
}
});
assert.strictEqual(result.code, "alert({bar:42});");
});
});
describe("collapse_vars", function() {
it("Should not produce invalid AST", function() {
var code = [
"function f(a) {",
" a = x();",
" return a;",
"}",
"f();",
].join("\n");
var ast = Uglify.minify(code, {
compress: false,
mangle: false,
output: {
ast: true
},
}).ast;
assert.strictEqual(ast.TYPE, "Toplevel");
assert.strictEqual(ast.body.length, 2);
assert.strictEqual(ast.body[0].TYPE, "Defun");
assert.strictEqual(ast.body[0].body.length, 2);
assert.strictEqual(ast.body[0].body[0].TYPE, "SimpleStatement");
var stat = ast.body[0].body[0];
Uglify.minify(ast, {
compress: {
sequences: false
},
mangle: false
});
assert.ok(stat.body);
assert.strictEqual(stat.print_to_string(), "a=x()");
});
});
});

View File

@@ -1,23 +1,20 @@
var assert = require("assert");
var semver = require("semver");
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"
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
[
"-b",
"-b bracketize",
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
});
});
if (semver.satisfies(process.version, "0.12")) return;
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",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
@@ -48,6 +43,7 @@ describe("test/jetstream.js", function() {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done);
});
});

View File

@@ -4,7 +4,7 @@ var uglify = require("../node");
describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(30000);
this.timeout(60000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +

View File

@@ -61,9 +61,9 @@ describe("String literals", function() {
var tests = [
['"\\76";', ';">";'],
['"\\0"', '"\\0";'],
['"\\08"', '"\\08";'],
['"\\008"', '"\\08";'],
['"\\0008"', '"\\08";'],
['"\\08"', '"\\x008";'],
['"\\008"', '"\\x008";'],
['"\\0008"', '"\\x008";'],
['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'],
['"use\\\n strict";\n"\\07";', ';"use strict";"\07";']
];
@@ -75,7 +75,44 @@ describe("String literals", function() {
});
it("Should not throw error when digit is 8 or 9", function() {
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\08";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\09";');
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\x008";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\x009";');
});
it("Should not unescape unpaired surrogates", function() {
var code = [];
for (var i = 0; i <= 0xF; i++) {
code.push("\\u000" + i.toString(16));
}
for (;i <= 0xFF; i++) {
code.push("\\u00" + i.toString(16));
}
for (;i <= 0xFFF; i++) {
code.push("\\u0" + i.toString(16));
}
for (; i <= 0xFFFF; i++) {
code.push("\\u" + i.toString(16));
}
code = '"' + code.join() + '"';
var normal = UglifyJS.minify(code, {
compress: false,
mangle: false,
output: {
ascii_only: false
}
});
if (normal.error) throw normal.error;
assert.ok(code.length > normal.code.length);
assert.strictEqual(eval(code), eval(normal.code));
var ascii = UglifyJS.minify(code, {
compress: false,
mangle: false,
output: {
ascii_only: false
}
});
if (ascii.error) throw ascii.error;
assert.ok(code.length > ascii.code.length);
assert.strictEqual(eval(code), eval(ascii.code));
});
});

View File

@@ -86,7 +86,6 @@ function run_compress_tests() {
log_start_file(file);
function test_case(test) {
log_test(test.name);
U.base54.reset();
var output_options = test.beautify || {};
var expect;
if (test.expect) {
@@ -101,9 +100,6 @@ function run_compress_tests() {
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, {
warnings: false
});
@@ -115,12 +111,26 @@ function run_compress_tests() {
};
if (!options.warnings) options.warnings = true;
}
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props);
}
if (test.rename) {
input.figure_out_scope(test.mangle);
input.expand_names(test.mangle);
}
var cmp = new U.Compressor(options, true);
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle) {
U.base54.reset();
output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle);
if (test.mangle.properties) {
output = U.mangle_properties(output, test.mangle.properties);
}
}
output = make_code(output, output_options);
if (expect != output) {
@@ -294,8 +304,22 @@ function parse_test(file) {
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
test[label.name] = stat.body.value;
var body = stat.body;
if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
} else {
test[label.name] = read_string(stat) + "\n";
}

View File

@@ -19,23 +19,25 @@ function safe_log(arg, level) {
var FUNC_TOSTRING = [
"Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 0;",
" var id = 100000;",
" return function() {",
' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";',
" var i = this.name;",
' if (typeof i != "number") {',
" i = ++id;",
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return i;",
" }",
" });",
] : [], [
" }",
' return "[Function: " + i + "]";',
" }",
"}();",
].join("\n");
]).join("\n");
exports.run_code = function(code) {
var stdout = "";
var original_write = process.stdout.write;
@@ -50,7 +52,10 @@ exports.run_code = function(code) {
"}();",
].join("\n"), {
console: {
log: function() {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));

View File

@@ -102,23 +102,23 @@ for (var i = 2; i < process.argv.length; ++i) {
case '--help':
case '-h':
case '-?':
console.log('** UglifyJS fuzzer help **');
console.log('Valid options (optional):');
console.log('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case');
console.log('-V: print every 100th generated test case');
console.log('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
console.log('** UglifyJS fuzzer exiting **');
println('** UglifyJS fuzzer help **');
println('Valid options (optional):');
println('<number>: generate this many cases (if used must be first arg)');
println('-v: print every generated test case');
println('-V: print every 100th generated test case');
println('-t <int>: generate this many toplevels per run (more take longer)');
println('-r <int>: maximum recursion depth for generator (higher takes longer)');
println('-s1 <statement name>: force the first level statement to be this one (see list below)');
println('-s2 <statement name>: force the second level statement to be this one (see list below)');
println('--no-catch-redef: do not redefine catch variables');
println('--no-directive: do not generate directives');
println('--use-strict: generate "use strict"');
println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
println('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
println('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
println('** UglifyJS fuzzer exiting **');
return 0;
default:
// first arg may be a number.
@@ -162,6 +162,7 @@ var VALUES = [
'"object"',
'"number"',
'"function"',
'this',
];
var BINARY_OPS_NO_COMMA = [
@@ -349,10 +350,10 @@ function createParams() {
return params.join(', ');
}
function createArgs() {
function createArgs(recurmax, stmtDepth, canThrow) {
var args = [];
for (var n = rng(4); --n >= 0;) {
args.push(createValue());
args.push(rng(2) ? createValue() : createExpression(recurmax - 1, COMMA_OK, stmtDepth, canThrow));
}
return args.join(', ');
}
@@ -390,9 +391,10 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
VAR_NAMES.length = namesLenBefore;
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ');';
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s;
// avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name;
s += '(' + createArgs(recurmax, stmtDepth, canThrow) + ');';
return s;
}
@@ -626,6 +628,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
return createValue();
case p++:
case p++:
return getVarName();
case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
@@ -941,7 +946,17 @@ if (require.main !== module) {
return;
}
function try_beautify(code, result) {
function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg);
process.stdout.write("\n");
}
function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg);
process.stderr.write("\n");
}
function try_beautify(code, result, printfn) {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
@@ -951,15 +966,15 @@ function try_beautify(code, result) {
},
});
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)");
console.log(beautified.code);
printfn("// (beautified)");
printfn(beautified.code);
return;
}
console.log("//");
console.log(code);
printfn("//");
printfn(code);
}
var default_options = UglifyJS.default_options();
@@ -977,8 +992,8 @@ function log_suspects(minify_options, component) {
m[component] = o;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
console.log("Error testing options." + component + "." + name);
console.log(result.error);
errorln("Error testing options." + component + "." + name);
errorln(result.error.stack);
} else {
var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r);
@@ -986,49 +1001,49 @@ function log_suspects(minify_options, component) {
}
});
if (suspects.length > 0) {
console.log("Suspicious", component, "options:");
errorln("Suspicious " + component + " options:");
suspects.forEach(function(name) {
console.log(" " + name);
errorln(" " + name);
});
console.log();
errorln();
}
}
function log(options) {
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
console.log("//=============================================================");
if (!ok) console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original_code, original_result);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
try_beautify(original_code, original_result, errorln);
errorln();
errorln();
errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") {
console.log("// uglified code");
try_beautify(uglify_code, uglify_result);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log("uglified result:");
console.log(uglify_result);
errorln("// uglified code");
try_beautify(uglify_code, uglify_result, errorln);
errorln();
errorln();
errorln("original result:");
errorln(typeof original_result == "string" ? original_result : original_result.stack);
errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
} else {
console.log("// !!! uglify failed !!!");
console.log(uglify_code.stack);
errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack);
if (typeof original_result != "string") {
console.log();
console.log();
console.log("original stacktrace:");
console.log(original_result.stack);
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result.stack);
}
}
console.log("minify(options):");
errorln("minify(options):");
options = JSON.parse(options);
console.log(options);
console.log();
errorln(JSON.stringify(options, null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options));
console.log("!!!!!! Failed... round", round);
errorln("!!!!!! Failed... round " + round);
}
}
@@ -1058,19 +1073,19 @@ for (var round = 1; round <= num_iterations; round++) {
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") {
console.log("//=============================================================");
console.log("// original code");
try_beautify(original_code, original_result);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log();
println("//=============================================================");
println("// original code");
try_beautify(original_code, original_result, println);
println();
println();
println("original result:");
println(original_result.stack);
println();
}
if (!ok && isFinite(num_iterations)) {
console.log();
println();
process.exit(1);
}
});
}
console.log();
println();

View File

@@ -16,11 +16,9 @@
{},
{
"compress": {
"toplevel": true
"hoist_props": true
},
"mangle": {
"toplevel": true
}
"toplevel": true
},
{
"compress": {