Compare commits

...

302 Commits

Author SHA1 Message Date
Alex Lam S.L
ffcce28ce1 v3.11.2 2020-10-11 21:19:25 +08:00
Alex Lam S.L
9c0feb69e5 fix corner case in reduce_vars (#4189)
fixes #4188
2020-10-07 22:01:39 +08:00
Alex Lam S.L
bc6e105174 fix corner case in ie8 (#4187)
fixes #4186
2020-10-06 09:20:41 +08:00
Alex Lam S.L
b91a2459c0 fix corner case in unused (#4185)
fixes #4184
2020-10-05 18:59:03 +08:00
Alex Lam S.L
b7a57fc69d fix corner case in loops (#4183)
fixes #4182
2020-10-05 17:28:46 +08:00
Alex Lam S.L
2dbe40b01b enhance conditionals (#4181) 2020-10-05 15:55:37 +08:00
Alex Lam S.L
813ac3ba96 enhance loops (#4180) 2020-10-05 08:26:59 +08:00
Alex Lam S.L
220dc95c0d clean up scope-related variables (#4179) 2020-10-05 06:56:52 +08:00
Alex Lam S.L
8f0521d51d retrofit try-catch-finally as block-scoped (#4178)
- support optional catch binding
2020-10-05 05:30:14 +08:00
Alex Lam S.L
f9946767c9 retrofit AST_BlockStatement as block-scoped (#4177) 2020-10-05 01:58:50 +08:00
Alex Lam S.L
58ac5b9bd5 extend support for numeral literals (#4176) 2020-10-05 00:05:03 +08:00
Alex Lam S.L
66140b459e enhance side_effects (#4175) 2020-10-04 23:43:49 +08:00
Alex Lam S.L
1786c69070 v3.11.1 2020-10-04 22:12:07 +08:00
Alex Lam S.L
95ef4d5377 fix corner case in mangle (#4174) 2020-10-04 08:24:41 +08:00
Alex Lam S.L
04017215cc support JSON dump beyond AST_Toplevel (#4173) 2020-10-03 22:53:06 +08:00
Alex Lam S.L
142bd1bd1a workaround quirks on latter specs (#4172)
closes #4171
2020-10-03 18:27:17 +08:00
Alex Lam S.L
8cb509d50e fix corner case in merge_vars (#4170)
fixes #4168
2020-10-03 07:03:39 +08:00
Alex Lam S.L
baf4903aa7 fix corner cases of catch variable inlining (#4169) 2020-10-03 07:02:28 +08:00
Alex Lam S.L
35465d590e report immediate ufuzz failure from Pull Request (#4166) 2020-10-02 23:43:38 +08:00
Alex Lam S.L
ccd91b9952 retrofit catch as block-scoped (#4165) 2020-10-02 23:29:58 +08:00
Alex Lam S.L
47a5e6e17a enhance if_return (#4164) 2020-10-02 16:10:25 +08:00
Alex Lam S.L
090ee895e1 enhance inline (#4163) 2020-09-30 21:03:28 +08:00
Alex Lam S.L
1cd1a1e5ee improve resilience against GitHub API (#4161) 2020-09-30 01:13:29 +08:00
Alex Lam S.L
1d835ac17d fix corner case in inline (#4160)
fixes #4159
2020-09-29 07:01:38 +08:00
Alex Lam S.L
9e07ac4102 fix corner case in merge_vars (#4158)
fixes #4157
2020-09-28 14:09:55 +08:00
Alex Lam S.L
92d1391e5e v3.11.0 2020-09-27 20:36:27 +08:00
Alex Lam S.L
b4ff6d0f2d fix corner cases in functions & merge_vars (#4156)
fixes #4155
2020-09-26 15:31:33 +08:00
Alex Lam S.L
9882a9f4af fix corner case in ufuzz scheduling (#4154) 2020-09-26 11:23:56 +08:00
Alex Lam S.L
40f36b9e01 improve ufuzz duty cycle heuristic (#4153) 2020-09-26 07:56:00 +08:00
Alex Lam S.L
6e105c5ca6 enhance merge_vars (#4152) 2020-09-25 22:00:20 +08:00
Alex Lam S.L
af35cd32f2 fix corner case in merge_vars (#4151) 2020-09-25 08:04:51 +08:00
Alex Lam S.L
7de8daa4b1 minor clean up (#4149) 2020-09-23 23:06:12 +08:00
Alex Lam S.L
305a4bdcee minor clean up (#4148) 2020-09-23 16:34:22 +08:00
Alex Lam S.L
3472cf1a90 fix corner case in unused (#4147)
fixes #4146
2020-09-22 20:08:45 +08:00
Alex Lam S.L
6d4c0fa6fa fix corner case in unused (#4145)
fixes #4144
2020-09-22 14:03:27 +08:00
Alex Lam S.L
3cca0d6249 fix corner case in evaluate (#4143)
fixes #4142
2020-09-22 12:11:25 +08:00
Alex Lam S.L
12ac49b970 Merge pull request #4141 from alexlamsl/unused
enhance `unused`
2020-09-22 02:21:43 +01:00
alexlamsl
8c670cae93 enhance unused 2020-09-22 07:48:55 +08:00
Alex Lam S.L
0e3da27727 fix corner case in merge_vars (#4140)
fixes #4139
2020-09-21 23:49:41 +01:00
Alex Lam S.L
13cdc167a2 fix corner case in evaluate (#4138)
fixes #4137
2020-09-22 06:49:32 +08:00
alexlamsl
51803cdcb2 fix corner case in merge_vars
fixes #4139
2020-09-22 05:03:06 +08:00
Alex Lam S.L
8fa470c17c fix corner case in merge_vars (#4136)
fixes #4135
2020-09-20 23:54:14 +08:00
Alex Lam S.L
90410f9fc3 fix corner case in unused (#4134)
fixes #4133
2020-09-20 23:21:59 +08:00
Alex Lam S.L
ef3831437d improve ufuzz duty cycle heuristic (#4132) 2020-09-20 08:29:35 +08:00
Alex Lam S.L
171c544705 fix corner case in merge_vars (#4131)
fixes #4130
2020-09-20 05:36:16 +08:00
Alex Lam S.L
3c609e2f4a enhance unused (#4129) 2020-09-20 01:45:52 +08:00
Alex Lam S.L
f0ae03ed39 report immediate ufuzz failure from Pull Request (#4128) 2020-09-19 20:31:37 +08:00
Alex Lam S.L
31c6b45036 fix corner case in merge_vars (#4127)
fixes #4126
2020-09-19 19:56:21 +08:00
Alex Lam S.L
3ac533e644 enhance merge_vars (#4125) 2020-09-19 11:16:23 +08:00
Alex Lam S.L
38a46c86d7 enhance side_effects (#4124)
- add documentation for `merge_vars`
2020-09-18 21:35:29 +08:00
Alex Lam S.L
0f0759ec15 remove redundant transform (#4123) 2020-09-18 07:04:46 +08:00
Alex Lam S.L
7f501f9fed add tests (#4122) 2020-09-18 00:26:31 +08:00
Alex Lam S.L
72844eb5a4 improve fix for #4119 (#4121) 2020-09-17 23:08:36 +08:00
Alex Lam S.L
09d93cc6c8 fix corner case in evaluate (#4120)
fixes #4119
2020-09-17 21:20:31 +08:00
Alex Lam S.L
dd1374aa8a minor clean up (#4118) 2020-09-17 07:10:45 +08:00
Alex Lam S.L
fdf2e8c5b0 enhance collapse_vars (#4117) 2020-09-17 06:35:22 +08:00
Alex Lam S.L
a9d934ab4e improve handling of switch statements (#4114) 2020-09-17 03:12:08 +08:00
Alex Lam S.L
2a053710bd fix corner case in merge_vars (#4116)
fixes #4115
2020-09-17 03:11:57 +08:00
Alex Lam S.L
219aac6a84 fix corner case in merge_vars (#4113)
fixes #4112
2020-09-16 22:18:28 +08:00
Alex Lam S.L
2039185051 enhance conditionals (#4106) 2020-09-16 05:51:42 +08:00
Alex Lam S.L
ad27c14202 fix corner cases in merge_vars (#4108)
fixes #4107
fixes #4109
fixes #4110
fixes #4111
2020-09-16 04:43:01 +08:00
Alex Lam S.L
a62b086184 enhance merge_vars (#4105) 2020-09-15 22:59:10 +08:00
Alex Lam S.L
335456cf77 fix corner case in merge_vars (#4104)
fixes #4103
2020-09-15 19:47:12 +08:00
Alex Lam S.L
d64d0b0bec fix corner case in merge_vars (#4102)
fixes #4101
2020-09-15 19:18:12 +08:00
Alex Lam S.L
3ac575f2e8 introduce merge_vars (#4100) 2020-09-15 10:01:48 +08:00
Alex Lam S.L
d33a3a3253 enhance unused (#4098) 2020-09-13 01:05:43 +08:00
Alex Lam S.L
d7456a2dc2 enhance if_return (#4097) 2020-09-10 22:31:34 +08:00
Alex Lam S.L
d97672613d fix corner case in reduce_vars (#4095) 2020-09-08 22:12:27 +08:00
Alex Lam S.L
30761eede5 v3.10.4 2020-09-07 00:25:54 +08:00
Alex Lam S.L
fb30aeccaf relax ufuzz job timing constraint (#4094) 2020-09-05 19:29:50 +08:00
Alex Lam S.L
226aa1f76b enhance unsafe_math (#4093) 2020-09-04 10:14:39 +08:00
Alex Lam S.L
6e235602fb fix corner case in loops & unused (#4092)
fixes #4091
2020-09-04 01:51:26 +08:00
Alex Lam S.L
980fcbb56b enhance unused (#4090) 2020-09-03 17:41:33 +08:00
Alex Lam S.L
375ebe316d enhance join_vars (#4089) 2020-09-03 01:41:10 +08:00
Alex Lam S.L
2500930234 enhance reduce_vars (#4088) 2020-09-02 11:30:46 +08:00
Alex Lam S.L
2f0da2ff05 reduce AST_ForIn gracefully (#4087) 2020-09-02 08:51:43 +08:00
Alex Lam S.L
83a3cbf151 fix test case runtime accounting (#4086) 2020-09-02 03:23:08 +08:00
Alex Lam S.L
da8d154571 fix corner case in loops & unused (#4085)
fixes #4084
2020-09-02 03:20:58 +08:00
Alex Lam S.L
e33c727e8b v3.10.3 2020-08-30 13:09:12 +01:00
Alex Lam S.L
f886b3fb2b fix corner case in loops & unused (#4083)
fixes #4082
2020-08-29 02:42:17 +08:00
Alex Lam S.L
b1cc15e85b fix corner case in sequences (#4080)
fixes #4079
2020-08-26 20:41:11 +08:00
Alex Lam S.L
3aa765e429 fix corner case in evaluate (#4078)
fixes #4077
2020-08-26 19:45:38 +08:00
Alex Lam S.L
93d084a1d1 fix corner case in loops & unused (#4076)
fixes #4075
2020-08-26 17:32:20 +08:00
Alex Lam S.L
c7a3e09407 enhance loops & unused (#4074)
- extend `ufuzz` generation of for-in loops
2020-08-26 09:32:55 +08:00
Alex Lam S.L
09525c7530 fix corner case in sequences (#4073) 2020-08-26 01:26:49 +08:00
Alex Lam S.L
a7e15fe73c streamline parenthesis logic (#4072) 2020-08-25 19:45:37 +08:00
Alex Lam S.L
a31c27c7cf fix corner case in collapse_vars (#4071)
fixes #4070
2020-08-25 17:23:36 +08:00
Alex Lam S.L
1caf7c7bd2 minor clean up (#4069) 2020-08-25 10:10:56 +08:00
Alex Lam S.L
0eb0c9b388 fix corner case in evaluate (#4068)
fixes #4067
2020-08-24 14:57:26 +08:00
Alex Lam S.L
7dc61cdc89 tidy up various interfaces (#4066) 2020-08-24 04:39:38 +08:00
Alex Lam S.L
af1b2f30c9 v3.10.2 2020-08-23 23:09:12 +08:00
Alex Lam S.L
37b4fc7e31 update domprops (#4065) 2020-08-23 23:06:15 +08:00
Alex Lam S.L
da85d102e3 enhance mangle.properties (#4064) 2020-08-23 08:45:39 +08:00
Alex Lam S.L
35fe1092d3 simplify traversal logic (#4063) 2020-08-23 05:45:35 +08:00
Alex Lam S.L
f2d486e771 enhance comparisons (#4062) 2020-08-23 01:03:48 +08:00
Alex Lam S.L
fee677786e fix corner case in collapse_vars (#4061) 2020-08-21 10:35:34 +08:00
Alex Lam S.L
aa83ecdb3b fix corner case in switches (#4060)
fixes #4059
2020-08-21 08:05:10 +08:00
Alex Lam S.L
a153176469 enhance conditionals & switches (#4058) 2020-08-21 00:35:39 +08:00
Alex Lam S.L
1c6384b6a5 improve ufuzz duty cycle heuristic (#4057) 2020-08-19 23:29:01 +08:00
Alex Lam S.L
e8db526f51 avoid setters during console.log() in sandbox (#4055)
fixes #4054
2020-08-19 06:14:41 +08:00
Alex Lam S.L
fa13ed4391 reject multiple defaults in switch (#4053)
fixes #4050
2020-08-17 10:09:12 +08:00
Alex Lam S.L
23f0dca992 fix corner cases in collapse_vars & dead_code (#4052)
fixes #4051
2020-08-17 05:54:27 +08:00
Alex Lam S.L
45ab3b51d8 clarify toplevel & global variable aliasing (#4046) 2020-08-10 06:39:28 +08:00
Alex Lam S.L
49670d216b fix corner case in collapse_vars (#4048)
fixes #4047
2020-08-10 05:48:56 +08:00
Alex Lam S.L
e2237d8cd2 improve ufuzz duty cycle heuristic (#4045) 2020-08-09 03:10:19 +08:00
Alex Lam S.L
91f078fe35 workaround incorrect workflow status (#4044) 2020-08-08 05:16:54 +08:00
Alex Lam S.L
a546cb881d improve ufuzz duty cycle on GitHub Actions (#4043) 2020-08-07 18:42:36 +08:00
Alex Lam S.L
84d5dffd9f tweak GitHub Actions (#4042) 2020-08-07 02:15:51 +08:00
Alex Lam S.L
a8e286f7e1 fix corner case in collapse_vars (#4041)
fixes #4040
2020-08-06 20:30:28 +08:00
Alex Lam S.L
9b05494ebc fix corner cases in aliasing of global variables (#4039)
fixes #4038
2020-08-06 09:39:50 +01:00
Alex Lam S.L
30ef20a208 tweak GitHub Actions (#4037) 2020-08-05 22:09:02 +08:00
Alex Lam S.L
a4002ef467 fix corner case in evaluate (#4036)
fixes #4035
2020-08-04 20:05:10 +08:00
Alex Lam S.L
9d758a216b v3.10.1 2020-08-02 21:08:48 +08:00
Alex Lam S.L
af13f8dd2c improve diagnostics upon AST validation failure (#4033) 2020-07-31 22:50:16 +08:00
Alex Lam S.L
88423f2574 validate against multiple parents on AST_Node (#4032)
- fix related issues in `global_defs`, `ie8` & `reduce_vars`
2020-07-31 08:09:19 +08:00
Alex Lam S.L
ee632a5519 fix corner case in reduce_vars (#4031)
fixes #4030
2020-07-31 08:05:09 +08:00
Alex Lam S.L
dfe47bcc42 fix corner case in ie8 & reduce_vars (#4029)
fixes #4028
2020-07-29 03:11:02 +08:00
Alex Lam S.L
6d3dcaa59e fix corner case in unused (#4026)
fixes #4025
2020-07-26 09:27:54 +08:00
Alex Lam S.L
1bc0df1569 fix corner case in hoist_props (#4024)
fixes #4023
2020-07-26 09:27:34 +08:00
Alex Lam S.L
a98ba994bd reduce ufuzz test cases that fail to minify() (#4021) 2020-07-21 17:22:18 +08:00
Alex Lam S.L
cd671221c5 fix corner case in ie8 & reduce_vars (#4020)
fixes #4019
2020-07-21 17:22:18 +08:00
Alex Lam S.L
bce3919748 fix corner case in unused (#4018)
fixes #4017
2020-07-21 17:21:58 +08:00
Alex Lam S.L
61b66e83f1 fix corner case in ie8 (#4016)
fixes #4015
2020-07-21 02:32:20 +08:00
Alex Lam S.L
a5db8cd14c fix corner case in collapse_vars (#4013)
fixes #4012
2020-07-20 23:28:13 +08:00
Alex Lam S.L
2021c2fa3e fix corner case in false positive detection (#4011) 2020-07-20 21:57:22 +08:00
Alex Lam S.L
484d3fd8c7 fix corner case in side_effects (#4009)
fixes #4008
2020-07-01 11:33:48 +08:00
Alex Lam S.L
3bf8699f95 fix corner case in inline (#4007)
fixes #4006
2020-06-29 09:06:23 +08:00
Alex Lam S.L
58c24f8007 v3.10.0 2020-06-21 11:30:24 +01:00
Alex Lam S.L
e61bc34eb1 fix corner case in collapse_vars (#4002)
fixes #4001
2020-06-20 02:19:37 +08:00
Alex Lam S.L
8b2cfd45fa fix corner case in rename (#4000)
fixes #3999
2020-06-15 01:29:01 +08:00
Alex Lam S.L
ae9f56be10 fix corner case in evaluate (#3998)
fixes #3997
2020-06-15 01:28:44 +08:00
Alex Lam S.L
9aed0e3a73 speed up false positive detection in ufuzz (#3996) 2020-06-14 03:42:42 +08:00
Alex Lam S.L
88850a6e05 enhance evaluate (#3995) 2020-06-14 02:50:26 +08:00
Alex Lam S.L
9e881407bd fix corner cases related to AST_Hole (#3994) 2020-06-13 15:24:57 +01:00
Alex Lam S.L
3188db7b90 remove AppVeyor (#3992) 2020-06-12 08:43:42 +08:00
Alex Lam S.L
a82ca62b66 fix corner case in dead_code (#3991) 2020-06-12 08:00:19 +08:00
Alex Lam S.L
e9465717ab enhance dead_code (#3990) 2020-06-12 02:16:13 +08:00
Alex Lam S.L
e89031f1af fix corner case in unsafe evaluate (#3989)
fixes #3988
2020-06-11 07:37:39 +08:00
Alex Lam S.L
596fad182e fix corner case in unused (#3987)
fixes #3986
2020-06-11 02:01:23 +08:00
Alex Lam S.L
ed69adedcd fix corner case in --reduce-test (#3985) 2020-06-10 15:51:00 +01:00
Alex Lam S.L
1dbf7d4a3a fix corner case in side_effects (#3984)
fixes #3983
2020-06-10 19:30:37 +08:00
Alex Lam S.L
2a9d0fc6fb improve false positive detection in ufuzz (#3982) 2020-06-10 07:28:56 +08:00
Alex Lam S.L
45db96679e perform ufuzz on more stable Node.js version (#3981) 2020-06-10 00:02:05 +08:00
Alex Lam S.L
1d15f51238 improve fix for #3976 (#3980) 2020-06-10 00:00:57 +08:00
Alex Lam S.L
ed7c82fa5e fix corner case in collapse_vars (#3978)
fixes #3976
2020-06-09 19:07:20 +08:00
Alex Lam S.L
3b273cecac improve false positive detection in ufuzz (#3977) 2020-06-09 19:07:02 +08:00
Alex Lam S.L
d764b6cc3b fix corner case in reduce_vars (#3975)
fixes #3974
2020-06-09 10:33:47 +08:00
Alex Lam S.L
08c4729eb4 improve false positive detection in ufuzz (#3973) 2020-06-09 01:47:50 +08:00
Alex Lam S.L
5561d3e7f3 fix corner case in collapse_vars (#3972)
fixes #3971
2020-06-09 00:09:21 +08:00
Alex Lam S.L
491d6ce1d5 improve false positive detection in ufuzz (#3968) 2020-06-08 14:21:45 +08:00
Alex Lam S.L
cd55eeb77c fix corner case in dead_code (#3969)
fixes #3967
2020-06-08 13:42:09 +08:00
Alex Lam S.L
3230952d57 improve handling of invalid CLI options (#3966) 2020-06-08 11:16:03 +08:00
Alex Lam S.L
df3bb8028a fix corner cases related to in (#3964) 2020-06-08 05:23:23 +08:00
Alex Lam S.L
28b7b15da1 parse command line internally (#3961) 2020-06-07 13:48:51 +08:00
Alex Lam S.L
aa37b19698 fix corner case in unused (#3963)
fixes #3962
2020-06-07 13:48:29 +08:00
Alex Lam S.L
02e889e449 improve fix for #3958 (#3960) 2020-06-06 15:07:32 +01:00
Alex Lam S.L
486ce00b8e fix corner case in reduce_vars (#3959)
fixes #3958
2020-06-06 10:04:37 +08:00
Alex Lam S.L
eb481cee8c fix corner cases in reduce_vars & unused (#3955)
fixes #3953
fixes #3956
fixes #3957
2020-06-05 18:51:21 +08:00
Alex Lam S.L
fbc9d8009b fix corner case in collapse_vars (#3954) 2020-06-05 14:28:08 +08:00
Alex Lam S.L
04fd3d90f8 fix corner cases in assignments, reduce_vars & unused (#3950)
fixes #3949
fixes #3951
2020-06-05 04:06:43 +08:00
Alex Lam S.L
a489f8cb5e add test case for #3945 (#3948) 2020-06-03 03:34:38 +08:00
Alex Lam S.L
e2e4b7fb37 fix corner case in hoist_props (#3947)
fixes #3945
2020-06-02 23:51:06 +08:00
Alex Lam S.L
c97ad98f92 fix corner case in evaluate (#3946)
fixes #3944
2020-06-02 23:50:40 +08:00
Alex Lam S.L
b24eb22c6b enhance reduce_vars (#3942) 2020-06-01 20:55:23 +08:00
Alex Lam S.L
06ba4e2ce8 fix corner case in arguments (#3939) 2020-05-31 12:18:27 +08:00
Alex Lam S.L
0eb4577a82 fix corner case in evaluate (#3938)
fixes #3937
2020-05-30 18:22:40 +08:00
Alex Lam S.L
43498769f0 fix corner case in evaluate (#3936)
fixes #3935
2020-05-29 22:10:36 +08:00
Alex Lam S.L
60c0bc1e6b fix corner case in evaluate (#3934)
fixes #3933
2020-05-29 17:48:26 +08:00
Alex Lam S.L
6a5c63e1e3 enhance evaluate, functions & inline (#3931) 2020-05-29 07:05:47 +08:00
Alex Lam S.L
d47ea77811 fix corner case in functions (#3930)
fixes #3929
2020-05-28 20:07:36 +08:00
Alex Lam S.L
7840746bd9 fix corner case in collapse_vars (#3928)
fixes #3927
2020-05-27 21:02:48 +08:00
Alex Lam S.L
49ea629f3f v3.9.4 2020-05-27 07:50:18 +01:00
Alex Lam S.L
13c72a986c fix corner case in infinite recursion detection (#3926) 2020-05-27 02:03:38 +08:00
Alex Lam S.L
08af3eae44 perform ufuzz on Pull Requests (#3925) 2020-05-25 22:55:14 +08:00
Alex Lam S.L
27bdcbbd83 fix corner cases in infinite recursion detection (#3924) 2020-05-25 22:54:57 +08:00
Alex Lam S.L
2c4d7d66ef fix corner case in reduce_vars (#3923)
fixes #3922
2020-05-24 07:38:40 +08:00
Alex Lam S.L
d1cc5270a3 fix corner case in evaluate (#3921)
fixes #3920
2020-05-22 11:38:09 +08:00
Alex Lam S.L
75c5b6029b fix corner case in ie8 & reduce_vars (#3919)
fixes #3918
2020-05-22 09:56:35 +08:00
Alex Lam S.L
fa14a9cfcd fix corner case in join_vars (#3917)
fixes #3916
2020-05-22 05:26:46 +08:00
Alex Lam S.L
aeb9ea5ac2 fix corner case in inline (#3915)
fixes #3911
2020-05-21 22:05:31 +08:00
Alex Lam S.L
798841be82 improve job resilience (#3913) 2020-05-21 04:50:42 +08:00
Alex Lam S.L
cc6eb4b15f improve ufuzz (#3912)
- preserve test case if `beautify` suppresses bug
- determine suspicious options even if `minify()` fails
2020-05-21 04:00:38 +08:00
Alex Lam S.L
14eee81dc6 update header comment for --reduce-test (#3910) 2020-05-19 11:35:33 +08:00
Alex Lam S.L
55ebb27878 fix corner case in collapse_vars (#3909)
fixes #3908
2020-05-19 11:34:50 +08:00
Alex Lam S.L
87046410ef enhance dead_code (#3907) 2020-05-19 03:53:08 +08:00
Alex Lam S.L
f9b3198714 fix corner case in evaluate (#3906)
fixes #3905
2020-05-18 08:41:10 +08:00
Alex Lam S.L
48b62393a4 fix corner case in evaluate (#3904)
fixes #3903
2020-05-17 22:25:13 +08:00
Alex Lam S.L
a00f8dade7 fix suspicious toplevel detection (#3902) 2020-05-17 21:35:17 +08:00
Alex Lam S.L
9daa2fb6f5 benchmark without validation by default (#3901) 2020-05-15 23:57:50 +08:00
Alex Lam S.L
8d81d264f4 fix corner case in functions (#3900)
fixes #3899
2020-05-15 18:03:56 +08:00
Alex Lam S.L
5ef7060098 fix corner case in collapse_vars (#3898)
fixes #3897
2020-05-15 01:09:54 +08:00
Alex Lam S.L
938368ba21 enhance collapse_vars (#3896) 2020-05-14 07:52:42 +08:00
Alex Lam S.L
fe2f1965d6 fix corner case in reduce_vars (#3895)
fixes #3894
2020-05-13 23:44:54 +08:00
Alex Lam S.L
30ed8f5580 v3.9.3 2020-05-13 17:23:01 +08:00
Alex Lam S.L
dc9e7cd1fe suppress ufuzz false positives (#3893) 2020-05-13 07:07:49 +08:00
Alex Lam S.L
76f40e2528 fix corner case in collapse_vars (#3892)
fixes #3891
2020-05-12 22:28:55 +08:00
Alex Lam S.L
8024f7f7a8 fix corner case in ie8 (#3890)
fixes #3889
2020-05-12 19:28:29 +08:00
Alex Lam S.L
eb7fa25270 fix corner case in evaluate (#3888)
fixes #3887
2020-05-12 17:58:37 +08:00
Alex Lam S.L
ee7647dc67 fix corner case in collapse_vars (#3885)
fixes #3884
2020-05-12 04:01:14 +08:00
Alex Lam S.L
bd2f53bc8b fix corner case in evaluate (#3883)
fixes #3882
2020-05-12 03:24:44 +08:00
Alex Lam S.L
e8a7956b6f fix corner case in reduce_vars (#3881)
fixes #3880
2020-05-12 02:29:33 +08:00
Alex Lam S.L
2b24dc25fb fix corner cases in evaluate & reduce_vars (#3879)
fixes #3878
2020-05-11 22:46:00 +08:00
Alex Lam S.L
35cc5aa06f extend --reduce-test to cover minify() bugs (#3876) 2020-05-11 07:32:21 +08:00
Alex Lam S.L
c1dd49e075 fix corner case in comparisons (#3877) 2020-05-11 06:33:52 +08:00
Alex Lam S.L
c76ee4b868 enhance if_return (#3875) 2020-05-11 04:29:55 +08:00
Alex Lam S.L
e23bf48052 enhance evaluate & reduce_vars (#3873) 2020-05-11 03:08:05 +08:00
Alex Lam S.L
7e0ad232b0 retain @__PURE__ call when return value is used (#3874) 2020-05-11 01:07:05 +08:00
Alex Lam S.L
63adfb1590 fix corner case in hoist_props (#3872)
fixes #3871
2020-05-10 22:23:09 +08:00
Alex Lam S.L
f9806b43c3 enhance evaluate & reduce_vars (#3870) 2020-05-10 18:38:32 +08:00
Alex Lam S.L
c4c9c6d37d fix corner case in hoist_props (#3869)
fixes #3868
2020-05-10 10:35:24 +01:00
Alex Lam S.L
33f3b0c1d9 fix corner case in reduce_vars (#3867)
fixes #3866
2020-05-10 16:35:03 +08:00
Alex Lam S.L
abb8ae02a5 improve inline of /*@__PURE__*/ calls (#3865) 2020-05-10 07:16:09 +08:00
Alex Lam S.L
97728c4f0b improve AST validation (#3864) 2020-05-10 05:25:44 +08:00
Alex Lam S.L
f74b7f7401 implement AST validation (#3863) 2020-05-09 09:58:03 +08:00
Alex Lam S.L
b06fd8a933 improve fix for #3856 (#3862) 2020-05-09 08:50:25 +08:00
Alex Lam S.L
1bb0804d60 improve ufuzz detection of suspicious options (#3860) 2020-05-08 15:03:48 +08:00
Alex Lam S.L
998245ffd6 fix corner case in inline (#3859)
fixes #3858
2020-05-08 15:03:29 +08:00
Alex Lam S.L
7a033bb825 fix corner case in join_vars (#3857)
fixes #3856
2020-05-08 11:49:17 +08:00
Alex Lam S.L
a441b00951 suppress ufuzz false positives (#3855) 2020-05-08 03:21:44 +08:00
Alex Lam S.L
88985a46ed fix corner case in inline (#3853)
fixes #3852
2020-05-07 20:53:05 +08:00
Alex Lam S.L
34ead0430b enhance dead_code (#3849) 2020-05-06 05:02:35 +08:00
Alex Lam S.L
66ab2df97f fix intermittent CI failures on GitHub Actions (#3851) 2020-05-06 03:29:23 +08:00
kzc
b656f7c083 remove unused returns from tree walk (#3850) 2020-05-06 02:21:36 +08:00
Alex Lam S.L
873db281e8 improve TreeWalker performance (#3848) 2020-05-05 22:45:58 +08:00
Alex Lam S.L
6bf1486935 update links to repository after rename (#3847) 2020-05-05 21:07:33 +08:00
Alex Lam S.L
ffa1943177 fix corner case in reduce_vars (#3845)
fixes #3844
2020-05-04 03:30:10 +08:00
Alex Lam S.L
ac429dc8e1 enhance reduce_vars (#3843) 2020-05-03 22:52:43 +08:00
Alex Lam S.L
3766d5c962 enhance unused (#3839) 2020-05-03 17:38:28 +08:00
Alex Lam S.L
20f9a1d908 v3.9.2 2020-05-03 11:01:18 +08:00
Alex Lam S.L
dcb74f558e fix diagnostic text (#3838) 2020-05-01 18:55:06 +08:00
Alex Lam S.L
0794aaa2c2 fix corner case in inline (#3837)
fixes #3836
2020-05-01 17:20:23 +08:00
Alex Lam S.L
74801de315 fix corner cases in inline (#3834)
fixes #3833
fixes #3835
2020-05-01 09:06:40 +08:00
Alex Lam S.L
f80d5b8c9e enhance inline (#3832) 2020-05-01 04:33:46 +08:00
Alex Lam S.L
d900006973 fix corner case in dead_code (#3831)
fixes #3830
2020-04-30 21:52:57 +08:00
Alex Lam S.L
39f849590b update dependencies (#3828)
- actions/checkout@v2
2020-04-30 05:55:04 +08:00
Alex Lam S.L
818738beec fix corner case in ie8 (#3826)
fixes #3825
2020-04-27 16:51:21 +08:00
Alex Lam S.L
bc2a4a3bb8 fix corner case in ie8 (#3824)
fixes #3823
2020-04-27 06:44:53 +08:00
Alex Lam S.L
a4a8ccea8c fix corner case in inline (#3822)
fixes #3821
2020-04-27 03:31:19 +08:00
Alex Lam S.L
36dcfa3e82 improve suspicious option detection (#3820) 2020-04-27 00:59:26 +08:00
Alex Lam S.L
94f33570e3 fix corner case in --reduce-test (#3819) 2020-04-25 06:49:50 +08:00
Alex Lam S.L
44d6912a55 improve --reduce-test on Error.message (#3816)
closes #3815
2020-04-25 01:30:37 +08:00
Alex Lam S.L
3a4497a1c3 fix corner case in typeofs (#3818)
fixes #3817
2020-04-25 01:29:42 +08:00
Alex Lam S.L
3ee13cae02 improve compress (#3814)
- avoid identifier overflow through consecutive API calls
- simplify `reduce_vars`
- enhance `unsafe` `evaluate`
2020-04-24 06:50:53 +08:00
Alex Lam S.L
99cf3a38c5 improve unused efficiency (#3813) 2020-04-23 08:31:35 +08:00
Alex Lam S.L
3ae24329eb gate various label-related transformations (#3812) 2020-04-23 05:27:26 +08:00
Alex Lam S.L
01b13d797c enhance dead_code (#3811) 2020-04-22 18:22:45 +08:00
Alex Lam S.L
306e8e9873 adjust ufuzz schedule (#3810) 2020-04-22 16:03:15 +08:00
Alex Lam S.L
9577c8c1b7 fix corner case in conditionals (#3809)
fixes #3808
2020-04-22 06:30:08 +08:00
Alex Lam S.L
925a0ca1a0 adjust ufuzz schedule (#3807) 2020-04-22 02:58:02 +08:00
Alex Lam S.L
b694bfa351 speed up ufuzz on GitHub Actions (#3806) 2020-04-21 22:51:42 +08:00
Alex Lam S.L
a2fc32c64b enhance conditionals (#3805) 2020-04-20 09:42:13 +08:00
Alex Lam S.L
88504ab869 enhance join_vars (#3804) 2020-04-20 06:37:46 +08:00
Alex Lam S.L
e38754e802 fix corner case in functions & unused (#3803)
fixes #3802
2020-04-19 06:28:01 +08:00
Alex Lam S.L
eb6f32bfc3 enhance collapse_vars (#3801) 2020-04-19 05:04:21 +08:00
Alex Lam S.L
f110601fb4 enhance unused (#3800) 2020-04-19 00:10:24 +08:00
Alex Lam S.L
2a508c6e5f enhance collapse_vars (#3799) 2020-04-18 23:12:20 +08:00
Alex Lam S.L
fd6144d95b enhance conditionals (#3798) 2020-04-18 22:32:22 +08:00
Alex Lam S.L
60d4e7b09f enhance unused (#3794) 2020-04-18 18:52:44 +08:00
Alex Lam S.L
b38838c6bf fix corner case in join_vars (#3796)
fixes #3795
2020-04-18 18:08:05 +08:00
Alex Lam S.L
708973e51d report top-level default options (#3797) 2020-04-18 18:03:06 +08:00
Alex Lam S.L
dac9e69f9e enhance collapse_vars (#3793) 2020-04-18 10:06:20 +08:00
Alex Lam S.L
39aa33749b expand ufuzz patterns (#3792) 2020-04-18 07:12:13 +08:00
Alex Lam S.L
da68ec6e19 fix corner cases in join_vars (#3790)
fixes #3789
fixes #3791
2020-04-18 02:53:26 +08:00
Alex Lam S.L
15a3ebd467 fix corner cases in join_vars (#3787)
fixes #3786
fixes #3788
2020-04-17 21:19:18 +08:00
Alex Lam S.L
9110fac9a2 suppress mutation of input options (#3785) 2020-04-17 15:13:49 +08:00
Alex Lam S.L
83f42ede36 support optional output of names in source maps (#3784) 2020-04-17 07:20:48 +08:00
Alex Lam S.L
0ce71bbec0 enhance join_vars (#3783) 2020-04-17 05:31:33 +08:00
Alex Lam S.L
46d142cbf6 improve source-map generation (#3782)
- emit singleton segments to mark generated code from input
2020-04-16 23:30:25 +08:00
Alex Lam S.L
38c3bcf9a0 v3.9.1 2020-04-15 17:26:30 +08:00
Alex Lam S.L
6e9afdc94f prevent input source map mutation (#3781)
fixes #3780
2020-04-15 17:25:58 +08:00
Alex Lam S.L
c4d28e3b2a expand testing on Node.js versions (#3779) 2020-04-14 10:13:42 +08:00
Alex Lam S.L
77261e1ee0 v3.9.0 2020-04-13 13:45:02 +01:00
Alex Lam S.L
903a5df9a5 fix corner case in inline (#3778)
fixes #3777
2020-04-11 19:54:26 +08:00
Alex Lam S.L
c810ecd081 improve handling of eval (#3776)
closes #3768
2020-04-11 06:36:17 +08:00
Alex Lam S.L
dce9dfce0e fix corner case in reduce_vars (#3775)
fixes #3774
2020-04-11 02:19:38 +08:00
Alex Lam S.L
3d72663689 add tests for eval() (#3769)
closes #3768
2020-04-11 00:36:53 +08:00
Alex Lam S.L
a2b16e89a4 fix corner cases in inline (#3773)
fixes #3770
fixes #3771
fixes #3772
2020-04-11 00:34:45 +08:00
Alex Lam S.L
b35f4c5a83 enhance inline (#3767) 2020-04-10 10:48:24 +08:00
Alex Lam S.L
41eb4f1725 workaround intermittent nodejs.org corruptions (#3766) 2020-04-07 08:40:38 +08:00
Alex Lam S.L
94bc221669 fix export of PATH to Node.js (#3765) 2020-04-07 01:14:16 +08:00
Alex Lam S.L
822d298a55 fix Github Actions retry logic (#3763) 2020-04-06 22:16:48 +08:00
Alex Lam S.L
273c6020ba expand ufuzz patterns (#3761) 2020-04-05 22:12:46 +08:00
Alex Lam S.L
1b07f64057 enhance inline (#3760) 2020-04-05 10:42:23 +08:00
Alex Lam S.L
80d9c44b22 improve resilience against nodejs.org failures (#3759) 2020-04-03 02:49:38 +08:00
Alex Lam S.L
dc0cd088cf fix corner case in evaluate & unsafe_math (#3756)
fixes #3755
2020-03-30 19:13:14 +08:00
Alex Lam S.L
c69c026728 improve resilience against nodejs.org failures (#3758) 2020-03-30 10:20:13 +08:00
Alex Lam S.L
b5f4e1187f handle single-field segments (#3757) 2020-03-30 06:39:32 +08:00
Alex Lam S.L
827bcec186 handle source-map operations internally (#3754) 2020-03-28 22:18:56 +08:00
Alex Lam S.L
d105ab9722 v3.8.1 2020-03-28 01:04:40 +08:00
Alex Lam S.L
b39228892d fix line accounting in multi-line strings (#3752)
fixes #3748
2020-03-21 07:17:41 +08:00
Alex Lam S.L
ff72eaa3c3 improve --reduce-test (#3742)
- ignore difference in error messages
- improve readability on trailing whitespace differences
- improve performance & quality via `console.log()` insertions
2020-03-21 05:50:41 +08:00
Alex Lam S.L
0a1c9b34ce fix corner case in evaluate & ie8 (#3751)
fixes #3750
2020-03-21 00:55:24 +08:00
Alex Lam S.L
03e968be62 improve suspicious option detection (#3749) 2020-03-13 04:03:47 +08:00
Alex Lam S.L
421bb7083a fix corner case in unused (#3747)
fixes #3746
2020-03-06 18:27:42 +00:00
Alex Lam S.L
bdc8ef2218 fix corner case in collapse_vars (#3745)
fixes #3744
2020-03-06 18:27:06 +00:00
Alex Lam S.L
bca52fcba2 speed up CI (#3741) 2020-03-02 22:07:30 +08:00
Alex Lam S.L
d6d31cbb5a improve AST fuzzing (#3740) 2020-03-02 19:38:30 +08:00
Alex Lam S.L
a051846d22 fix corner case in evaluate (#3739)
fixes #3738
2020-03-01 20:34:31 +00:00
Alex Lam S.L
3485472866 avoid reducing setter argument (#3737) 2020-03-01 05:04:21 +00:00
Alex Lam S.L
c8d60d6983 detect toplevel option properly (#3735)
fixes #3730
2020-02-29 17:33:48 +00:00
Alex Lam S.L
6092bf23de fix corner case in evaluate (#3729) 2020-02-19 00:41:10 +00:00
Alex Lam S.L
7052ce5aef fix corner case in evaluate (#3728)
- augment `ufuzz` for further `RegExp` testing
2020-02-18 19:35:37 +00:00
99 changed files with 18848 additions and 4018 deletions

View File

@@ -1,31 +1,48 @@
name: CI name: CI
on: [ push, pull_request ] on:
pull_request:
push:
branches: [ master ]
jobs: jobs:
test: test:
strategy: strategy:
matrix: matrix:
node: [ "0.8", "0.10", "0.12", "4", "6", "8", "10", "12", latest ]
os: [ ubuntu-latest, windows-latest ] os: [ ubuntu-latest, windows-latest ]
node: [ "0.10", 0.12, 4, 6, 8, 10, latest ]
script: [ compress, mocha, release/benchmark, release/jetstream ] script: [ compress, mocha, release/benchmark, release/jetstream ]
name: ${{ matrix.os }} ${{ matrix.node }} ${{ matrix.script }} exclude:
- node: "0.8"
script: release/benchmark
- node: "0.8"
script: release/jetstream
name: ${{ matrix.node }} ${{ matrix.os }} ${{ matrix.script }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
NODE: ${{ matrix.node }} NODE: ${{ matrix.node }}
TYPE: ${{ matrix.script }} TYPE: ${{ matrix.script }}
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- uses: actions/cache@v1 - uses: actions/cache@v2
with: with:
path: tmp path: tmp
key: tmp ${{ matrix.script }} key: tmp ${{ matrix.script }}
- name: Perform tests - name: Perform tests
shell: bash shell: bash
run: | run: |
git clone --branch v1.5.3 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version . ~/.nvs/nvs.sh --version
nvs add $NODE
nvs use $NODE nvs use $NODE
node --version node --version
npm --version --no-update-notifier npm config set audit false
npm install --no-audit --no-optional --no-save --no-update-notifier npm config set optional false
npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
node test/$TYPE node test/$TYPE

View File

@@ -1,7 +1,13 @@
name: Fuzzing name: Fuzzing
on: on:
pull_request:
schedule: schedule:
- cron: "*/15 * * * *" - cron: "*/5 * * * *"
env:
BASE_URL: https://api.github.com/repos/${{ github.repository }}
CAUSE: ${{ github.event_name }}
RUN_NUM: ${{ github.run_number }}
TOKEN: ${{ github.token }}
jobs: jobs:
ufuzz: ufuzz:
strategy: strategy:
@@ -11,15 +17,28 @@ jobs:
name: ${{ matrix.os }} name: ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Perform fuzzing - name: Perform fuzzing
shell: bash shell: bash
run: | run: |
git clone --branch v1.5.3 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add 8 && nvs use 8'; do
cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version . ~/.nvs/nvs.sh --version
nvs add 10 nvs use 8
nvs use 10
node --version node --version
npm --version --no-update-notifier npm config set audit false
npm install --no-audit --no-optional --no-save --no-update-notifier npm config set optional false
node test/ufuzz/job 3600000 npm config set save false
npm config set strict-ssl false
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
if [[ $CAUSE == "schedule" ]]; then
node test/ufuzz/job $BASE_URL $TOKEN $RUN_NUM
else
node test/ufuzz/job 5000
fi

View File

@@ -4,8 +4,8 @@ UglifyJS 3
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit. UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note: #### Note:
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**. - **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**. - **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- `uglify-js` only supports JavaScript (ECMAScript 5). - `uglify-js` only supports JavaScript (ECMAScript 5).
- To minify ECMAScript 2015 or above, transpile using tools like [Babel](https://babeljs.io/). - To minify ECMAScript 2015 or above, transpile using tools like [Babel](https://babeljs.io/).
@@ -126,6 +126,7 @@ a double dash to prevent input files being used as option arguments:
`includeSources` Pass this flag if you want to include `includeSources` Pass this flag if you want to include
the content of source files in the the content of source files in the
source map as sourcesContent property. source map as sourcesContent property.
`names` Include symbol names in the source map.
`root` Path to the original source to be included in `root` Path to the original source to be included in
the source map. the source map.
`url` If specified, path to the source map to append in `url` If specified, path to the source map to append in
@@ -159,6 +160,9 @@ Additional options:
- `--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 "names=false"` to omit symbol names if you want to reduce size
of the source map file.
- `--source-map "url='<URL>'"` to specify the URL where the source map 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 Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
`//# sourceMappingURL=` directive. `//# sourceMappingURL=` directive.
@@ -208,17 +212,16 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported: (comma-separated) options are supported:
- `toplevel` (default `false`) -- mangle names declared in the top level scope. - `eval` (default `false`) -- mangle names visible in scopes where `eval` or
`with` are used.
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used. - `reserved` (default: `[]`) -- 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:
When mangling is enabled but you want to prevent certain names from being uglifyjs ... -m reserved=['$','require','exports']
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=['$','require','exports'] to prevent the `require`, `exports` and `$` names from being changed.
to prevent the `require`, `exports` and `$` names from being changed.
### CLI mangling property names (`--mangle-props`) ### CLI mangling property names (`--mangle-props`)
@@ -593,6 +596,9 @@ var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`. If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
If you wish to reduce file size of the source map, set option `sourceMap.names`
to be `false` and all symbol names will be omitted.
## Parse options ## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements - `bare_returns` (default `false`) -- support top level `return` statements
@@ -650,8 +656,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `hoist_props` (default: `true`) -- hoist properties from constant object and - `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example: 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` `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, works best with `toplevel` and `mangle` enabled, alongside with `compress` option
and the `compress` option `toplevel` enabled. `passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false` - `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general) by default because it seems to increase the size of the output in general)
@@ -682,6 +688,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops - `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
when we can statically determine the condition. when we can statically determine the condition.
- `merge_vars` (default: `true`) -- combine and reuse variables.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions" - `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the where the return value is discarded, to avoid the parens that the
code generator would insert. code generator would insert.
@@ -906,7 +914,7 @@ can pass additional arguments that control the code output:
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked - `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
function expressions. See function expressions. See
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details. [#640](https://github.com/mishoo/UglifyJS/issues/640) for more details.
# Miscellaneous # Miscellaneous
@@ -1065,8 +1073,8 @@ var result = UglifyJS.minify(ast, {
### Working with Uglify AST ### Working with Uglify AST
Transversal and transformation of the native AST can be performed through Transversal and transformation of the native AST can be performed through
[`TreeWalker`](https://github.com/mishoo/UglifyJS2/blob/master/lib/ast.js) and [`TreeWalker`](https://github.com/mishoo/UglifyJS/blob/master/lib/ast.js) and
[`TreeTransformer`](https://github.com/mishoo/UglifyJS2/blob/master/lib/transform.js) [`TreeTransformer`](https://github.com/mishoo/UglifyJS/blob/master/lib/transform.js)
respectively. respectively.
### ESTree / SpiderMonkey AST ### ESTree / SpiderMonkey AST
@@ -1150,3 +1158,19 @@ To allow for better optimizations, the compiler makes various assumptions:
- Object properties can be added, removed and modified (not prevented with - Object properties can be added, removed and modified (not prevented with
`Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`, `Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`Object.preventExtensions()` or `Object.seal()`). `Object.preventExtensions()` or `Object.seal()`).
- When `toplevel` is enabled, UglifyJS effectively assumes input code is wrapped
within `function(){ ... }`, thus forbids aliasing of declared global variables:
```js
A = "FAIL";
var B = "FAIL";
// can be `global`, `self`, `window` etc.
var top = function() {
return this;
}();
// "PASS"
top.A = "PASS";
console.log(A);
// "FAIL" after compress and/or mangle
top.B = "PASS";
console.log(B);
```

View File

@@ -1,5 +0,0 @@
build: off
matrix:
fast_finish: true
test_script:
- echo No longer in use

View File

@@ -8,166 +8,252 @@ require("../tools/exit");
var fs = require("fs"); var fs = require("fs");
var info = require("../package.json"); var info = require("../package.json");
var path = require("path"); var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node"); var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ]; var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var files = {}; var files = {};
var options = { var options = {};
compress: false, var short_forms = {
mangle: false b: "beautify",
c: "compress",
d: "define",
e: "enclose",
h: "help",
m: "mangle",
o: "output",
O: "output-opts",
p: "parse",
v: "version",
V: "version",
}; };
program.version(info.name + " " + info.version); var args = process.argv.slice(2);
program.parseArgv = program.parse; var paths = [];
program.parse = undefined; var output, nameCache;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast; var specified = {};
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() { while (args.length) {
var text = []; var arg = args.shift();
var options = UglifyJS.default_options(); if (arg[0] != "-") {
for (var option in options) { paths.push(arg);
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:"); } else if (arg == "--") {
text.push(format_object(options[option])); paths = paths.concat(args);
text.push(""); break;
} else if (arg[1] == "-") {
process_option(arg.slice(2));
} else [].forEach.call(arg.slice(1), function(letter, index, arg) {
if (!(letter in short_forms)) fatal("invalid option -" + letter);
process_option(short_forms[letter], index + 1 < arg.length);
});
}
function process_option(name, no_value) {
specified[name] = true;
switch (name) {
case "help":
switch (read_value()) {
case "ast":
print(UglifyJS.describe_ast());
break;
case "options":
var text = [];
var toplevels = [];
var padding = "";
var defaults = UglifyJS.default_options();
for (var name in defaults) {
var option = defaults[name];
if (option && typeof option == "object") {
text.push("--" + ({
output: "beautify",
sourceMap: "source-map",
}[name] || name) + " options:");
text.push(format_object(option));
text.push("");
} else {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
toplevels.push([ {
keep_fnames: "keep-fnames",
nameCache: "name-cache",
}[name] || name, option ]);
}
}
toplevels.forEach(function(tokens) {
text.push("--" + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
print(text.join("\n"));
break;
default:
print([
"Usage: uglifyjs [files...] [options]",
"",
"Options:",
" -h, --help Print usage information.",
" `--help options` for details on available options.",
" -v, -V, --version Print version number.",
" -p, --parse <options> Specify parser options.",
" -c, --compress [options] Enable compressor/specify compressor options.",
" -m, --mangle [options] Mangle names/specify mangler options.",
" --mangle-props [options] Mangle properties/specify mangler options.",
" -b, --beautify [options] Beautify output/specify output options.",
" -O, --output-opts <options> Output options (beautify disabled).",
" -o, --output <file> Output file (default STDOUT).",
" --comments [filter] Preserve copyright comments in the output.",
" --config-file <file> Read minify() options from JSON file.",
" -d, --define <expr>[=value] Global definitions.",
" -e, --enclose [arg[,...][:value[,...]]] Embed everything in a big function, with configurable argument(s) & value(s).",
" --ie8 Support non-standard Internet Explorer 8.",
" --keep-fnames Do not mangle/drop function names. Useful for code relying on Function.prototype.name.",
" --name-cache <file> File to hold mangled name mappings.",
" --rename Force symbol expansion.",
" --no-rename Disable symbol expansion.",
" --self Build UglifyJS as a library (implies --wrap UglifyJS)",
" --source-map [options] Enable source map/specify source map options.",
" --timings Display operations run time on STDERR.",
" --toplevel Compress and/or mangle variables in toplevel scope.",
" --validate Perform validation during AST manipulations.",
" --verbose Print diagnostic messages.",
" --warn Print warning messages.",
" --wrap <name> Embed everything as a function with “exports” corresponding to “name” globally.",
" --reduce-test Reduce a standalone test case (assumes cloned repository).",
].join("\n"));
}
process.exit();
case "version":
print(info.name + " " + info.version);
process.exit();
case "config-file":
var config = JSON.parse(read_file(read_value(true)));
if (config.mangle && config.mangle.properties && config.mangle.properties.regex) {
config.mangle.properties.regex = UglifyJS.parse(config.mangle.properties.regex, {
expression: true,
}).value;
}
for (var key in config) if (!(key in options)) options[key] = config[key];
break;
case "compress":
case "mangle":
options[name] = parse_js(read_value(), options[name]);
break;
case "source-map":
options.sourceMap = parse_js(read_value(), options.sourceMap);
break;
case "enclose":
options[name] = read_value();
break;
case "ie8":
case "timings":
case "toplevel":
case "validate":
options[name] = true;
break;
case "keep-fnames":
options.keep_fnames = true;
break;
case "wrap":
options[name] = read_value(true);
break;
case "verbose":
options.warnings = "verbose";
break;
case "warn":
if (!options.warnings) options.warnings = true;
break;
case "beautify":
options.output = parse_js(read_value(), options.output);
if (!("beautify" in options.output)) options.output.beautify = true;
break;
case "output-opts":
options.output = parse_js(read_value(true), options.output);
break;
case "comments":
if (typeof options.output != "object") options.output = {};
options.output.comments = read_value();
if (options.output.comments === true) options.output.comments = "some";
break;
case "define":
if (typeof options.compress != "object") options.compress = {};
options.compress.global_defs = parse_js(read_value(true), options.compress.global_defs, "define");
break;
case "mangle-props":
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = parse_js(read_value(), options.mangle.properties);
break;
case "name-cache":
nameCache = read_value(true);
options.nameCache = JSON.parse(read_file(nameCache, "{}"));
break;
case "output":
output = read_value(true);
break;
case "parse":
options.parse = parse_js(read_value(true), options.parse);
break;
case "rename":
options.rename = true;
break;
case "no-rename":
options.rename = false;
break;
case "reduce-test":
case "self":
break;
default:
fatal("invalid option --" + name);
} }
return text.join("\n");
}; function read_value(required) {
program.option("-p, --parse <options>", "Specify parser options.", parse_js()); if (no_value || !args.length || args[0][0] == "-") {
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js()); if (required) fatal("missing option argument for --" + name);
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js()); return true;
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js()); }
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js()); return args.shift();
program.option("-O, --output-opts [options]", "Output options (beautify disabled).", 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.");
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
program.option("-e, --enclose [arg[,...][:value[,...]]]", "Embed everything in a big function, with configurable argument(s) & value(s).");
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("--rename", "Force symbol expansion.");
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_js());
program.option("--timings", "Display operations run time on STDERR.");
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages.");
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally.");
program.option("--reduce-test", "Reduce a standalone `console.log` based test case.");
program.arguments("[files...]").parseArgv(process.argv);
if (program.configFile) {
options = JSON.parse(read_file(program.configFile));
if (options.mangle && options.mangle.properties && options.mangle.properties.regex) {
options.mangle.properties.regex = UglifyJS.parse(options.mangle.properties.regex, {
expression: true
}).value;
} }
} }
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") { if (!output && options.sourceMap && options.sourceMap.url != "inline") fatal("cannot write source map to STDOUT");
fatal("cannot write source map to STDOUT"); if (specified["beautify"] && specified["output-opts"]) fatal("--beautify cannot be used with --output-opts");
} [ "compress", "mangle" ].forEach(function(name) {
[ if (!(name in options)) options[name] = false;
"compress",
"enclose",
"ie8",
"mangle",
"sourceMap",
"toplevel",
"wrap"
].forEach(function(name) {
if (name in program) {
options[name] = program[name];
}
}); });
if (program.verbose) { if (options.mangle && options.mangle.properties) {
options.warnings = "verbose"; if (options.mangle.properties.domprops) {
} else if (program.warn) { delete options.mangle.properties.domprops;
options.warnings = true; } else {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
if (!Array.isArray(options.mangle.properties.reserved)) options.mangle.properties.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS.push_uniq(options.mangle.properties.reserved, name);
});
}
}
if (output == "ast") options.output = {
ast: true,
code: false,
};
if (options.parse && (options.parse.acorn || options.parse.spidermonkey)
&& options.sourceMap && options.sourceMap.content == "inline") {
fatal("inline source map only works with built-in parser");
} }
if (options.warnings) { if (options.warnings) {
UglifyJS.AST_Node.log_function(print_error, options.warnings == "verbose"); UglifyJS.AST_Node.log_function(print_error, options.warnings == "verbose");
delete options.warnings; delete options.warnings;
} }
if (program.beautify) {
options.output = typeof program.beautify == "object" ? program.beautify : {};
if (!("beautify" in options.output)) {
options.output.beautify = true;
}
}
if (program.outputOpts) {
if (program.beautify) fatal("--beautify cannot be used with --output-opts");
options.output = typeof program.outputOpts == "object" ? program.outputOpts : {};
}
if (program.comments) {
if (typeof options.output != "object") options.output = {};
options.output.comments = typeof program.comments == "string" ? program.comments : "some";
}
if (program.define) {
if (typeof options.compress != "object") options.compress = {};
if (typeof options.compress.global_defs != "object") options.compress.global_defs = {};
for (var expr in program.define) {
options.compress.global_defs[expr] = program.define[expr];
}
}
if (program.keepFnames) {
options.keep_fnames = true;
}
if (program.mangleProps) {
if (program.mangleProps.domprops) {
delete program.mangleProps.domprops;
} else {
if (typeof program.mangleProps != "object") program.mangleProps = {};
if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS.push_uniq(program.mangleProps.reserved, name);
});
}
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
}
if (program.nameCache) {
options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
}
if (program.output == "ast") {
options.output = {
ast: true,
code: false
};
}
if (program.parse) {
if (!program.parse.acorn && !program.parse.spidermonkey) {
options.parse = program.parse;
} else if (program.sourceMap && program.sourceMap.content == "inline") {
fatal("inline source map only works with built-in parser");
}
}
if (~program.rawArgs.indexOf("--rename")) {
options.rename = true;
} else if (!program.rename) {
options.rename = false;
}
var convert_path = function(name) { var convert_path = function(name) {
return name; return name;
}; };
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) { if (typeof options.sourceMap == "object" && "base" in options.sourceMap) {
convert_path = function() { convert_path = function() {
var base = program.sourceMap.base; var base = options.sourceMap.base;
delete options.sourceMap.base; delete options.sourceMap.base;
return function(name) { return function(name) {
return path.relative(base, name); return path.relative(base, name);
}; };
}(); }();
} }
if (program.self) { if (specified["self"]) {
if (program.args.length) UglifyJS.AST_Node.warn("Ignoring input files since --self was passed"); if (paths.length) UglifyJS.AST_Node.warn("Ignoring input files since --self was passed");
if (!options.wrap) options.wrap = "UglifyJS"; if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) { paths = UglifyJS.FILES;
files[convert_path(name)] = read_file(name); }
}); if (paths.length) {
run(); simple_glob(paths).forEach(function(name) {
} else if (program.args.length) {
simple_glob(program.args).forEach(function(name) {
files[convert_path(name)] = read_file(name); files[convert_path(name)] = read_file(name);
}); });
run(); run();
@@ -188,15 +274,14 @@ function convert_ast(fn) {
} }
function run() { function run() {
var content = program.sourceMap && program.sourceMap.content; var content = options.sourceMap && options.sourceMap.content;
if (content && content != "inline") { if (content && content != "inline") {
UglifyJS.AST_Node.info("Using input source map: " + content); UglifyJS.AST_Node.info("Using input source map: " + content);
options.sourceMap.content = read_file(content, content); options.sourceMap.content = read_file(content, content);
} }
if (program.timings) options.timings = true;
try { try {
if (program.parse) { if (options.parse) {
if (program.parse.acorn) { if (options.parse.acorn) {
files = convert_ast(function(toplevel, name) { files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], { return require("acorn").parse(files[name], {
locations: true, locations: true,
@@ -204,7 +289,7 @@ function run() {
sourceFile: name sourceFile: name
}); });
}); });
} else if (program.parse.spidermonkey) { } else if (options.parse.spidermonkey) {
files = convert_ast(function(toplevel, name) { files = convert_ast(function(toplevel, name) {
var obj = JSON.parse(files[name]); var obj = JSON.parse(files[name]);
if (!toplevel) return obj; if (!toplevel) return obj;
@@ -216,14 +301,17 @@ function run() {
} catch (ex) { } catch (ex) {
fatal(ex); fatal(ex);
} }
if (program.reduceTest) { var result;
// load on demand - assumes dev tree checked out if (specified["reduce-test"]) {
// load on demand - assumes cloned repository
var reduce_test = require("../test/reduce"); var reduce_test = require("../test/reduce");
var testcase = files[0] || files[Object.keys(files)[0]]; if (Object.keys(files).length != 1) fatal("can only test on a single file");
var result = reduce_test(testcase, options, {verbose: true}); result = reduce_test(files[Object.keys(files)[0]], options, {
} log: print_error,
else { verbose: true,
var result = UglifyJS.minify(files, options); });
} else {
result = UglifyJS.minify(files, options);
} }
if (result.error) { if (result.error) {
var ex = result.error; var ex = result.error;
@@ -253,9 +341,18 @@ function run() {
print_error(format_object(ex.defs)); print_error(format_object(ex.defs));
} }
fatal(ex); fatal(ex);
} else if (program.output == "ast") { } else if (output == "ast") {
if (!options.compress && !options.mangle) { if (!options.compress && !options.mangle) {
result.ast.figure_out_scope({}); var toplevel = result.ast;
if (!(toplevel instanceof UglifyJS.AST_Toplevel)) {
if (!(toplevel instanceof UglifyJS.AST_Statement)) toplevel = new UglifyJS.AST_SimpleStatement({
body: toplevel,
});
toplevel = new UglifyJS.AST_Toplevel({
body: [ toplevel ],
});
}
toplevel.figure_out_scope({});
} }
print(JSON.stringify(result.ast, function(key, value) { print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) { if (value) switch (key) {
@@ -282,26 +379,22 @@ function run() {
} }
return value; return value;
}, 2)); }, 2));
} else if (program.output == "spidermonkey") { } else if (output == "spidermonkey") {
print(JSON.stringify(UglifyJS.minify(result.code, { print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false, compress: false,
mangle: false, mangle: false,
output: { output: {
ast: true, ast: true,
code: false code: false
} },
}).ast.to_mozilla_ast(), null, 2)); }).ast.to_mozilla_ast(), null, 2));
} else if (program.output) { } else if (output) {
fs.writeFileSync(program.output, result.code); fs.writeFileSync(output, result.code);
if (result.map) { if (result.map) fs.writeFileSync(output + ".map", result.map);
fs.writeFileSync(program.output + ".map", result.map);
}
} else { } else {
print(result.code); print(result.code);
} }
if (program.nameCache) { if (nameCache) fs.writeFileSync(nameCache, JSON.stringify(options.nameCache));
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
}
if (result.timings) for (var phase in result.timings) { if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s"); print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
} }
@@ -337,7 +430,7 @@ function simple_glob(glob) {
.replace(/\?/g, "[^/\\\\]") + "$"; .replace(/\?/g, "[^/\\\\]") + "$";
var mod = process.platform === "win32" ? "i" : ""; var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod); var rx = new RegExp(pattern, mod);
var results = entries.filter(function(name) { var results = entries.sort().filter(function(name) {
return rx.test(name); return rx.test(name);
}).map(function(name) { }).map(function(name) {
return path.join(dir, name); return path.join(dir, name);
@@ -357,47 +450,45 @@ function read_file(path, default_value) {
} }
} }
function parse_js(flag) { function parse_js(value, options, flag) {
return function(value, options) { if (!options || typeof options != "object") options = {};
options = options || {}; if (typeof value == "string") try {
try { UglifyJS.parse(value, {
UglifyJS.parse(value, { expression: true
expression: true }).walk(new UglifyJS.TreeWalker(function(node) {
}).walk(new UglifyJS.TreeWalker(function(node) { if (node instanceof UglifyJS.AST_Assign) {
if (node instanceof UglifyJS.AST_Assign) { var name = node.left.print_to_string();
var name = node.left.print_to_string(); var value = node.right;
var value = node.right; if (flag) {
if (flag) { options[name] = value;
options[name] = value; } else if (value instanceof UglifyJS.AST_Array) {
} else if (value instanceof UglifyJS.AST_Array) { options[name] = value.elements.map(to_string);
options[name] = value.elements.map(to_string); } else {
} else { options[name] = to_string(value);
options[name] = to_string(value);
}
return true;
} }
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) { return true;
var name = node.print_to_string();
options[name] = true;
return true;
}
if (!(node instanceof UglifyJS.AST_Sequence)) throw node;
function to_string(value) {
return value instanceof UglifyJS.AST_Constant ? value.value : value.print_to_string({
quote_keys: true
});
}
}));
} catch (ex) {
if (flag) {
fatal("cannot parse arguments for '" + flag + "': " + value);
} else {
options[value] = null;
} }
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) {
var name = node.print_to_string();
options[name] = true;
return true;
}
if (!(node instanceof UglifyJS.AST_Sequence)) throw node;
function to_string(value) {
return value instanceof UglifyJS.AST_Constant ? value.value : value.print_to_string({
quote_keys: true
});
}
}));
} catch (ex) {
if (flag) {
fatal("cannot parse arguments for '" + flag + "': " + value);
} else {
options[value] = null;
} }
return options;
} }
return options;
} }
function skip_key(key) { function skip_key(key) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -85,9 +85,11 @@ function minify(files, options) {
sourceMap: false, sourceMap: false,
timings: false, timings: false,
toplevel: false, toplevel: false,
validate: false,
warnings: false, warnings: false,
wrap: false, wrap: false,
}, true); }, true);
if (options.validate) AST_Node.enable_validation();
var timings = options.timings && { var timings = options.timings && {
start: Date.now() start: Date.now()
}; };
@@ -129,6 +131,7 @@ function minify(files, options) {
content: null, content: null,
filename: null, filename: null,
includeSources: false, includeSources: false,
names: true,
root: null, root: null,
url: null, url: null,
}, true); }, true);
@@ -138,7 +141,7 @@ function minify(files, options) {
warnings.push(warning); warnings.push(warning);
}, options.warnings == "verbose"); }, options.warnings == "verbose");
if (timings) timings.parse = Date.now(); if (timings) timings.parse = Date.now();
var source_maps, toplevel; var toplevel;
if (files instanceof AST_Toplevel) { if (files instanceof AST_Toplevel) {
toplevel = files; toplevel = files;
} else { } else {
@@ -151,19 +154,17 @@ function minify(files, options) {
if (typeof source_map_content == "string" && source_map_content != "inline") { if (typeof source_map_content == "string" && source_map_content != "inline") {
source_map_content = parse_source_map(source_map_content); source_map_content = parse_source_map(source_map_content);
} }
source_maps = source_map_content && Object.create(null); if (source_map_content) options.sourceMap.orig = Object.create(null);
for (var name in files) if (HOP(files, name)) { for (var name in files) if (HOP(files, name)) {
options.parse.filename = name; options.parse.filename = name;
options.parse.toplevel = toplevel = parse(files[name], options.parse); options.parse.toplevel = toplevel = parse(files[name], options.parse);
if (source_maps) { if (source_map_content == "inline") {
if (source_map_content == "inline") { var inlined_content = read_source_map(name, toplevel);
var inlined_content = read_source_map(name, toplevel); if (inlined_content) {
if (inlined_content) { options.sourceMap.orig[name] = parse_source_map(inlined_content);
source_maps[name] = parse_source_map(inlined_content);
}
} else {
source_maps[name] = source_map_content;
} }
} else if (source_map_content) {
options.sourceMap.orig[name] = source_map_content;
} }
} }
} }
@@ -177,13 +178,17 @@ function minify(files, options) {
toplevel = toplevel[action](option); toplevel = toplevel[action](option);
files[toplevel.start.file] = toplevel.print_to_string().replace(orig, ""); files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
}); });
if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now(); if (timings) timings.rename = Date.now();
if (options.rename) { if (options.rename) {
toplevel.figure_out_scope(options.mangle); toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle); toplevel.expand_names(options.mangle);
} }
if (timings) timings.compress = Date.now(); if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); if (options.compress) {
toplevel = new Compressor(options.compress).compress(toplevel);
if (options.validate) toplevel.validate_ast();
}
if (timings) timings.scope = Date.now(); if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle); if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now(); if (timings) timings.mangle = Date.now();
@@ -192,9 +197,7 @@ function minify(files, options) {
toplevel.mangle_names(options.mangle); toplevel.mangle_names(options.mangle);
} }
if (timings) timings.properties = Date.now(); if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) { if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now(); if (timings) timings.output = Date.now();
var result = {}; var result = {};
if (options.output.ast) { if (options.output.ast) {
@@ -202,19 +205,13 @@ function minify(files, options) {
} }
if (!HOP(options.output, "code") || options.output.code) { if (!HOP(options.output, "code") || options.output.code) {
if (options.sourceMap) { if (options.sourceMap) {
options.output.source_map = SourceMap({ options.output.source_map = SourceMap(options.sourceMap);
file: options.sourceMap.filename,
orig: source_maps,
root: options.sourceMap.root
});
if (options.sourceMap.includeSources) { if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) { if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable"); throw new Error("original source content unavailable");
} else for (var name in files) if (HOP(files, name)) { } else for (var name in files) if (HOP(files, name)) {
options.output.source_map.get().setSourceContent(name, files[name]); options.output.source_map.setSourceContent(name, files[name]);
} }
} else {
options.output.source_map.get()._sourcesContents = null;
} }
} }
delete options.output.ast; delete options.output.ast;
@@ -260,5 +257,7 @@ function minify(files, options) {
return result; return result;
} catch (ex) { } catch (ex) {
return { error: ex }; return { error: ex };
} finally {
AST_Node.disable_validation();
} }
} }

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -111,7 +111,7 @@
var args = { var args = {
start : my_start_token(key), start : my_start_token(key),
end : my_end_token(M.value), end : my_end_token(M.value),
key : key.type == "Identifier" ? key.name : key.value, key : "" + key[key.type == "Identifier" ? "name" : "value"],
value : from_moz(M.value) value : from_moz(M.value)
}; };
if (M.kind == "init") return new AST_ObjectKeyVal(args); if (M.kind == "init") return new AST_ObjectKeyVal(args);
@@ -212,7 +212,14 @@
end : my_end_token(M), end : my_end_token(M),
name : M.name name : M.name
}); });
} },
ThisExpression: function(M) {
return new AST_This({
start : my_start_token(M),
end : my_end_token(M),
name : "this",
});
},
}; };
MOZ_TO_ME.UpdateExpression = MOZ_TO_ME.UpdateExpression =
@@ -245,7 +252,6 @@
map("VariableDeclarator", AST_VarDef, "id>name, init>value"); map("VariableDeclarator", AST_VarDef, "id>name, init>value");
map("CatchClause", AST_Catch, "param>argname, body%body"); map("CatchClause", AST_Catch, "param>argname, body%body");
map("ThisExpression", AST_This);
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
@@ -407,6 +413,10 @@
}; };
}); });
def_to_moz(AST_This, function To_Moz_ThisExpression() {
return { type: "ThisExpression" };
});
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var flags = M.value.toString().match(/[gimuy]*$/)[0]; var flags = M.value.toString().match(/[gimuy]*$/)[0];
var value = "/" + M.value.raw_source + "/" + flags; var value = "/" + M.value.raw_source + "/" + flags;

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -100,7 +100,7 @@ function OutputStream(options) {
} }
} }
var indentation = 0; var indentation = options.indent_start;
var current_col = 0; var current_col = 0;
var current_line = 1; var current_line = 1;
var current_pos = 0; var current_pos = 0;
@@ -191,10 +191,6 @@ function OutputStream(options) {
return name; return name;
} }
function make_indent(back) {
return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
}
/* -----[ beautification/minification ]----- */ /* -----[ beautification/minification ]----- */
var has_parens = false; var has_parens = false;
@@ -305,6 +301,7 @@ function OutputStream(options) {
|| (ch == "/" && ch == prev) || (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last) || ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!" || str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") { || last == "--" && ch == ">") {
OUTPUT += " "; OUTPUT += " ";
current_col++; current_col++;
@@ -344,9 +341,7 @@ function OutputStream(options) {
}; };
var indent = options.beautify ? function(half) { var indent = options.beautify ? function(half) {
if (options.beautify) { print(repeat_string(" ", half ? indentation - (options.indent_level >> 1) : indentation));
print(make_indent(half ? 0.5 : 0));
}
} : noop; } : noop;
var with_indent = options.beautify ? function(col, cont) { var with_indent = options.beautify ? function(col, cont) {
@@ -574,9 +569,9 @@ function OutputStream(options) {
get : get, get : get,
toString : get, toString : get,
indent : indent, indent : indent,
indentation : function() { return indentation }, should_break : readonly ? noop : function() {
current_width : function() { return current_col - indentation }, return options.width && current_col - indentation >= options.width;
should_break : function() { return options.width && this.current_width() >= options.width }, },
has_parens : function() { return has_parens }, has_parens : function() { return has_parens },
newline : newline, newline : newline,
print : print, print : print,
@@ -629,13 +624,7 @@ function OutputStream(options) {
var use_asm = false; var use_asm = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens) { AST_Node.DEFMETHOD("print", function(stream, force_parens) {
var self = this, generator = self._codegen; var self = this;
function doit() {
stream.prepend_comments(self);
self.add_source_map(stream);
generator(self, stream);
stream.append_comments(self);
}
stream.push_node(self); stream.push_node(self);
if (force_parens || self.needs_parens(stream)) { if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit); stream.with_parens(doit);
@@ -643,9 +632,14 @@ function OutputStream(options) {
doit(); doit();
} }
stream.pop_node(); stream.pop_node();
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
function doit() {
stream.prepend_comments(self);
self.add_source_map(stream);
self._codegen(stream);
stream.append_comments(self);
}
});
AST_Node.DEFMETHOD("print_to_string", function(options) { AST_Node.DEFMETHOD("print_to_string", function(options) {
var s = OutputStream(options); var s = OutputStream(options);
this.print(s); this.print(s);
@@ -688,78 +682,66 @@ function OutputStream(options) {
PARENS(AST_Unary, function(output) { PARENS(AST_Unary, function(output) {
var p = output.parent(); var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this;
|| p instanceof AST_Call && p.expression === this;
}); });
PARENS(AST_Sequence, function(output) { PARENS(AST_Sequence, function(output) {
var p = output.parent(); var p = output.parent();
// (foo, bar)() or foo(1, (2, 3), 4) // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
return p instanceof AST_Call return p instanceof AST_Array
// !(foo, bar, baz)
|| p instanceof AST_Unary
// 1 + (2, 3) + 4 ==> 8 // 1 + (2, 3) + 4 ==> 8
|| p instanceof AST_Binary || p instanceof AST_Binary
// var a = (1, 2), b = a + a; ==> b == 4 // new (foo, bar) or foo(1, (2, 3), 4)
|| p instanceof AST_VarDef || p instanceof AST_Call
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_PropAccess && p.expression === this
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|| p instanceof AST_Array
// { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_ObjectProperty
// (false, true) ? (a = 10, b = 20) : (c = 30) // (false, true) ? (a = 10, b = 20) : (c = 30)
// ==> 20 (side effect, set a := 10 and b := 20) // ==> 20 (side effect, set a := 10 and b := 20)
|| p instanceof AST_Conditional; || p instanceof AST_Conditional
// { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_ObjectProperty
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_PropAccess && p.expression === this
// !(foo, bar, baz)
|| p instanceof AST_Unary
// var a = (1, 2), b = a + a; ==> b == 4
|| p instanceof AST_VarDef;
}); });
PARENS(AST_Binary, function(output) { PARENS(AST_Binary, function(output) {
var p = output.parent(); var p = output.parent();
// (foo && bar)()
if (p instanceof AST_Call && p.expression === this)
return true;
// typeof (foo && bar)
if (p instanceof AST_Unary)
return true;
// (foo && bar)["prop"], (foo && bar).prop
if (p instanceof AST_PropAccess && p.expression === this)
return true;
// this deals with precedence: 3 * (2 + 1) // this deals with precedence: 3 * (2 + 1)
if (p instanceof AST_Binary) { if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po]; var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so]; var so = this.operator, sp = PRECEDENCE[so];
if (pp > sp return pp > sp || (pp == sp && this === p.right);
|| (pp == sp
&& this === p.right)) {
return true;
}
} }
// (foo && bar)()
if (p instanceof AST_Call) return p.expression === this;
// (foo && bar)["prop"], (foo && bar).prop
if (p instanceof AST_PropAccess) return p.expression === this;
// typeof (foo && bar)
if (p instanceof AST_Unary) return true;
}); });
PARENS(AST_PropAccess, function(output) { PARENS(AST_PropAccess, function(output) {
var node = this;
var p = output.parent(); var p = output.parent();
if (p instanceof AST_New && p.expression === this) { if (p instanceof AST_New && p.expression === node) {
// i.e. new (foo.bar().baz) // i.e. new (foo().bar)
// //
// if there's one call into this subtree, then we need // if there's one call into this subtree, then we need
// parens around it too, otherwise the call will be // parens around it too, otherwise the call will be
// interpreted as passing the arguments to the upper New // interpreted as passing the arguments to the upper New
// expression. // expression.
var parens = false; do {
this.walk(new TreeWalker(function(node) { node = node.expression;
if (parens || node instanceof AST_Scope) return true; } while (node instanceof AST_PropAccess);
if (node instanceof AST_Call) { return node.TYPE == "Call";
parens = true;
return true;
}
}));
return parens;
} }
}); });
PARENS(AST_Call, function(output) { PARENS(AST_Call, function(output) {
var p = output.parent(); var p = output.parent();
if (p instanceof AST_New && p.expression === this) return true; if (p instanceof AST_New) return p.expression === this;
// https://bugs.webkit.org/show_bug.cgi?id=123506 // https://bugs.webkit.org/show_bug.cgi?id=123506
if (output.option('webkit')) { if (output.option('webkit')) {
var g = output.parent(1); var g = output.parent(1);
@@ -772,49 +754,43 @@ function OutputStream(options) {
}); });
PARENS(AST_New, function(output) { PARENS(AST_New, function(output) {
if (need_constructor_parens(this, output)) return false;
var p = output.parent(); var p = output.parent();
if (!need_constructor_parens(this, output) // (new foo)(bar)
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() if (p instanceof AST_Call) return p.expression === this;
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar) // (new Date).getTime(), (new Date)["getTime"]()
return true; return p instanceof AST_PropAccess;
}); });
PARENS(AST_Number, function(output) { PARENS(AST_Number, function(output) {
var p = output.parent(); var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) { if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.value; var value = this.value;
// https://github.com/mishoo/UglifyJS2/issues/115 // https://github.com/mishoo/UglifyJS/issues/115
// https://github.com/mishoo/UglifyJS2/pull/1009 // https://github.com/mishoo/UglifyJS/pull/1009
if (value < 0 || /^0/.test(make_num(value))) { return value < 0 || /^0/.test(make_num(value));
return true;
}
} }
}); });
PARENS([ AST_Assign, AST_Conditional ], function(output) { PARENS([ AST_Assign, AST_Conditional ], function(output) {
var p = output.parent(); var p = output.parent();
// !(a = false) → true
if (p instanceof AST_Unary)
return true;
// 1 + (a = 2) + 3 → 6, side effect setting a = 2 // 1 + (a = 2) + 3 → 6, side effect setting a = 2
if (p instanceof AST_Binary && !(p instanceof AST_Assign)) if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
return true;
// (a = func)() —or— new (a = Object)() // (a = func)() —or— new (a = Object)()
if (p instanceof AST_Call && p.expression === this) if (p instanceof AST_Call) return p.expression === this;
return true;
// (a = foo) ? bar : baz // (a = foo) ? bar : baz
if (p instanceof AST_Conditional && p.condition === this) if (p instanceof AST_Conditional) return p.condition === this;
return true;
// (a = foo)["prop"] —or— (a = foo).prop // (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess && p.expression === this) if (p instanceof AST_PropAccess) return p.expression === this;
return true; // !(a = false) → true
if (p instanceof AST_Unary) return true;
}); });
/* -----[ PRINTERS ]----- */ /* -----[ PRINTERS ]----- */
DEFPRINT(AST_Directive, function(self, output) { DEFPRINT(AST_Directive, function(output) {
var quote = self.quote; var quote = this.quote;
var value = self.value; var value = this.value;
switch (output.option("quote_style")) { switch (output.option("quote_style")) {
case 0: case 0:
case 2: case 2:
@@ -827,7 +803,7 @@ function OutputStream(options) {
output.print(quote + value + quote); output.print(quote + value + quote);
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_Debugger, function(self, output) { DEFPRINT(AST_Debugger, function(output) {
output.print("debugger"); output.print("debugger");
output.semicolon(); output.semicolon();
}); });
@@ -863,21 +839,21 @@ function OutputStream(options) {
force_statement(this.body, output); force_statement(this.body, output);
}); });
DEFPRINT(AST_Statement, function(self, output) { DEFPRINT(AST_Statement, function(output) {
self.body.print(output); this.body.print(output);
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_Toplevel, function(self, output) { DEFPRINT(AST_Toplevel, function(output) {
display_body(self.body, true, output, true); display_body(this.body, true, output, true);
output.print(""); output.print("");
}); });
DEFPRINT(AST_LabeledStatement, function(self, output) { DEFPRINT(AST_LabeledStatement, function(output) {
self.label.print(output); this.label.print(output);
output.colon(); output.colon();
self.body.print(output); this.body.print(output);
}); });
DEFPRINT(AST_SimpleStatement, function(self, output) { DEFPRINT(AST_SimpleStatement, function(output) {
self.body.print(output); this.body.print(output);
output.semicolon(); output.semicolon();
}); });
function print_braced_empty(self, output) { function print_braced_empty(self, output) {
@@ -894,13 +870,14 @@ function OutputStream(options) {
}); });
} else print_braced_empty(self, output); } else print_braced_empty(self, output);
} }
DEFPRINT(AST_BlockStatement, function(self, output) { DEFPRINT(AST_BlockStatement, function(output) {
print_braced(self, output); print_braced(this, output);
}); });
DEFPRINT(AST_EmptyStatement, function(self, output) { DEFPRINT(AST_EmptyStatement, function(output) {
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_Do, function(self, output) { DEFPRINT(AST_Do, function(output) {
var self = this;
output.print("do"); output.print("do");
output.space(); output.space();
make_block(self.body, output); make_block(self.body, output);
@@ -912,7 +889,8 @@ function OutputStream(options) {
}); });
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_While, function(self, output) { DEFPRINT(AST_While, function(output) {
var self = this;
output.print("while"); output.print("while");
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
@@ -921,7 +899,8 @@ function OutputStream(options) {
output.space(); output.space();
self._do_print_body(output); self._do_print_body(output);
}); });
DEFPRINT(AST_For, function(self, output) { DEFPRINT(AST_For, function(output) {
var self = this;
output.print("for"); output.print("for");
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
@@ -950,7 +929,8 @@ function OutputStream(options) {
output.space(); output.space();
self._do_print_body(output); self._do_print_body(output);
}); });
DEFPRINT(AST_ForIn, function(self, output) { DEFPRINT(AST_ForIn, function(output) {
var self = this;
output.print("for"); output.print("for");
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
@@ -963,7 +943,8 @@ function OutputStream(options) {
output.space(); output.space();
self._do_print_body(output); self._do_print_body(output);
}); });
DEFPRINT(AST_With, function(self, output) { DEFPRINT(AST_With, function(output) {
var self = this;
output.print("with"); output.print("with");
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
@@ -974,7 +955,7 @@ function OutputStream(options) {
}); });
/* -----[ functions ]----- */ /* -----[ functions ]----- */
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) { DEFPRINT(AST_Lambda, function(output, nokeyword) {
var self = this; var self = this;
if (!nokeyword) { if (!nokeyword) {
output.print("function"); output.print("function");
@@ -992,32 +973,23 @@ function OutputStream(options) {
output.space(); output.space();
print_braced(self, output, true); print_braced(self, output, true);
}); });
DEFPRINT(AST_Lambda, function(self, output) {
self._do_print(output);
});
/* -----[ jumps ]----- */ /* -----[ jumps ]----- */
function print_jump(output, kind, target) { function print_jump(kind, prop) {
output.print(kind); return function(output) {
if (target) { output.print(kind);
output.space(); var target = this[prop];
target.print(output); if (target) {
} output.space();
output.semicolon(); target.print(output);
}
output.semicolon();
};
} }
DEFPRINT(AST_Return, print_jump("return", "value"));
DEFPRINT(AST_Return, function(self, output) { DEFPRINT(AST_Throw, print_jump("throw", "value"));
print_jump(output, "return", self.value); DEFPRINT(AST_Break, print_jump("break", "label"));
}); DEFPRINT(AST_Continue, print_jump("continue", "label"));
DEFPRINT(AST_Throw, function(self, output) {
print_jump(output, "throw", self.value);
});
DEFPRINT(AST_Break, function(self, output) {
print_jump(output, "break", self.label);
});
DEFPRINT(AST_Continue, function(self, output) {
print_jump(output, "continue", self.label);
});
/* -----[ if ]----- */ /* -----[ if ]----- */
function make_then(self, output) { function make_then(self, output) {
@@ -1046,7 +1018,8 @@ function OutputStream(options) {
} }
force_statement(self.body, output); force_statement(self.body, output);
} }
DEFPRINT(AST_If, function(self, output) { DEFPRINT(AST_If, function(output) {
var self = this;
output.print("if"); output.print("if");
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
@@ -1068,7 +1041,8 @@ function OutputStream(options) {
}); });
/* -----[ switch ]----- */ /* -----[ switch ]----- */
DEFPRINT(AST_Switch, function(self, output) { DEFPRINT(AST_Switch, function(output) {
var self = this;
output.print("switch"); output.print("switch");
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
@@ -1086,28 +1060,30 @@ function OutputStream(options) {
}); });
}); });
}); });
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) { function print_branch_body(self, output) {
output.newline(); output.newline();
this.body.forEach(function(stmt) { self.body.forEach(function(stmt) {
output.indent(); output.indent();
stmt.print(output); stmt.print(output);
output.newline(); output.newline();
}); });
}); }
DEFPRINT(AST_Default, function(self, output) { DEFPRINT(AST_Default, function(output) {
output.print("default:"); output.print("default:");
self._do_print_body(output); print_branch_body(this, output);
}); });
DEFPRINT(AST_Case, function(self, output) { DEFPRINT(AST_Case, function(output) {
var self = this;
output.print("case"); output.print("case");
output.space(); output.space();
self.expression.print(output); self.expression.print(output);
output.print(":"); output.print(":");
self._do_print_body(output); print_branch_body(self, output);
}); });
/* -----[ exceptions ]----- */ /* -----[ exceptions ]----- */
DEFPRINT(AST_Try, function(self, output) { DEFPRINT(AST_Try, function(output) {
var self = this;
output.print("try"); output.print("try");
output.space(); output.space();
print_braced(self, output); print_braced(self, output);
@@ -1120,22 +1096,26 @@ function OutputStream(options) {
self.bfinally.print(output); self.bfinally.print(output);
} }
}); });
DEFPRINT(AST_Catch, function(self, output) { DEFPRINT(AST_Catch, function(output) {
var self = this;
output.print("catch"); output.print("catch");
output.space(); if (self.argname) {
output.with_parens(function() { output.space();
self.argname.print(output); output.with_parens(function() {
}); self.argname.print(output);
});
}
output.space(); output.space();
print_braced(self, output); print_braced(self, output);
}); });
DEFPRINT(AST_Finally, function(self, output) { DEFPRINT(AST_Finally, function(output) {
output.print("finally"); output.print("finally");
output.space(); output.space();
print_braced(self, output); print_braced(this, output);
}); });
DEFPRINT(AST_Var, function(self, output) { DEFPRINT(AST_Var, function(output) {
var self = this;
output.print("var"); output.print("var");
output.space(); output.space();
self.definitions.forEach(function(def, i) { self.definitions.forEach(function(def, i) {
@@ -1149,7 +1129,7 @@ function OutputStream(options) {
function parenthesize_for_noin(node, output, noin) { function parenthesize_for_noin(node, output, noin) {
var parens = false; var parens = false;
// need to take some precautions here: // need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60 // https://github.com/mishoo/UglifyJS/issues/60
if (noin) node.walk(new TreeWalker(function(node) { if (noin) node.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true; if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") { if (node instanceof AST_Binary && node.operator == "in") {
@@ -1160,7 +1140,8 @@ function OutputStream(options) {
node.print(output, parens); node.print(output, parens);
} }
DEFPRINT(AST_VarDef, function(self, output) { DEFPRINT(AST_VarDef, function(output) {
var self = this;
self.name.print(output); self.name.print(output);
if (self.value) { if (self.value) {
output.space(); output.space();
@@ -1173,10 +1154,7 @@ function OutputStream(options) {
}); });
/* -----[ other expressions ]----- */ /* -----[ other expressions ]----- */
DEFPRINT(AST_Call, function(self, output) { function print_call_args(self, output) {
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) { if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start); output.add_mapping(self.start);
} }
@@ -1186,14 +1164,20 @@ function OutputStream(options) {
expr.print(output); expr.print(output);
}); });
}); });
}
DEFPRINT(AST_Call, function(output) {
this.expression.print(output);
print_call_args(this, output);
}); });
DEFPRINT(AST_New, function(self, output) { DEFPRINT(AST_New, function(output) {
var self = this;
output.print("new"); output.print("new");
output.space(); output.space();
AST_Call.prototype._codegen(self, output); self.expression.print(output);
if (need_constructor_parens(self, output)) print_call_args(self, output);
}); });
DEFPRINT(AST_Sequence, function(self, output) { DEFPRINT(AST_Sequence, function(output) {
self.expressions.forEach(function(node, index) { this.expressions.forEach(function(node, index) {
if (index > 0) { if (index > 0) {
output.comma(); output.comma();
if (output.should_break()) { if (output.should_break()) {
@@ -1204,7 +1188,8 @@ function OutputStream(options) {
node.print(output); node.print(output);
}); });
}); });
DEFPRINT(AST_Dot, function(self, output) { DEFPRINT(AST_Dot, function(output) {
var self = this;
var expr = self.expression; var expr = self.expression;
expr.print(output); expr.print(output);
var prop = self.property; var prop = self.property;
@@ -1225,35 +1210,38 @@ function OutputStream(options) {
output.print_name(prop); output.print_name(prop);
} }
}); });
DEFPRINT(AST_Sub, function(self, output) { DEFPRINT(AST_Sub, function(output) {
self.expression.print(output); this.expression.print(output);
output.print("["); output.print("[");
self.property.print(output); this.property.print(output);
output.print("]"); output.print("]");
}); });
DEFPRINT(AST_UnaryPrefix, function(self, output) { DEFPRINT(AST_UnaryPrefix, function(output) {
var op = self.operator; var op = this.operator;
var exp = this.expression;
output.print(op); output.print(op);
if (/^[a-z]/i.test(op) if (/^[a-z]/i.test(op)
|| (/[+-]$/.test(op) || (/[+-]$/.test(op)
&& self.expression instanceof AST_UnaryPrefix && exp instanceof AST_UnaryPrefix
&& /^[+-]/.test(self.expression.operator))) { && /^[+-]/.test(exp.operator))) {
output.space(); output.space();
} }
self.expression.print(output); exp.print(output);
}); });
DEFPRINT(AST_UnaryPostfix, function(self, output) { DEFPRINT(AST_UnaryPostfix, function(output) {
self.expression.print(output); this.expression.print(output);
output.print(self.operator); output.print(this.operator);
}); });
DEFPRINT(AST_Binary, function(self, output) { DEFPRINT(AST_Binary, function(output) {
var self = this;
self.left.print(output); self.left.print(output);
output.space(); output.space();
output.print(self.operator); output.print(self.operator);
output.space(); output.space();
self.right.print(output); self.right.print(output);
}); });
DEFPRINT(AST_Conditional, function(self, output) { DEFPRINT(AST_Conditional, function(output) {
var self = this;
self.condition.print(output); self.condition.print(output);
output.space(); output.space();
output.print("?"); output.print("?");
@@ -1265,10 +1253,10 @@ function OutputStream(options) {
}); });
/* -----[ literals ]----- */ /* -----[ literals ]----- */
DEFPRINT(AST_Array, function(self, output) { DEFPRINT(AST_Array, function(output) {
output.with_square(function() { var a = this.elements, len = a.length;
var a = self.elements, len = a.length; output.with_square(len > 0 ? function() {
if (len > 0) output.space(); output.space();
a.forEach(function(exp, i) { a.forEach(function(exp, i) {
if (i) output.comma(); if (i) output.comma();
exp.print(output); exp.print(output);
@@ -1278,12 +1266,13 @@ function OutputStream(options) {
if (i === len - 1 && exp instanceof AST_Hole) if (i === len - 1 && exp instanceof AST_Hole)
output.comma(); output.comma();
}); });
if (len > 0) output.space(); output.space();
}); } : noop);
}); });
DEFPRINT(AST_Object, function(self, output) { DEFPRINT(AST_Object, function(output) {
if (self.properties.length > 0) output.with_block(function() { var props = this.properties;
self.properties.forEach(function(prop, i) { if (props.length > 0) output.with_block(function() {
props.forEach(function(prop, i) {
if (i) { if (i) {
output.print(","); output.print(",");
output.newline(); output.newline();
@@ -1293,7 +1282,7 @@ function OutputStream(options) {
}); });
output.newline(); output.newline();
}); });
else print_braced_empty(self, output); else print_braced_empty(this, output);
}); });
function print_property_name(key, quote, output) { function print_property_name(key, quote, output) {
@@ -1312,47 +1301,48 @@ function OutputStream(options) {
} }
} }
DEFPRINT(AST_ObjectKeyVal, function(self, output) { DEFPRINT(AST_ObjectKeyVal, function(output) {
var self = this;
print_property_name(self.key, self.quote, output); print_property_name(self.key, self.quote, output);
output.colon(); output.colon();
self.value.print(output); self.value.print(output);
}); });
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) { function print_accessor(type) {
output.print(type); return function(output) {
output.space(); var self = this;
print_property_name(this.key.name, this.quote, output); output.print(type);
this.value._do_print(output, true); output.space();
}); print_property_name(self.key.name, self.quote, output);
DEFPRINT(AST_ObjectSetter, function(self, output) { self.value._codegen(output, true);
self._print_getter_setter("set", output); };
}); }
DEFPRINT(AST_ObjectGetter, function(self, output) { DEFPRINT(AST_ObjectGetter, print_accessor("get"));
self._print_getter_setter("get", output); DEFPRINT(AST_ObjectSetter, print_accessor("set"));
}); DEFPRINT(AST_Symbol, function(output) {
DEFPRINT(AST_Symbol, function(self, output) { var def = this.definition();
var def = self.definition(); output.print_name(def && def.mangled_name || this.name);
output.print_name(def && def.mangled_name || self.name);
}); });
DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output) { DEFPRINT(AST_This, function(output) {
output.print("this"); output.print("this");
}); });
DEFPRINT(AST_Constant, function(self, output) { DEFPRINT(AST_Constant, function(output) {
output.print(self.value); output.print(this.value);
}); });
DEFPRINT(AST_String, function(self, output) { DEFPRINT(AST_String, function(output) {
output.print_string(self.value, self.quote); output.print_string(this.value, this.quote);
}); });
DEFPRINT(AST_Number, function(self, output) { DEFPRINT(AST_Number, function(output) {
if (use_asm && self.start && self.start.raw != null) { var start = this.start;
output.print(self.start.raw); if (use_asm && start && start.raw != null) {
output.print(start.raw);
} else { } else {
output.print(make_num(self.value)); output.print(make_num(this.value));
} }
}); });
DEFPRINT(AST_RegExp, function(self, output) { DEFPRINT(AST_RegExp, function(output) {
var regexp = self.value; var regexp = this.value;
var str = regexp.toString(); var str = regexp.toString();
var end = str.lastIndexOf("/"); var end = str.lastIndexOf("/");
if (regexp.raw_source) { if (regexp.raw_source) {
@@ -1386,7 +1376,7 @@ function OutputStream(options) {
} }
})); }));
var p = output.parent(); var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === this)
output.print(" "); output.print(" ");
}); });
@@ -1470,7 +1460,6 @@ function OutputStream(options) {
AST_Node, AST_Node,
// since the label symbol will mark it // since the label symbol will mark it
AST_LabeledStatement, AST_LabeledStatement,
AST_Toplevel,
], noop); ], noop);
// XXX: I'm not exactly sure if we need it for all of these nodes, // XXX: I'm not exactly sure if we need it for all of these nodes,
@@ -1482,7 +1471,6 @@ function OutputStream(options) {
AST_Constant, AST_Constant,
AST_Debugger, AST_Debugger,
AST_Definitions, AST_Definitions,
AST_Directive,
AST_Finally, AST_Finally,
AST_Jump, AST_Jump,
AST_Lambda, AST_Lambda,

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -44,11 +44,14 @@
"use strict"; "use strict";
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with'; var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with";
var KEYWORDS_ATOM = 'false null true'; var KEYWORDS_ATOM = "false null true";
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield' var RESERVED_WORDS = [
+ " " + KEYWORDS_ATOM + " " + KEYWORDS; "abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield",
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; KEYWORDS_ATOM,
KEYWORDS,
].join(" ");
var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case";
KEYWORDS = makePredicate(KEYWORDS); KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS); RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -57,8 +60,9 @@ KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; var RE_BIN_NUMBER = /^0b([01]+)$/i;
var RE_OCT_NUMBER = /^0[0-7]+$/; var RE_HEX_NUMBER = /^0x([0-9a-f]+)$/i;
var RE_OCT_NUMBER = /^0o?([0-7]+)$/i;
var OPERATORS = makePredicate([ var OPERATORS = makePredicate([
"in", "in",
@@ -144,10 +148,6 @@ function is_digit(code) {
return code >= 48 && code <= 57; return code >= 48 && code <= 57;
} }
function is_alphanumeric_char(code) {
return is_digit(code) || is_letter(code);
}
function is_unicode_digit(code) { function is_unicode_digit(code) {
return UNICODE.digit.test(String.fromCharCode(code)); return UNICODE.digit.test(String.fromCharCode(code));
} }
@@ -181,14 +181,12 @@ function is_identifier_string(str) {
} }
function parse_js_number(num) { function parse_js_number(num) {
if (RE_HEX_NUMBER.test(num)) { var match;
return parseInt(num.substr(2), 16); if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
} else if (RE_OCT_NUMBER.test(num)) { if (match = RE_HEX_NUMBER.exec(num)) return parseInt(match[1], 16);
return parseInt(num.substr(1), 8); if (match = RE_OCT_NUMBER.exec(num)) return parseInt(match[1], 8);
} else { var val = parseFloat(num);
var val = parseFloat(num); if (val == num) return val;
if (val == num) return val;
}
} }
function JS_Parse_Error(message, filename, line, col, pos) { function JS_Parse_Error(message, filename, line, col, pos) {
@@ -241,16 +239,16 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if (signal_eof && !ch) if (signal_eof && !ch)
throw EX_EOF; throw EX_EOF;
if (NEWLINE_CHARS[ch]) { if (NEWLINE_CHARS[ch]) {
S.newline_before = S.newline_before || !in_string;
++S.line;
S.col = 0; S.col = 0;
if (!in_string && ch == "\r" && peek() == "\n") { S.line++;
// treat a \r\n sequence as a single \n if (!in_string) S.newline_before = true;
++S.pos; if (ch == "\r" && peek() == "\n") {
// treat `\r\n` as `\n`
S.pos++;
ch = "\n"; ch = "\n";
} }
} else { } else {
++S.col; S.col++;
} }
return ch; return ch;
} }
@@ -344,11 +342,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
case (after_e = false, 46): // . case (after_e = false, 46): // .
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
} }
return is_alphanumeric_char(code); return is_digit(code) || is_letter(code) || ch == "_";
}); });
if (prefix) num = prefix + num; if (prefix) num = prefix + num;
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { if (/^0[0-7_]+$/.test(num)) {
parse_error("Legacy octal literals are not allowed in strict mode"); if (next_token.has_directive("use strict")) parse_error("Legacy octal literals are not allowed in strict mode");
} else {
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
} }
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) return token("num", valid); if (!isNaN(valid)) return token("num", valid);
@@ -438,7 +438,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() { var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() {
var regex_allowed = S.regex_allowed; var regex_allowed = S.regex_allowed;
var i = find("*/", true); var i = find("*/", true);
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n'); var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, "\n");
// update stream position // update stream position
forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2); forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
S.comments_before.push(token("comment2", text, true)); S.comments_before.push(token("comment2", text, true));
@@ -948,7 +948,7 @@ function parse($TEXT, options) {
if (!(stat instanceof AST_IterationStatement)) { if (!(stat instanceof AST_IterationStatement)) {
// check for `continue` that refers to this label. // check for `continue` that refers to this label.
// those should be reported as syntax errors. // those should be reported as syntax errors.
// https://github.com/mishoo/UglifyJS2/issues/287 // https://github.com/mishoo/UglifyJS/issues/287
label.references.forEach(function(ref) { label.references.forEach(function(ref) {
if (ref instanceof AST_Continue) { if (ref instanceof AST_Continue) {
ref = ref.label.start; ref = ref.label.start;
@@ -1092,7 +1092,7 @@ function parse($TEXT, options) {
function switch_body_() { function switch_body_() {
expect("{"); expect("{");
var a = [], cur = null, branch = null, tmp; var a = [], branch, cur, default_branch, tmp;
while (!is("punc", "}")) { while (!is("punc", "}")) {
if (is("eof")) expect_token("punc", "}"); if (is("eof")) expect_token("punc", "}");
if (is("keyword", "case")) { if (is("keyword", "case")) {
@@ -1107,12 +1107,14 @@ function parse($TEXT, options) {
expect(":"); expect(":");
} else if (is("keyword", "default")) { } else if (is("keyword", "default")) {
if (branch) branch.end = prev(); if (branch) branch.end = prev();
if (default_branch) croak("More than one default clause in switch statement");
cur = []; cur = [];
branch = new AST_Default({ branch = new AST_Default({
start : (tmp = S.token, next(), expect(":"), tmp), start : (tmp = S.token, next(), expect(":"), tmp),
body : cur body : cur
}); });
a.push(branch); a.push(branch);
default_branch = branch;
} else { } else {
if (!cur) unexpected(); if (!cur) unexpected();
cur.push(statement()); cur.push(statement());
@@ -1128,9 +1130,12 @@ function parse($TEXT, options) {
if (is("keyword", "catch")) { if (is("keyword", "catch")) {
var start = S.token; var start = S.token;
next(); next();
expect("("); var name = null;
var name = as_symbol(AST_SymbolCatch); if (is("punc", "(")) {
expect(")"); next();
name = as_symbol(AST_SymbolCatch);
expect(")");
}
bcatch = new AST_Catch({ bcatch = new AST_Catch({
start : start, start : start,
argname : name, argname : name,

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -43,7 +43,8 @@
"use strict"; "use strict";
function find_builtins(reserved) { var builtins = function() {
var names = [];
// NaN will be included due to Number.NaN // NaN will be included due to Number.NaN
[ [
"null", "null",
@@ -67,19 +68,21 @@ function find_builtins(reserved) {
].forEach(function(ctor) { ].forEach(function(ctor) {
Object.getOwnPropertyNames(ctor).map(add); Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) { if (ctor.prototype) {
Object.getOwnPropertyNames(new ctor()).map(add);
Object.getOwnPropertyNames(ctor.prototype).map(add); Object.getOwnPropertyNames(ctor.prototype).map(add);
} }
}); });
return makePredicate(names);
function add(name) { function add(name) {
push_uniq(reserved, name); names.push(name);
} }
} }();
function reserve_quoted_keys(ast, reserved) { function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) { if (node instanceof AST_ObjectKeyVal) {
add(node.key); if (node.quote) add(node.key);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
addStrings(node.property, add); addStrings(node.property, add);
} }
@@ -91,17 +94,14 @@ function reserve_quoted_keys(ast, reserved) {
} }
function addStrings(node, add) { function addStrings(node, add) {
node.walk(new TreeWalker(function(node) { if (node instanceof AST_Conditional) {
if (node instanceof AST_Sequence) { addStrings(node.consequent, add);
addStrings(node.tail_node(), add); addStrings(node.alternative, add);
} else if (node instanceof AST_String) { } else if (node instanceof AST_Sequence) {
add(node.value); addStrings(node.tail_node(), add);
} else if (node instanceof AST_Conditional) { } else if (node instanceof AST_String) {
addStrings(node.consequent, add); add(node.value);
addStrings(node.alternative, add); }
}
return true;
}));
} }
function mangle_properties(ast, options) { function mangle_properties(ast, options) {
@@ -110,21 +110,21 @@ function mangle_properties(ast, options) {
cache: null, cache: null,
debug: false, debug: false,
keep_quoted: false, keep_quoted: false,
only_cache: false,
regex: null, regex: null,
reserved: null, reserved: null,
}, true); }, true);
var reserved = options.reserved; var reserved = Object.create(options.builtins ? null : builtins);
if (!Array.isArray(reserved)) reserved = []; if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
if (!options.builtins) find_builtins(reserved); reserved[name] = true;
});
var cname = -1; var cname = -1;
var cache; var cache;
if (options.cache) { if (options.cache) {
cache = options.cache.props; cache = options.cache.props;
cache.each(function(mangled_name) { cache.each(function(name) {
push_uniq(reserved, mangled_name); reserved[name] = true;
}); });
} else { } else {
cache = new Dictionary(); cache = new Dictionary();
@@ -139,62 +139,92 @@ function mangle_properties(ast, options) {
var debug_suffix; var debug_suffix;
if (debug) debug_suffix = options.debug === true ? "" : options.debug; if (debug) debug_suffix = options.debug === true ? "" : options.debug;
var names_to_mangle = []; var names_to_mangle = Object.create(null);
var unmangleable = []; var unmangleable = Object.create(reserved);
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_Binary) {
if (node.operator == "in") addStrings(node.left, add);
} else if (node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_Dot) switch (exp.property) {
case "defineProperty":
case "getOwnPropertyDescriptor":
if (node.args.length < 2) break;
exp = exp.expression;
if (!(exp instanceof AST_SymbolRef)) break;
if (exp.name != "Object") break;
if (!exp.definition().undeclared) break;
addStrings(node.args[1], add);
break;
case "hasOwnProperty":
if (node.args.length < 1) break;
addStrings(node.args[0], add);
break;
}
} else if (node instanceof AST_Dot) {
add(node.property);
} else if (node instanceof AST_ObjectKeyVal) {
add(node.key); add(node.key);
} else if (node instanceof AST_ObjectProperty) { } else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above // setter or getter, since KeyVal is handled above
add(node.key.name); add(node.key.name);
} else if (node instanceof AST_Dot) {
add(node.property);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
addStrings(node.property, add); addStrings(node.property, add);
} else if (node instanceof AST_Call
&& node.expression.print_to_string() == "Object.defineProperty") {
addStrings(node.args[1], add);
} }
})); }));
// step 2: transform the tree, renaming properties // step 2: renaming properties
return ast.transform(new TreeTransformer(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_Binary) {
if (node.operator == "in") mangleStrings(node.left);
} else if (node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_Dot) switch (exp.property) {
case "defineProperty":
case "getOwnPropertyDescriptor":
if (node.args.length < 2) break;
exp = exp.expression;
if (!(exp instanceof AST_SymbolRef)) break;
if (exp.name != "Object") break;
if (!exp.definition().undeclared) break;
mangleStrings(node.args[1]);
break;
case "hasOwnProperty":
if (node.args.length < 1) break;
mangleStrings(node.args[0]);
break;
}
} else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
} else if (node instanceof AST_ObjectKeyVal) {
node.key = mangle(node.key); node.key = mangle(node.key);
} else if (node instanceof AST_ObjectProperty) { } else if (node instanceof AST_ObjectProperty) {
// setter or getter // setter or getter
node.key.name = mangle(node.key.name); node.key.name = mangle(node.key.name);
} else if (node instanceof AST_Dot) { } else if (node instanceof AST_Sub) {
node.property = mangle(node.property); if (!options.keep_quoted) mangleStrings(node.property);
} else if (!options.keep_quoted && node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
} else if (node instanceof AST_Call
&& node.expression.print_to_string() == "Object.defineProperty") {
node.args[1] = mangleStrings(node.args[1]);
} }
})); }));
// only function declarations after this line // only function declarations after this line
function can_mangle(name) { function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false; if (unmangleable[name]) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) return cache.has(name);
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false; if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true; return true;
} }
function should_mangle(name) { function should_mangle(name) {
if (reserved[name]) return false;
if (regex && !regex.test(name)) return false; if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false; return cache.has(name) || names_to_mangle[name];
return cache.has(name) || names_to_mangle.indexOf(name) >= 0;
} }
function add(name) { function add(name) {
if (can_mangle(name)) push_uniq(names_to_mangle, name); if (can_mangle(name)) names_to_mangle[name] = true;
if (!should_mangle(name)) push_uniq(unmangleable, name); if (!should_mangle(name)) unmangleable[name] = true;
} }
function mangle(name) { function mangle(name) {
@@ -218,17 +248,13 @@ function mangle_properties(ast, options) {
} }
function mangleStrings(node) { function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node) { if (node instanceof AST_Sequence) {
if (node instanceof AST_Sequence) { mangleStrings(node.expressions.tail_node());
var last = node.expressions.length - 1; } else if (node instanceof AST_String) {
node.expressions[last] = mangleStrings(node.expressions[last]); node.value = mangle(node.value);
} else if (node instanceof AST_String) { } else if (node instanceof AST_Conditional) {
node.value = mangle(node.value); mangleStrings(node.consequent);
} else if (node instanceof AST_Conditional) { mangleStrings(node.alternative);
node.consequent = mangleStrings(node.consequent); }
node.alternative = mangleStrings(node.alternative);
}
return node;
}));
} }
} }

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -43,31 +43,25 @@
"use strict"; "use strict";
function SymbolDef(scope, orig, init) { function SymbolDef(id, scope, orig, init) {
this.eliminated = 0;
this.global = false;
this.id = id;
this.init = init;
this.lambda = orig instanceof AST_SymbolLambda;
this.mangled_name = null;
this.name = orig.name; this.name = orig.name;
this.orig = [ orig ]; this.orig = [ orig ];
this.init = init;
this.eliminated = 0;
this.scope = scope;
this.references = []; this.references = [];
this.replaced = 0; this.replaced = 0;
this.global = false; this.scope = scope;
this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.id = SymbolDef.next_id++;
this.lambda = orig instanceof AST_SymbolLambda;
} }
SymbolDef.next_id = 1;
SymbolDef.prototype = { SymbolDef.prototype = {
unmangleable: function(options) { forEach: function(fn) {
return this.global && !options.toplevel this.orig.forEach(fn);
|| this.undeclared this.references.forEach(fn);
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
}, },
mangle: function(options) { mangle: function(options) {
var cache = options.cache && options.cache.props; var cache = options.cache && options.cache.props;
@@ -87,7 +81,15 @@ SymbolDef.prototype = {
}, },
redefined: function() { redefined: function() {
return this.defun && this.defun.variables.get(this.name); return this.defun && this.defun.variables.get(this.name);
} },
unmangleable: function(options) {
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
},
}; };
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
@@ -98,29 +100,39 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
var self = this; var self = this;
var scope = self.parent_scope = null;
var defun = null; var defun = null;
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Catch) { if (node instanceof AST_Defun) {
var save_scope = scope; node.name.walk(tw);
scope = new AST_Scope(node); walk_scope(function() {
scope.init_scope_vars(save_scope); node.argnames.forEach(function(argname) {
descend(); argname.walk(tw);
scope = save_scope; });
walk_body(node, tw);
});
return true; return true;
} }
if (node instanceof AST_Scope) { if (node instanceof AST_Try) {
node.init_scope_vars(scope); walk_scope(function() {
var save_scope = scope; walk_body(node, tw);
var save_defun = defun; });
defun = scope = node; if (node.bcatch) node.bcatch.walk(tw);
descend(); if (node.bfinally) node.bfinally.walk(tw);
scope = save_scope; return true;
defun = save_defun; }
if (node instanceof AST_BlockScope) {
walk_scope(descend);
return true; return true;
} }
if (node instanceof AST_With) { if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) s.uses_with = true; var s = scope;
do {
s = s.resolve();
if (s.uses_with) break;
s.uses_with = true;
} while (s = s.parent_scope);
return; return;
} }
if (node instanceof AST_Symbol) { if (node instanceof AST_Symbol) {
@@ -130,27 +142,46 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = node; node.thedef = node;
node.references = []; node.references = [];
} }
if (node instanceof AST_SymbolDefun) { if (node instanceof AST_SymbolCatch) {
// This should be defined in the parent scope, as we encounter the scope.def_variable(node).defun = defun;
// AST_Defun node before getting to its AST_Symbol. } else if (node instanceof AST_SymbolDefun) {
(node.scope = defun.parent_scope.resolve()).def_function(node, defun); defun.def_function(node, tw.parent());
entangle(defun, scope);
} else if (node instanceof AST_SymbolFunarg) {
defun.def_variable(node);
entangle(defun, scope);
} else if (node instanceof AST_SymbolLambda) { } else if (node instanceof AST_SymbolLambda) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun); var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie8) def.defun = defun.parent_scope.resolve(); if (options.ie8) def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolVar) { } else if (node instanceof AST_SymbolVar) {
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined); defun.def_variable(node, null);
if (defun !== scope) { entangle(defun, scope);
node.mark_enclosed(options); }
var def = scope.find_variable(node);
if (node.thedef !== def) { function walk_scope(descend) {
node.thedef = def; node.init_vars(scope);
} var save_defun = defun;
node.reference(options); var save_scope = scope;
} if (node instanceof AST_Scope) defun = node;
} else if (node instanceof AST_SymbolCatch) { scope = node;
scope.def_variable(node).defun = defun; descend();
scope = save_scope;
defun = save_defun;
}
function entangle(defun, scope) {
if (defun === scope) return;
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef === def) return;
node.thedef = def;
def.orig.push(node);
node.mark_enclosed(options);
} }
}); });
self.make_def = function(orig, init) {
return new SymbolDef(++next_def_id, this, orig, init);
};
self.walk(tw); self.walk(tw);
// pass 2: find back references and eval // pass 2: find back references and eval
@@ -162,17 +193,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
} }
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var name = node.name; var name = node.name;
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true;
}
}
var sym = node.scope.find_variable(name); var sym = node.scope.find_variable(name);
if (!sym) { if (!sym) {
sym = self.def_global(node); sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") { } else if (name == "arguments" && sym.scope instanceof AST_Lambda) {
sym.scope.uses_arguments = true; sym.scope.uses_arguments = true;
} }
if (name == "eval") {
var parent = tw.parent();
if (parent.TYPE == "Call" && parent.expression === node) {
var s = node.scope;
do {
s = s.resolve();
if (s.uses_eval) break;
s.uses_eval = true;
} while (s = s.parent_scope);
} else if (sym.undeclared) {
self.uses_eval = true;
}
}
node.thedef = sym; node.thedef = sym;
node.reference(options); node.reference(options);
return true; return true;
@@ -219,9 +258,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
var redef; var redef;
while (redef = new_def.redefined()) new_def = redef; while (redef = new_def.redefined()) new_def = redef;
} else { } else {
new_def = self.globals.get(name) || scope.def_variable(node); new_def = self.globals.get(name);
} }
old_def.orig.concat(old_def.references).forEach(function(node) { if (new_def) {
new_def.orig.push(node);
} else {
new_def = scope.def_variable(node);
}
old_def.defun = new_def.scope;
old_def.forEach(function(node) {
node.redef = true;
node.thedef = new_def; node.thedef = new_def;
node.reference(options); node.reference(options);
}); });
@@ -235,7 +281,7 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
if (globals.has(name)) { if (globals.has(name)) {
return globals.get(name); return globals.get(name);
} else { } else {
var g = new SymbolDef(this, node); var g = this.make_def(node);
g.undeclared = true; g.undeclared = true;
g.global = true; g.global = true;
globals.set(name, g); globals.set(name, g);
@@ -243,24 +289,36 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
} }
}); });
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) { function init_block_vars(scope, parent) {
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) scope.cname = -1; // the current index for mangling functions/variables
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement scope.parent_scope = parent; // the parent scope (null if this is the top level)
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.parent_scope = parent_scope; // the parent scope scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
this.cname = -1; // the current index for mangling functions/variables }
});
AST_Lambda.DEFMETHOD("init_scope_vars", function() { function init_scope_vars(scope, parent) {
AST_Scope.prototype.init_scope_vars.apply(this, arguments); init_block_vars(scope, parent);
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
}
AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
init_block_vars(this, parent_scope);
});
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
});
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({ this.def_variable(new AST_SymbolFunarg({
name: "arguments", name: "arguments",
start: this.start, start: this.start,
end: this.end end: this.end,
})); }));
return this;
}); });
AST_Symbol.DEFMETHOD("mark_enclosed", function(options) { AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
@@ -281,42 +339,36 @@ AST_Symbol.DEFMETHOD("reference", function(options) {
this.mark_enclosed(options); this.mark_enclosed(options);
}); });
AST_Scope.DEFMETHOD("find_variable", function(name) { AST_BlockScope.DEFMETHOD("find_variable", function(name) {
if (name instanceof AST_Symbol) name = name.name; if (name instanceof AST_Symbol) name = name.name;
return this.variables.get(name) return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name)); || (this.parent_scope && this.parent_scope.find_variable(name));
}); });
AST_Scope.DEFMETHOD("def_function", function(symbol, init) { AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
var def = this.def_variable(symbol, init); var def = this.def_variable(symbol, init);
if (!def.init || def.init instanceof AST_Defun) def.init = init; if (!def.init || def.init instanceof AST_Defun) def.init = init;
this.functions.set(symbol.name, def); this.functions.set(symbol.name, def);
return def; return def;
}); });
AST_Scope.DEFMETHOD("def_variable", function(symbol, init) { AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name); var def = this.variables.get(symbol.name);
if (def) { if (def) {
def.orig.push(symbol); def.orig.push(symbol);
if (def.init instanceof AST_Function) def.init = init; if (def.init instanceof AST_Function) def.init = init;
} else { } else {
def = new SymbolDef(this, symbol, init); def = this.make_def(symbol, init);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
def.global = !this.parent_scope; def.global = !this.parent_scope;
} }
return symbol.thedef = def; return symbol.thedef = def;
}); });
AST_Lambda.DEFMETHOD("resolve", return_this);
AST_Scope.DEFMETHOD("resolve", function() {
return this.parent_scope.resolve();
});
AST_Toplevel.DEFMETHOD("resolve", return_this);
function names_in_use(scope, options) { function names_in_use(scope, options) {
var names = scope.names_in_use; var names = scope.names_in_use;
if (!names) { if (!names) {
scope.names_in_use = names = Object.create(scope.mangled_names || null); scope.names_in_use = names = Object.create(null);
scope.cname_holes = []; scope.cname_holes = [];
var cache = options.cache && options.cache.props; var cache = options.cache && options.cache.props;
scope.enclosed.forEach(function(def) { scope.enclosed.forEach(function(def) {
@@ -334,7 +386,7 @@ function next_mangled_name(scope, options, def) {
var holes = scope.cname_holes; var holes = scope.cname_holes;
var names = Object.create(null); var names = Object.create(null);
var scopes = [ scope ]; var scopes = [ scope ];
def.references.forEach(function(sym) { def.forEach(function(sym) {
var scope = sym.scope; var scope = sym.scope;
do { do {
if (scopes.indexOf(scope) < 0) { if (scopes.indexOf(scope) < 0) {
@@ -350,7 +402,7 @@ function next_mangled_name(scope, options, def) {
name = base54(holes[i]); name = base54(holes[i]);
if (names[name]) continue; if (names[name]) continue;
holes.splice(i, 1); holes.splice(i, 1);
scope.names_in_use[name] = true; in_use[name] = true;
return name; return name;
} }
while (true) { while (true) {
@@ -359,7 +411,7 @@ function next_mangled_name(scope, options, def) {
if (!names[name]) break; if (!names[name]) break;
holes.push(scope.cname); holes.push(scope.cname);
} }
scope.names_in_use[name] = true; in_use[name] = true;
return name; return name;
} }
@@ -371,18 +423,10 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options) {
// labels are always mangleable // labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", return_false); AST_Label.DEFMETHOD("unmangleable", return_false);
AST_Symbol.DEFMETHOD("unreferenced", function() {
return !this.definition().references.length && !this.scope.pinned();
});
AST_Symbol.DEFMETHOD("definition", function() { AST_Symbol.DEFMETHOD("definition", function() {
return this.thedef; return this.thedef;
}); });
AST_Symbol.DEFMETHOD("global", function() {
return this.definition().global;
});
function _default_mangler_options(options) { function _default_mangler_options(options) {
options = defaults(options, { options = defaults(options, {
eval : false, eval : false,
@@ -408,7 +452,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var lname = -1; var lname = -1;
if (options.cache && options.cache.props) { if (options.cache && options.cache.props) {
var mangled_names = this.mangled_names = Object.create(null); var mangled_names = names_in_use(this, options);
options.cache.props.each(function(mangled_name) { options.cache.props.each(function(mangled_name) {
mangled_names[mangled_name] = true; mangled_names[mangled_name] = true;
}); });
@@ -446,7 +490,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
node.mangled_name = name; node.mangled_name = name;
return true; return true;
} }
if (!options.ie8 && node instanceof AST_Catch) { if (!options.ie8 && node instanceof AST_Catch && node.argname) {
var def = node.argname.definition(); var def = node.argname.definition();
var redef = defer_redef(def, node.argname); var redef = defer_redef(def, node.argname);
descend(); descend();
@@ -480,12 +524,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props; var cache = options.cache && options.cache.props;
var avoid = Object.create(null); var avoid = Object.create(RESERVED_WORDS);
options.reserved.forEach(to_avoid); options.reserved.forEach(to_avoid);
this.globals.each(add_def); this.globals.each(add_def);
this.walk(new TreeWalker(function(node) { this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def); if (node instanceof AST_BlockScope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
})); }));
return avoid; return avoid;
@@ -509,15 +552,14 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var cname = 0; var cname = 0;
this.globals.each(rename); this.globals.each(rename);
this.walk(new TreeWalker(function(node) { this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename); if (node instanceof AST_BlockScope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
})); }));
function next_name() { function next_name() {
var name; var name;
do { do {
name = base54(cname++); name = base54(cname++);
} while (avoid[name] || RESERVED_WORDS[name]); } while (avoid[name]);
return name; return name;
} }
@@ -528,11 +570,8 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var redef = def.redefined(); var redef = def.redefined();
var name = redef ? redef.rename || redef.name : next_name(); var name = redef ? redef.rename || redef.name : next_name();
def.rename = name; def.rename = name;
def.orig.forEach(function(sym) { def.forEach(function(sym) {
sym.name = name; if (sym.definition() === def) sym.name = name;
});
def.references.forEach(function(sym) {
sym.name = name;
}); });
} }
}); });
@@ -545,22 +584,24 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
options = _default_mangler_options(options); options = _default_mangler_options(options);
base54.reset(); base54.reset();
var fn = AST_Symbol.prototype.add_source_map;
try { try {
AST_Node.prototype.print = function(stream, force_parens) { AST_Symbol.prototype.add_source_map = function() {
this._print(stream, force_parens); if (!this.unmangleable(options)) base54.consider(this.name, -1);
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);
}
}
}; };
if (options.properties) {
AST_Dot.prototype.add_source_map = function() {
base54.consider(this.property, -1);
};
AST_Sub.prototype.add_source_map = function() {
skip_string(this.property);
};
}
base54.consider(this.print_to_string(), 1); base54.consider(this.print_to_string(), 1);
} finally { } finally {
AST_Node.prototype.print = AST_Node.prototype._print; AST_Symbol.prototype.add_source_map = fn;
delete AST_Dot.prototype.add_source_map;
delete AST_Sub.prototype.add_source_map;
} }
base54.sort(); base54.sort();

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -43,62 +43,149 @@
"use strict"; "use strict";
// a small wrapper around fitzgen's source-map library var vlq_char = characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
function SourceMap(options) { var vlq_bits = vlq_char.reduce(function(map, ch, bits) {
options = defaults(options, { map[ch] = bits;
file: null, return map;
root: null, }, Object.create(null));
orig: null,
orig_line_diff: 0, function vlq_decode(indices, str) {
dest_line_diff: 0, var value = 0;
}, true); var shift = 0;
var generator = new MOZ_SourceMap.SourceMapGenerator({ for (var i = 0, j = 0; i < str.length; i++) {
file: options.file, var bits = vlq_bits[str[i]];
sourceRoot: options.root value += (bits & 31) << shift;
}); if (bits & 32) {
var maps = options.orig && Object.create(null); shift += 5;
if (maps) for (var source in options.orig) { } else {
var map = new MOZ_SourceMap.SourceMapConsumer(options.orig[source]); indices[j++] += value & 1 ? 0x80000000 | -(value >> 1) : value >> 1;
if (Array.isArray(options.orig[source].sources)) { value = shift = 0;
map._sources.toArray().forEach(function(source) {
var sourceContent = map.sourceContentFor(source, true);
if (sourceContent) generator.setSourceContent(source, sourceContent);
});
} }
maps[source] = map;
} }
return { return j;
add: function(source, gen_line, gen_col, orig_line, orig_col, name) { }
var map = maps && maps[source];
if (map) { function vlq_encode(num) {
var info = map.originalPositionFor({ var result = "";
line: orig_line, num = Math.abs(num) << 1 | num >>> 31;
column: orig_col do {
var bits = num & 31;
if (num >>>= 5) bits |= 32;
result += vlq_char[bits];
} while (num);
return result;
}
function create_array_map() {
var map = Object.create(null);
var array = [];
array.index = function(name) {
if (!HOP(map, name)) {
map[name] = array.length;
array.push(name);
}
return map[name];
};
return array;
}
function SourceMap(options) {
var sources = create_array_map();
var sources_content = options.includeSources && Object.create(null);
var names = create_array_map();
var mappings = "";
if (options.orig) Object.keys(options.orig).forEach(function(name) {
var map = options.orig[name];
var indices = [ 0, 0, 1, 0, 0 ];
options.orig[name] = {
names: map.names,
mappings: map.mappings.split(/;/).map(function(line) {
indices[0] = 0;
return line.split(/,/).map(function(segment) {
return indices.slice(0, vlq_decode(indices, segment));
}); });
if (info.source === null) return; }),
source = info.source; sources: map.sources,
orig_line = info.line; };
orig_col = info.column; if (!sources_content || !map.sourcesContent) return;
name = info.name || name; for (var i = 0; i < map.sources.length; i++) {
} var content = map.sourcesContent[i];
generator.addMapping({ if (content) sources_content[map.sources[i]] = content;
name: name, }
source: source, });
generated: { var prev_source;
line: gen_line + options.dest_line_diff, var generated_line = 1;
column: gen_col var generated_column = 0;
}, var source_index = 0;
original: { var original_line = 1;
line: orig_line + options.orig_line_diff, var original_column = 0;
column: orig_col var name_index = 0;
return {
add: options.orig ? function(source, gen_line, gen_col, orig_line, orig_col, name) {
var map = options.orig[source];
if (map) {
var segments = map.mappings[orig_line - 1];
if (!segments) return;
var indices;
for (var i = 0; i < segments.length; i++) {
var col = segments[i][0];
if (orig_col >= col) indices = segments[i];
if (orig_col <= col) break;
} }
}); if (!indices || indices.length < 4) {
}, source = null;
get: function() { } else {
return generator; source = map.sources[indices[1]];
}, orig_line = indices[2];
orig_col = indices[3];
if (indices.length > 4) name = map.names[indices[4]];
}
}
add(source, gen_line, gen_col, orig_line, orig_col, name);
} : add,
setSourceContent: sources_content ? function(source, content) {
sources_content[source] = content;
} : noop,
toString: function() { toString: function() {
return JSON.stringify(generator.toJSON()); return JSON.stringify({
version: 3,
file: options.filename || undefined,
sourceRoot: options.root || undefined,
sources: sources,
sourcesContent: sources_content ? sources.map(function(source) {
return sources_content[source] || null;
}) : undefined,
names: names,
mappings: mappings,
});
} }
}; };
function add(source, gen_line, gen_col, orig_line, orig_col, name) {
if (prev_source == null && source == null) return;
prev_source = source;
if (generated_line < gen_line) {
generated_column = 0;
do {
mappings += ";";
} while (++generated_line < gen_line);
} else if (mappings) {
mappings += ",";
}
mappings += vlq_encode(gen_col - generated_column);
generated_column = gen_col;
if (source == null) return;
var src_idx = sources.index(source);
mappings += vlq_encode(src_idx - source_index);
source_index = src_idx;
mappings += vlq_encode(orig_line - original_line);
original_line = orig_line;
mappings += vlq_encode(orig_col - original_column);
original_column = orig_col;
if (options.names && name != null) {
var name_idx = names.index(name);
mappings += vlq_encode(name_idx - name_index);
name_index = name_idx;
}
}
} }

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -116,7 +116,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.bfinally) self.bfinally = self.bfinally.transform(tw); if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
}); });
DEF(AST_Catch, function(self, tw) { DEF(AST_Catch, function(self, tw) {
self.argname = self.argname.transform(tw); if (self.argname) self.argname = self.argname.transform(tw);
self.body = do_list(self.body, tw); self.body = do_list(self.body, tw);
}); });
DEF(AST_Definitions, function(self, tw) { DEF(AST_Definitions, function(self, tw) {

View File

@@ -1,7 +1,7 @@
/*********************************************************************** /***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor. A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2 https://github.com/mishoo/UglifyJS
-------------------------------- (C) --------------------------------- -------------------------------- (C) ---------------------------------
@@ -87,15 +87,13 @@ DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError); configure_error_stack(DefaultsError);
function defaults(args, defs, croak) { function defaults(args, defs, croak) {
if (args === true) args = {}; if (croak) for (var i in args) {
var ret = args || {}; if (HOP(args, i) && !HOP(defs, i)) throw new DefaultsError("`" + i + "` is not a supported option", defs);
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) {
throw new DefaultsError("`" + i + "` is not a supported option", defs);
} }
for (var i in defs) if (HOP(defs, i)) { for (var i in args) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; if (HOP(args, i)) defs[i] = args[i];
} }
return ret; return defs;
} }
function merge(obj, ext) { function merge(obj, ext) {
@@ -114,48 +112,29 @@ function return_this() { return this; }
function return_null() { return null; } function return_null() { return null; }
var List = (function() { var List = (function() {
function List(a, f, backwards) { function List(a, f) {
var ret = [], top = [], i; var ret = [];
function doit() { for (var i = 0; i < a.length; i++) {
var val = f(a[i], i); var val = f(a[i], i);
var is_last = val instanceof Last; if (val === skip) continue;
if (is_last) val = val.v; if (val instanceof Splice) {
if (val instanceof AtTop) { ret.push.apply(ret, val.v);
val = val.v;
if (val instanceof Splice) {
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
} else {
top.push(val);
}
} else if (val !== skip) {
if (val instanceof Splice) {
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
} else {
ret.push(val);
}
}
return is_last;
}
if (Array.isArray(a)) {
if (backwards) {
for (i = a.length; --i >= 0;) if (doit()) break;
ret.reverse();
top.reverse();
} else { } else {
for (i = 0; i < a.length; ++i) if (doit()) break; ret.push(val);
} }
} else {
for (i in a) if (HOP(a, i)) if (doit()) break;
} }
return top.concat(ret); return ret;
} }
List.at_top = function(val) { return new AtTop(val); }; List.is_op = function(val) {
List.splice = function(val) { return new Splice(val); }; return val === skip || val instanceof Splice;
List.last = function(val) { return new Last(val); }; };
List.splice = function(val) {
return new Splice(val);
};
var skip = List.skip = {}; var skip = List.skip = {};
function AtTop(val) { this.v = val; } function Splice(val) {
function Splice(val) { this.v = val; } this.v = val;
function Last(val) { this.v = val; } }
return List; return List;
})(); })();
@@ -185,7 +164,7 @@ function makePredicate(words) {
function all(array, predicate) { function all(array, predicate) {
for (var i = array.length; --i >= 0;) for (var i = array.length; --i >= 0;)
if (!predicate(array[i])) if (!predicate(array[i], i))
return false; return false;
return true; return true;
} }
@@ -217,6 +196,12 @@ Dictionary.prototype = {
return this; return this;
}, },
has: function(key) { return ("$" + key) in this._values }, has: function(key) { return ("$" + key) in this._values },
all: function(predicate) {
for (var i in this._values)
if (!predicate(this._values[i], i.substr(1)))
return false;
return true;
},
each: function(f) { each: function(f) {
for (var i in this._values) for (var i in this._values)
f(this._values[i], i.substr(1)); f(this._values[i], i.substr(1));

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.8.0", "version": "3.11.2",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -11,7 +11,7 @@
"Alex Lam <alexlamsl@gmail.com>", "Alex Lam <alexlamsl@gmail.com>",
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)" "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
], ],
"repository": "mishoo/UglifyJS2", "repository": "mishoo/UglifyJS",
"main": "tools/node.js", "main": "tools/node.js",
"bin": { "bin": {
"uglifyjs": "bin/uglifyjs" "uglifyjs": "bin/uglifyjs"
@@ -22,10 +22,6 @@
"tools", "tools",
"LICENSE" "LICENSE"
], ],
"dependencies": {
"commander": "~2.20.3",
"source-map": "~0.6.1"
},
"devDependencies": { "devDependencies": {
"acorn": "~7.1.0", "acorn": "~7.1.0",
"semver": "~6.3.0" "semver": "~6.3.0"

View File

@@ -5,12 +5,11 @@
var createHash = require("crypto").createHash; var createHash = require("crypto").createHash;
var fetch = require("./fetch"); var fetch = require("./fetch");
var fork = require("child_process").fork; var spawn = require("child_process").spawn;
var zlib = require("zlib"); var zlib = require("zlib");
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) args.push("-mc");
args.push("-mc"); args.unshift("bin/uglifyjs");
}
args.push("--timings"); args.push("--timings");
var urls = [ var urls = [
"https://code.jquery.com/jquery-3.4.1.js", "https://code.jquery.com/jquery-3.4.1.js",
@@ -70,18 +69,20 @@ urls.forEach(function(url) {
}; };
fetch(url, function(err, res) { fetch(url, function(err, res) {
if (err) throw err; if (err) throw err;
var uglifyjs = fork("bin/uglifyjs", args, { silent: true }); var uglifyjs = spawn(process.argv[0], args, { silent: true });
res.on("data", function(data) { res.on("data", function(data) {
results[url].input += data.length; results[url].input += data.length;
}).pipe(uglifyjs.stdin); }).pipe(uglifyjs.stdin);
var sha1 = createHash("sha1");
uglifyjs.stdout.on("data", function(data) { uglifyjs.stdout.on("data", function(data) {
results[url].output += data.length; results[url].output += data.length;
}).pipe(zlib.createGzip({ }).pipe(zlib.createGzip({
level: zlib.Z_BEST_COMPRESSION level: zlib.Z_BEST_COMPRESSION
})).on("data", function(data) { })).on("data", function(data) {
results[url].gzip += data.length; results[url].gzip += data.length;
}).pipe(createHash("sha1")).on("data", function(data) { sha1.update(data);
results[url].sha1 = data.toString("hex"); }).on("end", function() {
results[url].sha1 = sha1.digest("hex");
done(); done();
}); });
uglifyjs.stderr.setEncoding("utf8"); uglifyjs.stderr.setEncoding("utf8");

View File

@@ -63,7 +63,7 @@ function make_code(ast, options) {
function parse_test(file) { function parse_test(file) {
var script = fs.readFileSync(file, "utf8"); var script = fs.readFileSync(file, "utf8");
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348 // TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS/issues/348
try { try {
var ast = U.parse(script, { var ast = U.parse(script, {
filename: file filename: file
@@ -188,6 +188,7 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
} }
}); });
var options_formatted = JSON.stringify(options, null, 4); var options_formatted = JSON.stringify(options, null, 4);
options.validate = true;
var result = U.minify(input_code, options); var result = U.minify(input_code, options);
if (result.error) { if (result.error) {
log([ log([
@@ -207,8 +208,9 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
}); });
return false; return false;
} else { } else {
var expected = stdout[options.toplevel ? 1 : 0]; var toplevel = sandbox.has_toplevel(options);
var actual = run_code(result.code, options.toplevel); var expected = stdout[toplevel ? 1 : 0];
var actual = run_code(result.code, toplevel);
if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) { if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
actual = expected; actual = expected;
} }
@@ -250,6 +252,7 @@ function run_code(code, toplevel) {
function test_case(test) { function test_case(test) {
log(" Running test [{name}]", { name: test.name }); log(" Running test [{name}]", { name: test.name });
U.AST_Node.enable_validation();
var output_options = test.beautify || {}; var output_options = test.beautify || {};
var expect; var expect;
if (test.expect) { if (test.expect) {
@@ -266,6 +269,7 @@ function test_case(test) {
quote_style: 3, quote_style: 3,
}); });
try { try {
input.validate_ast();
U.parse(input_code); U.parse(input_code);
} catch (ex) { } catch (ex) {
log([ log([
@@ -308,12 +312,10 @@ function test_case(test) {
if (test.mangle) { if (test.mangle) {
output.compute_char_frequency(test.mangle); output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle); output.mangle_names(test.mangle);
if (test.mangle.properties) { if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties);
output = U.mangle_properties(output, test.mangle.properties);
}
} }
output = make_code(output, output_options); var output_code = make_code(output, output_options);
if (expect != output) { if (expect != output_code) {
log([ log([
"!!! failed", "!!! failed",
"---INPUT---", "---INPUT---",
@@ -326,14 +328,15 @@ function test_case(test) {
"", "",
].join("\n"), { ].join("\n"), {
input: input_formatted, input: input_formatted,
output: output, output: output_code,
expected: expect expected: expect
}); });
return false; return false;
} }
// expect == output // expect == output
try { try {
U.parse(output); output.validate_ast();
U.parse(output_code);
} catch (ex) { } catch (ex) {
log([ log([
"!!! Test matched expected result but cannot parse output", "!!! Test matched expected result but cannot parse output",
@@ -347,7 +350,7 @@ function test_case(test) {
"", "",
].join("\n"), { ].join("\n"), {
input: input_formatted, input: input_formatted,
output: output, output: output_code,
error: ex, error: ex,
}); });
return false; return false;
@@ -378,7 +381,10 @@ function test_case(test) {
} }
if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) { if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = [ run_code(input_code), run_code(input_code, true) ]; var stdout = [ run_code(input_code), run_code(input_code, true) ];
var toplevel = test.options.toplevel; var toplevel = sandbox.has_toplevel({
compress: test.options,
mangle: test.mangle
});
var actual = stdout[toplevel ? 1 : 0]; var actual = stdout[toplevel ? 1 : 0];
if (test.expect_stdout === true) { if (test.expect_stdout === true) {
test.expect_stdout = actual; test.expect_stdout = actual;
@@ -403,7 +409,7 @@ function test_case(test) {
}); });
return false; return false;
} }
actual = run_code(output, toplevel); actual = run_code(output_code, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) { if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([ log([
"!!! failed", "!!! failed",

View File

@@ -243,20 +243,18 @@ issue_3273: {
arguments: true, arguments: true,
} }
input: { input: {
function f(a) { (function(a) {
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect: { expect: {
function f(a) { (function(a) {
console.log(a, a); console.log(a, a);
a++; a++;
console.log(a, a); console.log(a, a);
} })(0);
f(0);
} }
expect_stdout: [ expect_stdout: [
"0 0", "0 0",
@@ -264,26 +262,43 @@ issue_3273: {
] ]
} }
issue_3273_no_call_arg: {
options = {
arguments: true,
}
input: {
(function(a) {
arguments[0] = "FAIL";
console.log(a);
})();
}
expect: {
(function(a) {
arguments[0] = "FAIL";
console.log(a);
})();
}
expect_stdout: "undefined"
}
issue_3273_reduce_vars: { issue_3273_reduce_vars: {
options = { options = {
arguments: true, arguments: true,
reduce_vars: true, reduce_vars: true,
} }
input: { input: {
function f(a) { (function(a) {
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect: { expect: {
function f(a) { (function(a) {
console.log(a, a); console.log(a, a);
a++; a++;
console.log(a, a); console.log(a, a);
} })(0);
f(0);
} }
expect_stdout: [ expect_stdout: [
"0 0", "0 0",
@@ -296,22 +311,20 @@ issue_3273_local_strict: {
arguments: true, arguments: true,
} }
input: { input: {
function f(a) { (function(a) {
"use strict"; "use strict";
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect: { expect: {
function f(a) { (function(a) {
"use strict"; "use strict";
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect_stdout: [ expect_stdout: [
"0 0", "0 0",
@@ -325,22 +338,20 @@ issue_3273_local_strict_reduce_vars: {
reduce_vars: true, reduce_vars: true,
} }
input: { input: {
function f(a) { (function(a) {
"use strict"; "use strict";
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect: { expect: {
function f(a) { (function(a) {
"use strict"; "use strict";
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect_stdout: [ expect_stdout: [
"0 0", "0 0",
@@ -354,21 +365,19 @@ issue_3273_global_strict: {
} }
input: { input: {
"use strict"; "use strict";
function f(a) { (function(a) {
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect: { expect: {
"use strict"; "use strict";
function f(a) { (function(a) {
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect_stdout: [ expect_stdout: [
"0 0", "0 0",
@@ -383,21 +392,19 @@ issue_3273_global_strict_reduce_vars: {
} }
input: { input: {
"use strict"; "use strict";
function f(a) { (function(a) {
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect: { expect: {
"use strict"; "use strict";
function f(a) { (function(a) {
console.log(arguments[0], a); console.log(arguments[0], a);
arguments[0]++; arguments[0]++;
console.log(arguments[0], a); console.log(arguments[0], a);
} })(0);
f(0);
} }
expect_stdout: [ expect_stdout: [
"0 0", "0 0",

View File

@@ -91,12 +91,11 @@ asm_mixed: {
function no_asm_GeometricMean(stdlib, foreign, buffer) { function no_asm_GeometricMean(stdlib, foreign, buffer) {
function logSum(start, end) { function logSum(start, end) {
start |= 0, end |= 0; start |= 0, end |= 0;
var sum = 0, p = 0, q = 0; for (var sum = 0, p = 0, q = 0, p = start << 3, q = end << 3; (0 | p) < (0 | q); p = p + 8 | 0) sum += +log(values[p >> 3]);
for (p = start << 3, q = end << 3; (0 | p) < (0 | q); p = p + 8 | 0) sum += +log(values[p >> 3]);
return +sum; return +sum;
} }
function geometricMean(start, end) { function geometricMean(start, end) {
return start |= 0, end |= 0, +exp(logSum(start, end) / (end - start | 0)); return start |= 0, end |= 0, +exp(+logSum(start, end) / (end - start | 0));
} }
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer); var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
return { geometricMean: geometricMean }; return { geometricMean: geometricMean };

View File

@@ -407,3 +407,57 @@ issue_3429_2: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
issue_3949_1: {
options = {
assignments: true,
evaluate: true,
reduce_vars: true,
}
input: {
var a = 42;
function f() {
var b = a;
b = b >> 2;
return 100 + b;
}
console.log(f());
}
expect: {
var a = 42;
function f() {
var b = a;
b >>= 2;
return 100 + b;
}
console.log(f());
}
expect_stdout: "110"
}
issue_3949_2: {
options = {
assignments: true,
evaluate: true,
reduce_vars: true,
}
input: {
var a = 42;
function f() {
var b = a;
b = 5 & b;
return 100 + b;
}
console.log(f());
}
expect: {
var a = 42;
function f() {
var b = a;
b &= 5;
return 100 + b;
}
console.log(f());
}
expect_stdout: "100"
}

File diff suppressed because it is too large Load Diff

View File

@@ -93,6 +93,59 @@ self_comparison_2: {
expect_stdout: "false true" expect_stdout: "false true"
} }
self_comparison_3: {
options = {
comparisons: true,
}
input: {
var a;
function f() {
var b = a;
a = null;
return b;
}
for (var i = 0; i < 2; i++)
console.log(f() === f());
}
expect: {
var a;
function f() {
var b = a;
a = null;
return b;
}
for (var i = 0; i < 2; i++)
console.log(f() === f());
}
expect_stdout: [
"false",
"true",
]
}
self_comparison_4: {
options = {
booleans: true,
comparisons: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {};
console.log(o == o, o != o);
console.log(o === o, o !== o);
}
expect: {
console.log(!0, !1);
console.log(!0, !1);
}
expect_stdout: [
"true false",
"true false",
]
}
issue_2857_1: { issue_2857_1: {
options = { options = {
comparisons: true, comparisons: true,

View File

@@ -238,6 +238,41 @@ concat_8: {
expect_stdout: true expect_stdout: true
} }
concat_9: {
options = {
booleans: true,
evaluate: true,
reduce_vars: true,
strings: true,
toplevel: true,
}
input: {
var a = "foo";
console.log(
12 + (34 + a),
null + (34 + a),
12 + (null + a),
false + (34 + a),
12 + (false + a),
"bar" + (34 + a),
12 + ("bar" + a)
);
}
expect: {
var a = "foo";
console.log(
"1234" + a,
"null34" + a,
"12null" + a,
!1 + (34 + a),
12 + (!1 + a),
"bar34" + a,
"12bar" + a
);
}
expect_stdout: true
}
issue_3689: { issue_3689: {
options = { options = {
strings: true, strings: true,

View File

@@ -783,6 +783,28 @@ cond_12: {
} }
} }
cond_13: {
options = {
conditionals: true,
}
input: {
x ? y(a) : z(a);
x ? y.f(a) : z.f(a);
x ? y.f(a) : z.g(a);
x ? y.f()(a) : z.g()(a);
x ? y.f.u(a) : z.g.u(a);
x ? y.f().u(a) : z.g().u(a);
}
expect: {
(x ? y : z)(a);
(x ? y : z).f(a);
x ? y.f(a) : z.g(a);
(x ? y.f() : z.g())(a);
(x ? y.f : z.g).u(a);
(x ? y.f() : z.g()).u(a);
}
}
ternary_boolean_consequent: { ternary_boolean_consequent: {
options = { options = {
booleans: true, booleans: true,
@@ -1137,7 +1159,7 @@ issue_1645_2: {
expect_stdout: true expect_stdout: true
} }
condition_symbol_matches_consequent: { condition_matches_consequent: {
options = { options = {
conditionals: true, conditionals: true,
} }
@@ -1166,6 +1188,35 @@ condition_symbol_matches_consequent: {
expect_stdout: "3 7 true 4" expect_stdout: "3 7 true 4"
} }
condition_matches_alternative: {
options = {
conditionals: true,
}
input: {
function foo(x, y) {
return x.p ? y[0] : x.p;
}
function bar() {
return g ? h : g;
}
var g = 4;
var h = 5;
console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar());
}
expect: {
function foo(x, y) {
return x.p && y[0];
}
function bar() {
return g && h;
}
var g = 4;
var h = 5;
console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar());
}
expect_stdout: "null 0 false 5"
}
delete_conditional_1: { delete_conditional_1: {
options = { options = {
booleans: true, booleans: true,
@@ -1240,11 +1291,11 @@ issue_2535_1: {
expect: { expect: {
y(); y();
x() && y(); x() && y();
(x(), 1) && y(); x(), y();
x() && y(); x() && y();
x() && y(); x() && y();
x() && y(); x() && y();
(x(), 0) && y(); x();
} }
} }
@@ -1384,7 +1435,7 @@ hoist_decl: {
} }
expect: { expect: {
var a, b; var a, b;
x() ? y() : z(); (x() ? y : z)();
} }
} }
@@ -1666,3 +1717,130 @@ issue_3668: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
conditional_assignments_1: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a, b, c, d) {
a = b;
if (c) a = d;
return a;
}
function g(a, b, c, d) {
a = b;
if (c); else a = d;
return a;
}
console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL"));
}
expect: {
function f(a, b, c, d) {
return a = c ? d : b, a;
}
function g(a, b, c, d) {
return a = c ? b : d, a;
}
console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL"));
}
expect_stdout: "PASS PASS"
}
conditional_assignments_2: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f1(b, c, d) {
a = b;
if (c) a = d;
return a;
}
function f2(a, c, d) {
a = b;
if (c) a = d;
return a;
}
function f3(a, b, d) {
a = b;
if (c) a = d;
return a;
}
function f4(a, b, c) {
a = b;
if (c) a = d;
return a;
}
}
expect: {
function f1(b, c, d) {
return a = c ? d : b, a;
}
function f2(a, c, d) {
return a = b, c && (a = d), a;
}
function f3(a, b, d) {
return a = b, c && (a = d), a;
}
function f4(a, b, c) {
return a = b, c && (a = d), a;
}
}
}
conditional_assignments_3: {
options = {
conditionals: true,
sequences: true,
}
input: {
console.log(function(a, b) {
a = "PASS";
if (b) a = a;
return a;
}(0, 1));
}
expect: {
console.log(function(a, b) {
return a = "PASS", b && (a = a), a;
}(0, 1));
}
expect_stdout: "PASS"
}
issue_3808_1: {
options = {
conditionals: true,
side_effects: true,
}
input: {
var a;
a = "PASS", [] + "" && (a = "FAIL");
console.log(a);
}
expect: {
var a;
a = [] + "" ? "FAIL" : "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3808_2: {
options = {
conditionals: true,
side_effects: true,
}
input: {
var a;
console.log((a = "PASS", [] + "" && (a = "FAIL")), a);
}
expect: {
var a;
console.log((a = "PASS", [] + "" && (a = "FAIL")), a);
}
expect_stdout: " PASS"
}

View File

@@ -97,6 +97,66 @@ dead_code_constant_boolean_should_warn_more: {
node_version: "<=4" node_version: "<=4"
} }
trim_try: {
options = {
dead_code: true,
}
input: {
try {
var a;
} catch (e) {
console.log("FAIL");
} finally {
console.log(a);
}
}
expect: {
var a;
console.log(a);
}
expect_stdout: "undefined"
}
trim_finally_1: {
options = {
dead_code: true,
}
input: {
try {
console.log("PASS");
} finally {
var a;
}
}
expect: {
console.log("PASS");
var a;
}
expect_stdout: "PASS"
}
trim_finally_2: {
options = {
dead_code: true,
}
input: {
try {
console.log("PASS");
} catch (e) {
} finally {
var a;
}
}
expect: {
try {
console.log("PASS");
var a;
} catch (e) {
}
}
expect_stdout: "PASS"
}
try_catch_finally: { try_catch_finally: {
options = { options = {
conditionals: true, conditionals: true,
@@ -130,10 +190,7 @@ try_catch_finally: {
a = 3; a = 3;
console.log("PASS"); console.log("PASS");
}(); }();
try { console.log(a);
console.log(a);
} finally {
}
} }
expect_stdout: [ expect_stdout: [
"PASS", "PASS",
@@ -179,7 +236,7 @@ collapse_vars_lvalues_drop_assign: {
} }
} }
collapse_vars_misc1: { collapse_vars_misc: {
options = { options = {
collapse_vars: true, collapse_vars: true,
dead_code: true, dead_code: true,
@@ -892,3 +949,416 @@ issue_3578: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3830_1: {
options = {
dead_code: true,
}
input: {
var o = {
set p(v) {
o = o.p = o = v;
}
};
o.p = "PASS";
console.log(o);
}
expect: {
var o = {
set p(v) {
o = o.p = o = v;
}
};
o.p = "PASS";
console.log(o);
}
expect_stdout: "PASS"
}
issue_3830_2: {
options = {
dead_code: true,
}
input: {
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[a] = a = v;
}
};
o[a] = "PASS";
console.log(a);
}
expect: {
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[a] = a = v;
}
};
o[a] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_3: {
options = {
dead_code: true,
}
input: {
function f() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[f()] = a = v;
}
};
o[f()] = "PASS";
console.log(a);
}
expect: {
function f() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = o[f()] = a = v;
}
};
o[f()] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_4: {
options = {
dead_code: true,
}
input: {
function f() {
return o;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[a] = a = v;
}
};
f()[a] = "PASS";
console.log(a);
}
expect: {
function f() {
return o;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[a] = a = v;
}
};
f()[a] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_5: {
options = {
dead_code: true,
}
input: {
function f() {
return o;
}
function g() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[g()] = a = v;
}
};
f()[g()] = "PASS";
console.log(a);
}
expect: {
function f() {
return o;
}
function g() {
return a;
}
var a = "FAIL";
var o = {
set FAIL(v) {
a = f()[g()] = a = v;
}
};
f()[g()] = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
issue_3830_6: {
options = {
dead_code: true,
}
input: {
function f() {
return o;
}
function g() {
return a;
}
function h(v) {
a = f()[g()] = a = v;
}
var a = "FAIL";
var o = {
set FAIL(v) {
h(v);
}
};
o.FAIL = "PASS";
console.log(a);
}
expect: {
function f() {
return o;
}
function g() {
return a;
}
function h(v) {
a = f()[g()] = a = v;
}
var a = "FAIL";
var o = {
set FAIL(v) {
h(v);
}
};
o.FAIL = "PASS";
console.log(a);
}
expect_stdout: "PASS"
}
redundant_assignments: {
options = {
dead_code: true,
}
input: {
var a = a = "PASS", b = "FAIL";
b = b = "PASS";
console.log(a, b);
}
expect: {
var a = "PASS", b = "FAIL";
b = "PASS";
console.log(a, b);
}
expect_stdout: "PASS PASS"
}
self_assignments_1: {
options = {
dead_code: true,
}
input: {
var a = "PASS";
a = a;
console.log(a);
}
expect: {
var a = "PASS";
a;
console.log(a);
}
expect_stdout: "PASS"
}
self_assignments_2: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "q", o = {
p: "PASS",
};
o.p = o.p;
o[a] = o[a];
console.log(o.p, o[a]);
}
expect: {
var a = "q", o = {
p: "PASS",
};
console.log(o.p, o[a]);
}
expect_stdout: "PASS undefined"
}
self_assignments_3: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "q", o = {
p: "FAIL",
get q() {
return "PASS";
},
set q(v) {
this.p = v;
},
};
o.p = o.p;
o[a] = o[a];
console.log(o.p, o[a]);
}
expect: {
var a = "q", o = {
p: "FAIL",
get q() {
return "PASS";
},
set q(v) {
this.p = v;
},
};
o.p = o.p;
o[a] = o[a];
console.log(o.p, o[a]);
}
expect_stdout: "PASS PASS"
}
self_assignments_4: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var i = 0, l = [ "PASS" ];
l[0] = l[0];
l[i] = l[i];
console.log(l[0], i);
}
expect: {
var i = 0, l = [ "PASS" ];
console.log(l[0], i);
}
expect_stdout: "PASS 0"
}
self_assignments_5: {
options = {
dead_code: true,
evaluate: true,
passes: 3,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var i = 0, l = [ "FAIL", "PASS" ];
l[0] = l[0];
l[i] = l[i];
l[i++] = l[i++];
console.log(l[0], i);
}
expect: {
var i = 0, l = [ "FAIL", "PASS" ];
l[0];
l[0];
l[0] = l[1];
console.log(l[0], 2);
}
expect_stdout: "PASS 2"
}
self_assignments_6: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var o = {
p: "PASS",
};
console.log(o.p = o.p);
}
expect: {
var o = {
p: "PASS",
};
console.log(o.p);
}
expect_stdout: "PASS"
}
issue_3967: {
options = {
dead_code: true,
}
input: {
var a = "FAIL";
try {
a = 0 in (a = "PASS");
} catch (e) {}
console.log(a);
}
expect: {
var a = "FAIL";
try {
a = 0 in (a = "PASS");
} catch (e) {}
console.log(a);
}
expect_stdout: "PASS"
}
issue_4051: {
options = {
dead_code: true,
}
input: {
try {
delete (A = A);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
delete (A = A);
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -46,7 +46,7 @@ simple_statement_is_not_a_directive: {
drop_lone_use_strict: { drop_lone_use_strict: {
options = { options = {
directives: true, directives: true,
side_effects: true, unused: true,
} }
input: { input: {
function f1() { function f1() {
@@ -66,10 +66,8 @@ drop_lone_use_strict: {
function f1() { function f1() {
} }
function f2() { function f2() {
"use strict";
function f3() {
}
} }
(function() {})();
} }
} }

View File

@@ -1730,7 +1730,7 @@ chained_3: {
expect: { expect: {
console.log(function(a, b) { console.log(function(a, b) {
var c = b; var c = b;
b++; +b;
return c; return c;
}(0, 2)); }(0, 2));
} }
@@ -1997,7 +1997,7 @@ issue_3146_4: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3192: { issue_3192_1: {
options = { options = {
unused: true, unused: true,
} }
@@ -2025,6 +2025,26 @@ issue_3192: {
] ]
} }
issue_3192_2: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
"use strict";
(function(a) {
console.log(a = "foo", arguments[0]);
})("bar");
}
expect: {
"use strict";
(function() {
console.log("foo", arguments[0]);
})("bar");
}
expect_stdout: "foo bar"
}
issue_3233: { issue_3233: {
options = { options = {
pure_getters: "strict", pure_getters: "strict",
@@ -2161,8 +2181,7 @@ issue_3515_1: {
expect: { expect: {
var c = 0; var c = 0;
(function() { (function() {
this[c++] = 0; for (var key20 in !(this[c++] = 0));
for (var key20 in !0);
})(); })();
console.log(c); console.log(c);
} }
@@ -2285,7 +2304,7 @@ issue_3598: {
try { try {
(function() { (function() {
a = "PASS"; a = "PASS";
var c = (void (c.p = 0))[!1]; (void ((void 0).p = 0))[!1];
})(); })();
} catch (e) {} } catch (e) {}
console.log(a); console.log(a);
@@ -2366,7 +2385,7 @@ function_parameter_ie8: {
} }
expect: { expect: {
(function() { (function() {
(function f() { (function() {
console.log("PASS"); console.log("PASS");
})(); })();
})(); })();
@@ -2413,3 +2432,656 @@ issue_3673: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3746: {
options = {
keep_fargs: "strict",
side_effects: true,
unused: true,
}
input: {
try {
A;
} catch (e) {
var e;
}
(function f(a) {
e = a;
})();
console.log("PASS");
}
expect: {
try {
A;
} catch (e) {
var e;
}
(function(a) {
e = a;
})();
console.log("PASS");
}
expect_stdout: "PASS"
}
drop_duplicated_side_effects: {
options = {
toplevel: true,
unused: true,
}
input: {
var a = 0;
for (var i = 1; i--;)
var a = 0, b = ++a;
console.log(a);
}
expect: {
var a = 0;
for (var i = 1; i--;)
a = 0, ++a;
console.log(a);
}
expect_stdout: "1"
}
drop_duplicated_var_catch: {
options = {
unused: true,
}
input: {
function f() {
try {
x();
} catch (a) {
var a, a;
}
}
}
expect: {
function f() {
try {
x();
} catch (a) {
var a;
}
}
}
}
issue_3802_1: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a += 0;
var a = function() {};
console.log(typeof a);
}
expect: {
var a = 0;
a += 0;
a = function() {};
console.log(typeof a);
}
expect_stdout: "function"
}
issue_3802_2: {
options = {
functions: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a += 0;
var a = function() {};
console.log(typeof a);
}
expect: {
0;
var a = function() {};
console.log(typeof a);
}
expect_stdout: "function"
}
issue_3899: {
options = {
assignments: true,
evaluate: true,
functions: true,
inline: true,
join_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a = a + 1;
var a = function f(b) {
return function() {
return b;
};
}(2);
console.log(typeof a);
}
expect: {
function a() {
return 2;
}
console.log(typeof a);
}
expect_stdout: "function"
}
cross_scope_assign_chain: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a, b = 0;
(function() {
a = b;
a++;
while (b++);
})();
console.log(a ? "PASS" : "FAIL");
}
expect: {
var a, b = 0;
(function() {
a = b;
a++;
while (b++);
})();
console.log(a ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
assign_if_assign_read: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
var b;
do {
b = "FAIL";
if (Array.isArray(a)) {
b = a[0];
console.log(b);
}
} while (!console);
})([ "PASS" ]);
}
expect: {
(function(a) {
var b;
do {
"FAIL";
if (Array.isArray(a)) {
b = a[0];
console.log(b);
}
} while (!console);
})([ "PASS" ]);
}
expect_stdout: "PASS"
}
issue_3951: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = console.log("PASS");
console.log(a);
a = "0";
console.log(a.p = 0);
a && a;
}
expect: {
var a = console.log("PASS");
console.log(a);
a = "0";
console.log(a.p = 0);
}
expect_stdout: [
"PASS",
"undefined",
"0",
]
}
issue_3956: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(function(a) {
function f(b) {
console.log(b);
a = 1;
}
var c = f(c += 0);
(function(d) {
console.log(d);
})(console.log(a) ^ 1, c);
})();
}
expect: {
var c, d;
c += 0,
console.log(NaN),
d = 1 ^ console.log(1),
console.log(d);
}
expect_stdout: [
"NaN",
"1",
"1",
]
}
issue_3962_1: {
options = {
evaluate: true,
keep_fargs: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
function f(b, c) {
do {
var d = console + e, e = 0..toString() === b;
} while (0);
if (c) console.log("PASS");
}
var a = f(a--, 1);
a;
}
expect: {
var a = 0;
a = (function(c) {
do {
console;
0..toString();
} while (0);
if (c) console.log("PASS");
}(1), 0);
void 0;
}
expect_stdout: "PASS"
}
issue_3962_2: {
options = {
keep_fargs: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
function f(b, c) {
do {
var d = console + e, e = 0..toString() === b;
} while (0);
if (c) console.log("PASS");
}
var a = f(a--, 1);
a;
}
expect: {
var a = 0;
a = (function(c) {
do {
console;
0..toString();
} while (0);
if (c) console.log("PASS");
}(1), 0);
}
expect_stdout: "PASS"
}
issue_3986: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0, b = 0;
(function() {
try {
throw 42;
} catch (e) {
a++;
}
b = b && 0;
})(b *= a);
console.log(b);
}
expect: {
var a = 0, b = 0;
(function() {
try {
throw 42;
} catch (e) {
a++;
}
b = b && 0;
})(b *= a);
console.log(b);
}
expect_stdout: "0"
}
issue_4017: {
options = {
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
var a = 0;
console.log(function f() {
var b = c &= 0;
var c = a++ + (A = a);
var d = c && c[f];
}());
}
expect: {
var a = 0;
console.log(function() {
c &= 0;
var c = (a++, A = a, 0);
}());
}
expect_stdout: "undefined"
}
issue_4025: {
options = {
collapse_vars: true,
evaluate: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0, b = 0, c = 0, d = a++;
try {
var e = console.log(c), f = b;
} finally {
var d = b = 1, d = c + 1;
c = 0;
}
console.log(a, b, d);
}
expect: {
var c = 0;
try {
console.log(c);
} finally {
var d = c + 1;
c = 0;
}
console.log(1, 1, d);
}
expect_stdout: [
"0",
"1 1 1",
]
}
forin_var_1: {
options = {
unused: true,
}
input: {
var k;
for (k in [ 1, 2 ])
console.log(k);
for (k in { PASS: 3 })
console.log(k);
console.log(k);
}
expect: {
for (var k in [ 1, 2 ])
console.log(k);
for (k in { PASS: 3 })
console.log(k);
console.log(k);
}
expect_stdout: [
"0",
"1",
"PASS",
"PASS",
]
}
forin_var_2: {
options = {
unused: true,
}
input: {
console.log(function() {
switch (0) {
case function() {
for (a in 0);
}:
var b = 0;
}
for (var c = 0; a;);
var a;
}());
}
expect: {
console.log(function() {
switch (0) {
case function() {
for (a in 0);
}:
}
for (; a;);
var a;
}());
}
expect_stdout: "undefined"
}
issue_4133: {
options = {
evaluate: true,
merge_vars: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
var b = [ a-- ], c = b && b[c];
console.log(a);
}
expect: {
var b = 1;
console.log(0);
}
expect_stdout: "0"
}
issue_4144: {
options = {
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
(function(a, b) {
var b = console, c = ++b;
})(console.log("PASS"), 0);
}
expect: {
(function(b) {
b = console,
++b;
})(console.log("PASS"));
}
expect_stdout: "PASS"
}
issue_4146: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
function g() {}
var a = g;
var c = b;
c.p;
console.log(typeof a);
}
f("FAIL", 42);
}
expect: {
(function(a, b) {
a = function () {};
var c = b;
c.p;
console.log(typeof a);
})(0, 42);
}
expect_stdout: "function"
}
var_catch_redefined: {
options = {
toplevel: true,
unused: true,
}
input: {
var a = "FAIL";
try {
throw "PASS";
} catch (a) {
function f() {
return a;
}
console.log(a);
}
f();
}
expect: {
var a = "FAIL";
try {
throw "PASS";
} catch (a) {
function f() {
return a;
}
console.log(a);
}
f();
}
expect_stdout: "PASS"
}
single_use_catch_redefined: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
issue_4184: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = function() {}, b = [ a, 1 && b, a = {} ];
try {
throw 42;
} catch (a) {
{
console.log(a);
}
}
})();
}
expect: {
(function() {
var b = [ function() {}, 1 && b, {} ];
try {
throw 42;
} catch (a) {
console.log(a);
}
})();
}
expect_stdout: "42"
}

View File

@@ -558,34 +558,51 @@ unsafe_array: {
unsafe: true, unsafe: true,
} }
input: { input: {
console.log( var a = "PASS";
[1, , 3][1], Array.prototype[1] = a;
[1, 2, 3, a] + 1, console.log([, ].length);
[1, 2, 3, 4] + 1, console.log("" + [, , ]);
[1, 2, 3, a][0] + 1, console.log([1, , 3][1]);
[1, 2, 3, 4][0] + 1, console.log([1, 2, 3, a] + 1);
[1, 2, 3, 4][6 - 5] + 1, console.log([1, 2, 3, 4] + 1);
[1, , 3, 4][6 - 5] + 1, console.log([1, 2, 3, a][0] + 1);
[[1, 2], [3, 4]][0] + 1, console.log([1, 2, 3, 4][0] + 1);
[[1, 2], [3, 4]][6 - 5][1] + 1, console.log([1, 2, 3, 4][6 - 5] + 1);
[[1, 2], , [3, 4]][6 - 5][1] + 1 console.log([1, , 3, 4][6 - 5] + 1);
); console.log([[1, 2], [3, 4]][0] + 1);
console.log([[1, 2], [3, 4]][6 - 5][1] + 1);
console.log([[1, 2], , [3, 4]][6 - 5][1] + 1);
} }
expect: { expect: {
console.log( var a = "PASS";
void 0, Array.prototype[1] = a;
[1, 2, 3, a] + 1, console.log([, ].length);
"1,2,3,41", console.log("" + [, , ]);
[1, 2, 3, a][0] + 1, console.log([1, , 3][1]);
2, console.log([1, 2, 3, a] + 1);
3, console.log("1,2,3,41");
NaN, console.log([1, 2, 3, a][0] + 1);
"1,21", console.log(2);
5, console.log(3);
(void 0)[1] + 1 console.log([1, , 3, 4][1] + 1);
); console.log("1,21");
console.log(5);
console.log([[1, 2], , [3, 4]][1][1] + 1);
} }
expect_stdout: true expect_stdout: [
"1",
",PASS",
"PASS",
"1,2,3,PASS1",
"1,2,3,41",
"2",
"2",
"3",
"PASS1",
"1,21",
"5",
"A1",
]
} }
unsafe_string: { unsafe_string: {
@@ -1579,9 +1596,9 @@ issue_2968_1: {
expect: { expect: {
var c = "FAIL"; var c = "FAIL";
(function() { (function() {
b = -(a = 42), a = 42,
void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0)); void ((a <<= 0) && (a[(c = "PASS", 0)] = 0));
var a, b; var a;
})(); })();
console.log(c); console.log(c);
} }
@@ -2161,3 +2178,839 @@ collapse_vars_regexp: {
"abbb", "abbb",
] ]
} }
issue_3738: {
options = {
evaluate: true,
}
input: {
console.log(1 / (0 + ([] - 1) % 1));
}
expect: {
console.log(1 / (0 + ([] - 1) % 1));
}
expect_stdout: "Infinity"
}
issue_3755: {
options = {
booleans: true,
evaluate: true,
unsafe: true,
unsafe_math: true,
}
input: {
console.log((/4/.exec(1 + (!0 - 5 / "23")) || 0).p);
}
expect: {
console.log((/4/.exec(!0 - 5 / "23" + 1), 0).p);
}
expect_stdout: "undefined"
}
void_side_effects: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = void console.log("PASS");
console.log(a);
}
expect: {
console.log("PASS");
console.log(void 0);
}
expect_stdout: [
"PASS",
"undefined",
]
}
no_returns: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect: {
(function() {
console.log("PASS");
})();
console.log(void 0);
}
expect_stdout: [
"PASS",
"undefined",
]
}
void_returns: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
function g(b) {
if (b) console.log("FAIL");
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g(e);
}
}
}();
console.log(a);
}
expect: {
(function() {
function g(b) {
if (b) console.log("FAIL");
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g(e);
}
}
})();
console.log(void 0);
}
expect_stdout: [
"PASS",
"undefined",
]
}
void_returns_recursive: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
function g(b) {
return f();
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g(e);
}
}
}();
console.log(a);
}
expect: {
var a = function f() {
function g(b) {
return f();
}
while (1) {
console.log("PASS");
try {
if (console) return;
} catch (e) {
return g();
}
}
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
issue_3878_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var b = function(a) {
return (a = 0) == (a && this > (a += 0));
}();
console.log(b ? "PASS" : "FAIL");
}
expect: {
console.log(true ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_3878_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = "foo";
a++ + a;
a && a;
console.log(a);
}
expect: {
var a = "foo";
a++ + a;
a;
console.log(a);
}
expect_stdout: "NaN"
}
issue_3882: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
return console.log(a++), a && this;
}
var b = f();
console.log(b);
}
expect: {
var b = function(a) {
return console.log(a++), a && this;
}();
console.log(b);
}
expect_stdout: [
"NaN",
"NaN",
]
}
issue_3887: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function(b) {
try {
b-- && console.log("PASS");
} catch (a_2) {}
})(1);
}
expect: {
(function(b) {
try {
1, console.log("PASS");
} catch (a_2) {}
})();
}
expect_stdout: "PASS"
}
issue_3903: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f(b, c) {
return console, c;
}
var d = f(f(), a = a);
console.log(d);
}
expect: {
function f(b, c) {
return console, c;
}
f(f(), "PASS");
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_3905: {
options = {
evaluate: true,
passes: 2,
unused: true,
}
input: {
(function(a, a) {
return console.log(a = 0), a && console.log("FAIL");
})("foo", 42);
}
expect: {
(function(a, a) {
return console.log(a = 0), a && console.log("FAIL");
})("foo", 42);
}
expect_stdout: "0"
}
issue_3920: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function(b) {
return (b[b = 0] = 0) >= (b ? 0 : 1);
}("foo");
console.log(a);
}
expect: {
(function(b) {
"foo"[0] = 0;
})();
console.log(false);
}
expect_stdout: "false"
}
inlined_increment_prefix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0;
(function() {
++a;
})();
console.log(a += 0);
}
expect: {
var a = 0;
void ++a;
console.log(1);
}
expect_stdout: "1"
}
inlined_increment_postfix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0;
(function() {
a++;
})();
console.log(a += 0);
}
expect: {
var a = 0;
void a++;
console.log(1);
}
expect_stdout: "1"
}
compound_assignment_to_property: {
options = {
evaluate: true,
unsafe: true,
}
input: {
1 + (0..p >>= 0) && console.log("PASS");
}
expect: {
1 + (0..p >>= 0),
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_2208_assign: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect_stdout: "42"
}
issue_2208_postfix: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
a = 41;
a++;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 41;
a++;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect_stdout: "42"
}
issue_2208_prefix: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
a = 43;
--a;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 43;
--a;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect_stdout: "42"
}
issue_3933: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a, b) {
a && (b ^= 1) && console.log("PASS");
})(1);
}
expect: {
(function(a, b) {
1, 1, console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_3935: {
options = {
evaluate: true,
}
input: {
console.log(function f(a) {
return a++;
}());
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}
issue_3937: {
options = {
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a = 123;
(a++ + (b = a))[b] ? 0 ? a : b : 0 ? a : b;
console.log(a, b);
}
expect: {
var a = 123;
(a++ + (b = a))[b], 0, b;
console.log(a, b);
}
expect_stdout: "124 124"
}
issue_3944: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
function f() {
while (function() {
var a = 0 == (b && b.p), b = console.log(a);
}());
f;
}
f();
})();
}
expect: {
void function f() {
while (a = 0 == (a = void 0), console.log(a), void 0);
var a;
f;
}();
}
expect_stdout: "false"
}
issue_3953: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
function f(a) {
(a += 0 * (a = 0)) && console.log("PASS");
}
f(1);
}
expect: {
function f(a) {
(a += 0 * (a = 0)) && console.log("PASS");
}
f(1);
}
expect_stdout: "PASS"
}
issue_3988: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function f(b) {
return ("" + (b &= 0))[b && this];
}
var a = f();
console.log(a);
}
expect: {
var a = function(b) {
return ("" + (b &= 0))[b && this];
}();
console.log(a);
}
expect_stdout: "0"
}
operator_in: {
options = {
evaluate: true,
unsafe: true,
}
input: {
Object.prototype.PASS = 0;
console.log(0 in [ 1 ]);
console.log(0 in [ , ]);
console.log(0 / 0 in { NaN: 2 });
console.log("PASS" in { });
console.log("FAIL" in { });
console.log("toString" in { });
console.log("toString" in { toString: 3 });
}
expect: {
Object.prototype.PASS = 0;
console.log(true);
console.log(0 in [ , ]);
console.log(true);
console.log("PASS" in { });
console.log("FAIL" in { });
console.log("toString" in { });
console.log(true);
}
expect_stdout: [
"true",
"false",
"true",
"true",
"false",
"true",
"true",
]
}
issue_3997: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = function f(b) {
return b[b += this] = b;
}(0);
console.log(typeof a);
}
expect: {
var a = function f(b) {
return b[b += this] = b;
}(0);
console.log(typeof a);
}
expect_stdout: "string"
}
issue_4035: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var a = 0;
(function() {
var b = --a;
console.log(delete (0 + b));
console.log(delete (1 * b));
console.log(delete (b + 0));
console.log(delete (b - 0));
console.log(delete (b / 1));
})();
}
expect: {
var a = 0;
(function() {
var b = --a;
console.log((0 + b, true));
console.log((1 * b, true));
console.log((0 + b, true));
console.log((b - 0, true));
console.log((b / 1, true));
})();
}
expect_stdout: [
"true",
"true",
"true",
"true",
"true",
]
}
issue_4067: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function(a) {
(function(b) {
b[0] += 0;
console.log(+a);
})(a);
})([]);
}
expect: {
(function(a) {
(function(b) {
b[0] += 0;
console.log(+a);
})(a);
})([]);
}
expect_stdout: "NaN"
}
issue_4077: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log((a = []) - (a[0]++, 1) || "PASS");
}
expect: {
console.log((a = []) - (a[0]++, 1) || "PASS");
}
expect_stdout: "PASS"
}
issue_4119_1: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a, b;
b = a = [];
a[0] += 0;
if (+b + 1) {
console.log("FAIL");
} else {
console.log("PASS");
}
}
expect: {
var a, b;
b = a = [];
a[0] += 0;
+b + 1 ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4119_2: {
options = {
conditionals: true,
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
var a;
(function(b) {
a[0] += 0;
console.log(+b + 1 ? "FAIL" : "PASS");
})(a = []);
}
expect: {
var a;
(function(b) {
a[0] += 0;
console.log(+b + 1 ? "FAIL" : "PASS");
})(a = []);
}
expect_stdout: "PASS"
}
issue_4119_3: {
options = {
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log(b.p ? "FAIL" : "PASS");
}
expect: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log(b.p ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
issue_4119_4: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log(!b ? "FAIL" : "PASS");
}
expect: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log((b, 0, "PASS"));
}
expect_stdout: "PASS"
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,20 @@ must_replace: {
} }
} }
repeated_nodes: {
options = {
global_defs: {
"@N": "rand()",
},
}
input: {
console.log(N, N);
}
expect: {
console.log(rand(), rand());
}
}
keyword: { keyword: {
options = { options = {
global_defs: { global_defs: {

View File

@@ -767,18 +767,17 @@ issue_3071_1: {
var obj = {}; var obj = {};
obj.one = 1; obj.one = 1;
obj.two = 2; obj.two = 2;
console.log(obj.one); console.log(obj.one, obj.two);
})(); })();
} }
expect: { expect: {
console.log(1); console.log(1, 2);
} }
expect_stdout: "1" expect_stdout: "1 2"
} }
issue_3071_2: { issue_3071_2: {
options = { options = {
evaluate: true,
hoist_props: true, hoist_props: true,
inline: true, inline: true,
join_vars: true, join_vars: true,
@@ -793,19 +792,18 @@ issue_3071_2: {
obj = {}; obj = {};
obj.one = 1; obj.one = 1;
obj.two = 2; obj.two = 2;
console.log(obj.one); console.log(obj.one, obj.two);
var obj; var obj;
})(); })();
} }
expect: { expect: {
console.log(1); console.log(1, 2);
} }
expect_stdout: "1" expect_stdout: "1 2"
} }
issue_3071_2_toplevel: { issue_3071_2_toplevel: {
options = { options = {
evaluate: true,
hoist_props: true, hoist_props: true,
inline: true, inline: true,
join_vars: true, join_vars: true,
@@ -821,14 +819,14 @@ issue_3071_2_toplevel: {
obj = {}; obj = {};
obj.one = 1; obj.one = 1;
obj.two = 2; obj.two = 2;
console.log(obj.one); console.log(obj.one, obj.two);
var obj; var obj;
})(); })();
} }
expect: { expect: {
console.log(1); console.log(1, 2);
} }
expect_stdout: "1" expect_stdout: "1 2"
} }
issue_3071_3: { issue_3071_3: {
@@ -914,3 +912,132 @@ issue_3440: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3868: {
options = {
hoist_props: true,
passes: 2,
reduce_vars: true,
side_effects: true,
}
input: {
(function(t) {
t = {};
({
get p() {},
q: (console.log("PASS"), +t),
}).r;
})();
}
expect: {
(function(t) {
t = {};
({
get p() {},
q: (console.log("PASS"), +t),
}).r;
})();
}
expect_stdout: "PASS"
}
issue_3871: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
console.log(function() {
do {
var b = {
get null() {
c;
}
};
} while (!b);
return "PASS";
}());
}
expect: {
console.log(function() {
do {
var b = {
get null() {
c;
}
};
} while (!b);
return "PASS";
}());
}
expect_stdout: "PASS"
}
issue_3945_1: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
function f() {
o.p;
var o = {
q: 0,
};
}
}
expect: {
function f() {
o.p;
var o = {
q: 0,
};
}
}
}
issue_3945_2: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
console.log(typeof o);
var o = {
p: 0,
};
}
expect: {
console.log(typeof o);
var o = {
p: 0,
};
}
expect_stdout: "undefined"
}
issue_4023: {
options = {
comparisons: true,
hoist_props: true,
inline: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function f() {
var a = function() {
return { p: 0 };
}();
return console.log("undefined" != typeof a);
}
f();
}
expect: {
console.log(void 0 !== {});
}
expect_stdout: "true"
}

View File

@@ -2223,13 +2223,13 @@ issue_3523_rename_ie8: {
expect: { expect: {
var a = 0, b, c = "FAIL"; var a = 0, b, c = "FAIL";
(function() { (function() {
var c, n, t, o, a, r, f, i, u, e, h, l, v, y; var c, n, t, o, a, r, e, f, i, u, h, l, v, y;
})(); })();
try { try {
throw 0; throw 0;
} catch (e) { } catch (e) {
(function() { (function() {
(function n() { (function e() {
c = "PASS"; c = "PASS";
})(); })();
})(); })();
@@ -2389,7 +2389,7 @@ issue_3703: {
var a = "PASS"; var a = "PASS";
(function() { (function() {
var b; var b;
var c = function g() { var c = function() {
a = "FAIL"; a = "FAIL";
}; };
a ? b |= c : b.p; a ? b |= c : b.p;
@@ -2398,3 +2398,464 @@ issue_3703: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3750: {
options = {
evaluate: true,
ie8: true,
}
input: {
(function(a) {
return function a() {
return a && console.log("PASS");
}();
})();
}
expect: {
(function(a) {
return function a() {
return a && console.log("PASS");
}();
})();
}
expect_stdout: "PASS"
}
issue_3823: {
options = {
ie8: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 1; i++) {
var a = a ? function f() {
f;
} : 0;
console.log("PASS", typeof f);
}
}
expect: {
for (var i = 0; i < 1; i++) {
(function f() {
f;
});
console.log("PASS", typeof f);
}
}
expect_stdout: "PASS undefined"
}
issue_3825: {
options = {
ie8: true,
pure_getters: "strict",
side_effects: true,
}
input: {
console.log({}[void (0..length ? 1 : 2)]);
}
expect: {
console.log({}[void 0]);
}
expect_stdout: "undefined"
}
issue_3889: {
options = {
evaluate: true,
ie8: true,
reduce_vars: true,
}
input: {
function f(a) {
a = 0;
(function a() {
var a;
console.log(a);
})();
}
f();
}
expect: {
function f(a) {
a = 0;
(function a() {
var a;
console.log(a);
})();
}
f();
}
expect_stdout: "undefined"
}
issue_3918: {
options = {
conditionals: true,
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
}
input: {
if (console.log("PASS")) {
var a = function f() {
f.p;
try {
console.log("FAIL");
} catch (e) {}
}, b = a;
}
}
expect: {
var a;
console.log("PASS") && (a = function f() {
f.p;
try {
console.log("FAIL");
} catch (o) {}
}, a);
}
expect_stdout: "PASS"
}
issue_3999: {
rename = true
mangle = {
ie8: true,
}
input: {
(function() {
(function f() {
for (var i = 0; i < 2; i++)
try {
f[0];
} catch (f) {
var f = 0;
console.log(i);
}
})();
})(typeof f);
}
expect: {
(function() {
(function f() {
for (var o = 0; o < 2; o++)
try {
f[0];
} catch (f) {
var f = 0;
console.log(o);
}
})();
})(typeof f);
}
expect_stdout: [
"0",
"1",
]
}
issue_4001: {
options = {
collapse_vars: true,
ie8: true,
inline: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
console.log(function(a) {
function f() {
return a;
var b;
}
var c = f();
(function g() {
c[42];
f;
})();
(function a() {});
}(42));
}
expect: {
function f() {
return a;
}
var a;
console.log((a = 42, void f()[42], void function a() {}));
}
expect_stdout: "undefined"
}
issue_4015: {
rename = true
mangle = {
ie8: true,
toplevel: true,
}
input: {
var n, a = 0, b;
function f() {
try {
throw 0;
} catch (b) {
(function g() {
(function b() {
a++;
})();
})();
}
}
f();
console.log(a);
}
expect: {
var n, o = 0, c;
function t() {
try {
throw 0;
} catch (c) {
(function n() {
(function c() {
o++;
})();
})();
}
}
t();
console.log(o);
}
expect_stdout: "1"
}
issue_4019: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = function() {
try {
console.log("FAIL");
} catch (b) {}
}, a = (console.log(a.length), ++a);
}
expect: {
var o = function() {
try {
console.log("FAIL");
} catch (o) {}
}, o = (console.log(o.length), ++o);
}
expect_stdout: "0"
}
issue_4028: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
}
input: {
function a() {
try {
A;
} catch (e) {}
}
var b = a += a;
console.log(typeof b);
}
expect: {
function a() {
try {
A;
} catch (a) {}
}
var b = a += a;
console.log(typeof b);
}
expect_stdout: "string"
}
issue_2737: {
options = {
ie8: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
a();
})(function f() {
console.log(typeof f);
});
}
expect: {
(function(a) {
a();
})(function f() {
console.log(typeof f);
});
}
expect_stdout: "function"
}
single_use_catch_redefined: {
options = {
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
single_use_inline_catch_redefined: {
options = {
ie8: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
direct_inline_catch_redefined: {
options = {
ie8: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, f(), g());
}
expect: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, a, g());
}
expect_stdout: true
}
issue_4186: {
options = {
dead_code: true,
evaluate: true,
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
function f() {
(function NaN() {
var a = 1;
while (a--)
try {} finally {
console.log(0/0);
var b;
}
})(f);
}
f();
NaN;
}
expect: {
(function() {
(function NaN() {
var n = 1;
while (n--)
console.log(0/0);
})();
})();
NaN;
}
expect_stdout: "NaN"
}

View File

@@ -573,3 +573,178 @@ issue_3600: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
iife_if_return_simple: {
options = {
conditionals: true,
if_return: true,
inline: true,
sequences: true,
side_effects: true,
}
input: {
(function() {
if (console)
return console.log("PASS");
console.log("FAIL");
})();
}
expect: {
console ? console.log("PASS") : console.log("FAIL");
}
expect_stdout: "PASS"
}
nested_if_break: {
options = {
if_return: true,
}
input: {
for (var i = 0; i < 3; i++)
L1: if ("number" == typeof i) {
if (0 === i) break L1;
console.log(i);
}
}
expect: {
for (var i = 0; i < 3; i++)
L1: if ("number" == typeof i)
if (0 !== i) console.log(i);
}
expect_stdout: [
"1",
"2",
]
}
nested_if_continue: {
options = {
conditionals: true,
if_return: true,
join_vars: true,
loops: true,
}
input: {
function f(n) {
var i = 0;
do {
if ("number" == typeof n) {
if (0 === n) {
console.log("even", i);
continue;
}
if (1 === n) {
console.log("odd", i);
continue;
}
i++;
}
} while (0 <= (n -= 2));
}
f(37);
f(42);
}
expect: {
function f(n) {
for (var i = 0;
"number" == typeof n
&& (0 !== n
? 1 !== n
? i++
: console.log("odd", i)
: console.log("even", i)),
0 <= (n -= 2););
}
f(37);
f(42);
}
expect_stdout: [
"odd 18",
"even 21",
]
}
nested_if_return: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f() {
if (A) {
if (B)
return B;
if (C)
return D;
if (E)
return F;
if (G)
return H;
if (I) {
if (J)
return K;
return;
}
if (L) {
if (M)
return;
return N;
}
}
}
}
expect: {
function f() {
if (A)
return B || (C ? D : E ? F : G ? H : I ? J ? K : void 0 : L && !M ? N : void 0);
}
}
}
issue_866_1: {
options = {
conditionals: true,
if_return: true,
sequences: false,
};
input: {
function f(a) {
if (a)
return "";
console.log(a);
}
}
expect: {
function f(a) {
if (a)
return "";
console.log(a);
}
}
}
issue_866_2: {
options = {
conditionals: true,
if_return: true,
sequences: true,
}
input: {
(function() {
if (a)
if (b)
c;
else
return d;
})();
}
expect: {
(function() {
if (a) {
if (!b)
return d;
c;
}
})();
}
}

View File

@@ -90,13 +90,13 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
"INFO: pass 0: last_count: Infinity, count: 37", "INFO: pass 0: last_count: Infinity, count: 36",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
"INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]", "INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
"INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]", "INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
"INFO: pass 1: last_count: 37, count: 18", "INFO: pass 1: last_count: 36, count: 18",
] ]
} }
@@ -248,13 +248,13 @@ non_hoisted_function_after_return_2a_strict: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
"INFO: pass 0: last_count: Infinity, count: 48", "INFO: pass 0: last_count: Infinity, count: 47",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
"INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]", "INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
"INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]", "INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
"INFO: pass 1: last_count: 48, count: 29", "INFO: pass 1: last_count: 47, count: 29",
] ]
} }

View File

@@ -16,7 +16,7 @@ multiple_functions: {
( function() { ( function() {
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ); if ( !window );
function f() {} function f() {}
function g() {} function g() {}
} )(); } )();
@@ -38,7 +38,7 @@ single_function: {
} }
expect: { expect: {
( function() { ( function() {
if ( window ); if ( !window );
function f() {} function f() {}
} )(); } )();
} }
@@ -67,7 +67,7 @@ deeply_nested: {
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ) if ( window )
if (document); if ( !document );
function f() {} function f() {}
function g() {} function g() {}
function h() {} function h() {}

View File

@@ -151,15 +151,18 @@ Infinity_not_in_with_scope: {
unused: true, unused: true,
} }
input: { input: {
var o = { Infinity: 'oInfinity' }; var o = { Infinity: "FAIL" };
var vInfinity = "Infinity"; var vInfinity = "Infinity";
vInfinity = Infinity; vInfinity = Infinity;
console.log(vInfinity);
} }
expect: { expect: {
var o = { Infinity: 'oInfinity' } var o = { Infinity: "FAIL" };
var vInfinity = "Infinity" var vInfinity = "Infinity";
vInfinity = 1/0 vInfinity = 1/0;
console.log(vInfinity);
} }
expect_stdout: "Infinity"
} }
Infinity_in_with_scope: { Infinity_in_with_scope: {
@@ -167,15 +170,18 @@ Infinity_in_with_scope: {
unused: true, unused: true,
} }
input: { input: {
var o = { Infinity: 'oInfinity' }; var o = { Infinity: "PASS" };
var vInfinity = "Infinity"; var vInfinity = "Infinity";
with (o) { vInfinity = Infinity; } with (o) { vInfinity = Infinity; }
console.log(vInfinity);
} }
expect: { expect: {
var o = { Infinity: 'oInfinity' } var o = { Infinity: "PASS" };
var vInfinity = "Infinity" var vInfinity = "Infinity";
with (o) vInfinity = Infinity with (o) vInfinity = Infinity;
console.log(vInfinity);
} }
expect_stdout: "PASS"
} }
assorted_Infinity_NaN_undefined_in_with_scope: { assorted_Infinity_NaN_undefined_in_with_scope: {

View File

@@ -22,8 +22,9 @@ issue_1639_1: {
console.log(a, b); console.log(a, b);
} }
expect: { expect: {
for (var a = 100, b = 10, L1 = 5; --L1 > 0;) for (var a = 100, b = 10, L1 = 5, ignore; --L1 > 0;) {
if (--b, 0) var ignore = 0; --b;
}
console.log(a, b); console.log(a, b);
} }
expect_stdout: "100 6" expect_stdout: "100 6"

View File

@@ -35,11 +35,7 @@ f7: {
console.log(a, b); console.log(a, b);
} }
expect_exact: [ expect_exact: [
"var b = 10;", "console.log(100, 100);",
"",
"!function() {",
" b = 100;",
"}(), console.log(100, b);",
] ]
expect_stdout: true expect_stdout: "100 100"
} }

View File

@@ -26,7 +26,7 @@ warn: {
}().length); }().length);
} }
expect_warnings: [ expect_warnings: [
"WARN: Function.prototype.caller not supported [test/compress/issue-2719.js:5,19]",
"WARN: Function.prototype.arguments not supported [test/compress/issue-2719.js:5,19]", "WARN: Function.prototype.arguments not supported [test/compress/issue-2719.js:5,19]",
"WARN: Function.prototype.caller not supported [test/compress/issue-2719.js:5,19]",
] ]
} }

128
test/compress/issue-3768.js Normal file
View File

@@ -0,0 +1,128 @@
mangle: {
mangle = {
toplevel: true,
}
input: {
var e = eval, x = 42;
(function() {
console.log(e("typeof x"));
})();
}
expect: {
var e = eval, x = 42;
(function() {
console.log(e("typeof x"));
})();
}
expect_stdout: true
}
compress: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
console.log(function() {
var a = 42;
return eval("typeof a");
}(), function(e) {
var a = null;
return e("typeof a");
}(eval), function(eval) {
var a = false;
return eval("typeof a");
}(eval), function(f) {
var a = "STRING";
var eval = f;
return eval("typeof a");
}(eval), function(g) {
var a = eval;
function eval() {
return g;
}
return eval()("typeof a");
}(eval));
}
expect: {
console.log(function() {
var a = 42;
return eval("typeof a");
}(), (0, eval)("typeof a"), function(eval) {
var a = false;
return eval("typeof a");
}(eval), function(f) {
var a = "STRING";
var eval = f;
return eval("typeof a");
}(eval), function(g) {
var a = eval;
function eval() {
return g;
}
return eval()("typeof a");
}(eval));
}
expect_stdout: "number undefined boolean string undefined"
}
call_arg_1: {
mangle = {
toplevel: true,
}
input: {
var z = "foo";
(function() {
var z = false;
(function(e) {
var z = 42;
e("console.log(typeof z)");
})(eval);
})();
}
expect: {
var z = "foo";
(function() {
var o = false;
(function(o) {
var a = 42;
o("console.log(typeof z)");
})(eval);
})();
}
expect_stdout: true
}
call_arg_2: {
mangle = {
toplevel: true,
}
input: {
function eval() {
console.log("PASS");
}
var z = "foo";
(function() {
var z = false;
(function(e) {
var z = 42;
e("console.log(typeof z)");
})(eval);
})();
}
expect: {
function n() {
console.log("PASS");
}
var o = "foo";
(function() {
var o = false;
(function(o) {
var n = 42;
o("console.log(typeof z)");
})(n);
})();
}
expect_stdout: "PASS"
}

View File

@@ -16,7 +16,6 @@ wrongly_optimized: {
function func() { function func() {
foo(); foo();
} }
// TODO: optimize to `func(), bar()` func(), 1, bar();
(func(), 1) && bar();
} }
} }

View File

@@ -84,6 +84,7 @@ wrongly_optimized: {
options = { options = {
booleans: true, booleans: true,
conditionals: true, conditionals: true,
dead_code: true,
evaluate: true, evaluate: true,
expression: true, expression: true,
} }
@@ -99,8 +100,8 @@ wrongly_optimized: {
function func() { function func() {
foo(); foo();
} }
// TODO: optimize to `func(), bar()` func(), 1;
if (func(), 1) bar(); bar();
} }
} }

View File

@@ -8,7 +8,7 @@ remove_sequence: {
(0, 1, _decorators.logThis)(); (0, 1, _decorators.logThis)();
} }
expect: { expect: {
eval(); (0, eval)();
logThis(); logThis();
(0, _decorators.logThis)(); (0, _decorators.logThis)();
} }

View File

@@ -53,20 +53,23 @@ this_binding_conditionals: {
this_binding_collapse_vars: { this_binding_collapse_vars: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
"use strict"; function f() {
var c = a; c(); "use strict";
var d = a.b; d(); var c = a; c();
var e = eval; e(); var d = a.b; d();
var e = eval; e();
}
} }
expect: { expect: {
"use strict"; function f() {
a(); "use strict";
(0, a.b)(); a();
(0, eval)(); (0, a.b)();
(0, eval)();
}
} }
} }
@@ -97,7 +100,7 @@ this_binding_side_effects: {
(function(foo) { (function(foo) {
foo(); foo();
(0, foo.bar)(); (0, foo.bar)();
eval("console.log(foo);"); (0, eval)("console.log(foo);");
}()); }());
(function(foo) { (function(foo) {
"use strict"; "use strict";
@@ -144,7 +147,7 @@ this_binding_sequences: {
return eval("this"); return eval("this");
}()), }()),
console.log(typeof function() { console.log(typeof function() {
return eval("this"); return (0, eval)("this");
}()), }()),
console.log(typeof function() { console.log(typeof function() {
"use strict"; "use strict";

1057
test/compress/join_vars.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -306,8 +306,6 @@ issue_2298: {
expect: { expect: {
!function() { !function() {
(function() { (function() {
var a = undefined;
var undefined = a++;
try { try {
!function() { !function() {
(void 0)[1] = "foo"; (void 0)[1] = "foo";
@@ -1226,8 +1224,7 @@ chained_3: {
} }
expect: { expect: {
console.log(function(b) { console.log(function(b) {
var c = 1; var c = 1, c = b;
c = b;
b++; b++;
return c; return c;
}(2)); }(2));

View File

@@ -3,6 +3,7 @@ labels_1: {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
if_return: true, if_return: true,
unused: true,
} }
input: { input: {
out: { out: {
@@ -21,6 +22,7 @@ labels_2: {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
if_return: true, if_return: true,
unused: true,
} }
input: { input: {
out: { out: {
@@ -61,6 +63,7 @@ labels_4: {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
if_return: true, if_return: true,
unused: true,
} }
input: { input: {
out: for (var i = 0; i < 5; ++i) { out: for (var i = 0; i < 5; ++i) {
@@ -105,6 +108,9 @@ labels_5: {
} }
labels_6: { labels_6: {
options = {
dead_code: true,
}
input: { input: {
out: break out; out: break out;
}; };
@@ -159,6 +165,7 @@ labels_9: {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
if_return: true, if_return: true,
unused: true,
} }
input: { input: {
out: while (foo) { out: while (foo) {

View File

@@ -201,7 +201,7 @@ evaluate: {
} }
} }
issue_1532: { issue_1532_1: {
options = { options = {
evaluate: true, evaluate: true,
loops: true, loops: true,
@@ -210,18 +210,56 @@ issue_1532: {
function f(x, y) { function f(x, y) {
do { do {
if (x) break; if (x) break;
foo(); console.log(y);
} while (false); } while (false);
} }
f(null, "PASS");
f(42, "FAIL");
}
expect: {
function f(x, y) {
for (; !x && (console.log(y), false););
}
f(null, "PASS");
f(42, "FAIL");
}
expect_stdout: "PASS"
}
issue_1532_2: {
options = {
evaluate: true,
loops: true,
}
input: {
function f(x, y) {
do {
if (x) {
console.log(x);
break;
}
console.log(y);
} while (false);
}
f(null, "PASS");
f(42, "FAIL");
} }
expect: { expect: {
function f(x, y) { function f(x, y) {
do { do {
if (x) break; if (x) {
foo(); console.log(x);
} while (false); break;
}
} while (console.log(y), false);
} }
f(null, "PASS");
f(42, "FAIL");
} }
expect_stdout: [
"PASS",
"42",
]
} }
issue_186: { issue_186: {
@@ -549,6 +587,7 @@ issue_2740_2: {
dead_code: true, dead_code: true,
loops: true, loops: true,
passes: 2, passes: 2,
unused: true,
} }
input: { input: {
L1: while (x()) { L1: while (x()) {
@@ -564,6 +603,7 @@ issue_2740_3: {
options = { options = {
dead_code: true, dead_code: true,
loops: true, loops: true,
unused: true,
} }
input: { input: {
L1: for (var x = 0; x < 3; x++) { L1: for (var x = 0; x < 3; x++) {
@@ -589,6 +629,7 @@ issue_2740_4: {
dead_code: true, dead_code: true,
loops: true, loops: true,
passes: 2, passes: 2,
unused: true,
} }
input: { input: {
L1: for (var x = 0; x < 3; x++) { L1: for (var x = 0; x < 3; x++) {
@@ -613,6 +654,7 @@ issue_2740_5: {
dead_code: true, dead_code: true,
loops: true, loops: true,
passes: 2, passes: 2,
unused: true,
} }
input: { input: {
L1: for (var x = 0; x < 3; x++) { L1: for (var x = 0; x < 3; x++) {
@@ -752,7 +794,37 @@ empty_for_in_side_effects: {
expect_warnings: [ expect_warnings: [
"WARN: Dropping unused variable b [test/compress/loops.js:4,16]", "WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
"INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]", "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
"WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]", "WARN: Side effects in object of for-in loop [test/compress/loops.js:2,17]",
]
}
empty_for_in_prop_init: {
options = {
loops: true,
pure_getters: "strict",
unused: true,
}
input: {
console.log(function f() {
var a = "bar";
for ((a, f)[a] in console.log("foo"));
return a;
}());
}
expect: {
console.log(function() {
var a = "bar";
console.log("foo");
return a;
}());
}
expect_stdout: [
"foo",
"bar",
]
expect_warnings: [
"INFO: Dropping unused loop variable f [test/compress/loops.js:3,21]",
"WARN: Side effects in object of for-in loop [test/compress/loops.js:3,30]",
] ]
} }
@@ -763,6 +835,7 @@ issue_3631_1: {
loops: true, loops: true,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
unused: true,
} }
input: { input: {
var c = 0; var c = 0;
@@ -789,6 +862,7 @@ issue_3631_2: {
loops: true, loops: true,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
unused: true,
} }
input: { input: {
L: for (var a = 1; a--; console.log(b)) { L: for (var a = 1; a--; console.log(b)) {
@@ -927,3 +1001,199 @@ issue_3634_2: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_4075: {
options = {
loops: true,
unused: true,
}
input: {
var a = "FAIL";
(function() {
for (a in { PASS: 0 });
})()
console.log(a);
}
expect: {
var a = "FAIL";
(function() {
for (a in { PASS: 0 });
})()
console.log(a);
}
expect_stdout: "PASS"
}
issue_4082: {
options = {
keep_fargs: "strict",
loops: true,
unused: true,
}
input: {
var a = "PASS";
(function(a) {
for (a in "foo")
var b;
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(a) {
for (a in "foo");
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_4084: {
options = {
keep_fargs: "strict",
loops: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
function f(a) {
var b = a++;
for (a in "foo");
}
f();
return typeof a;
}());
}
expect: {
console.log(function() {
(function() {
0;
})();
return typeof a;
}());
}
expect_stdout: "undefined"
}
issue_4091_1: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
try {
throw "FAIL";
} catch (e) {
for (var e in 42);
}
console.log(e && e);
}
expect: {
try {
throw "FAIL";
} catch (e) {
var e;
}
console.log(e && e);
}
expect_stdout: "undefined"
}
issue_4091_2: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
try {
throw "FAIL";
} catch (e) {
for (e in 42);
var e;
}
console.log(e && e);
}
expect: {
try {
throw "FAIL";
} catch (e) {
var e;
}
console.log(e && e);
}
expect_stdout: "undefined"
}
issue_4182_1: {
options = {
loops: true,
}
input: {
(function() {
do {
try {
return;
} finally {
continue;
}
console.log("FAIL");
} while (0);
console.log("PASS");
})();
}
expect: {
(function() {
do {
try {
return;
} finally {
continue;
}
console.log("FAIL");
} while (0);
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_4182_2: {
options = {
loops: true,
}
input: {
(function() {
L: do {
do {
try {
return;
} finally {
continue L;
}
console.log("FAIL");
} while (0);
console.log("FAIL");
} while (0);
console.log("PASS");
})();
}
expect: {
(function() {
L: do {
do {
try {
return;
} finally {
continue L;
}
} while (console.log("FAIL"), 0);
console.log("FAIL");
} while (0);
console.log("PASS");
})();
}
expect_stdout: "PASS"
}

3013
test/compress/merge_vars.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -91,7 +91,7 @@ evaluate_1: {
expect: { expect: {
console.log( console.log(
x + 1 + 2, x + 1 + 2,
2 * x, 2 * +x,
+x + 1 + 2, +x + 1 + 2,
1 + x + 2 + 3, 1 + x + 2 + 3,
3 | x, 3 | x,
@@ -130,7 +130,7 @@ evaluate_1_unsafe_math: {
expect: { expect: {
console.log( console.log(
x + 1 + 2, x + 1 + 2,
2 * x, 2 * +x,
+x + 3, +x + 3,
1 + x + 2 + 3, 1 + x + 2 + 3,
3 | x, 3 | x,
@@ -148,45 +148,52 @@ evaluate_1_unsafe_math: {
evaluate_2: { evaluate_2: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true,
unsafe_math: false, unsafe_math: false,
} }
input: { input: {
var x = "42", y = null; function f(num) {
[ var x = "" + num, y = null;
x + 1 + 2, [
x * 1 * 2, x + 1 + 2,
+x + 1 + 2, x * 1 * 2,
1 + x + 2 + 3, +x + 1 + 2,
1 | x | 2 | 3, 1 + x + 2 + 3,
1 + x-- + 2 + 3, 1 | x | 2 | 3,
1 + (x*y + 2) + 3, 1 + x-- + 2 + 3,
1 + (2 + x + 3), 1 + (x*y + 2) + 3,
1 + (2 + ~x + 3), 1 + (2 + x + 3),
-y + (2 + ~x + 3), 1 + (2 + ~x + 3),
1 & (2 & x & 3), -y + (2 + ~x + 3),
1 + (2 + (x |= 0) + 3), 1 & (2 & x & 3),
].forEach(function(n) { 1 + (2 + (x |= 0) + 3),
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect: { expect: {
var x = "42", y = null; function f(num) {
[ var x = "" + num, y = null;
x + 1 + 2, [
2 * x, x + "12",
+x + 1 + 2, 2 * x,
1 + x + 2 + 3, +x + 1 + 2,
3 | x, 1 + x + "23",
1 + x-- + 2 + 3, 3 | x,
x*y + 2 + 1 + 3, 1 + x-- + 2 + 3,
1 + (2 + x + 3), x*y + 2 + 1 + 3,
2 + ~x + 3 + 1, 2 + x + 3 + 1,
2 + ~x + 3 - y, 2 + ~x + 3 + 1,
0 & x, 2 + ~x + 3,
2 + (x |= 0) + 3 + 1, 0 & x,
].forEach(function(n) { 2 + (x |= 0) + 3 + 1,
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect_stdout: [ expect_stdout: [
"string 4212", "string 4212",
@@ -207,45 +214,52 @@ evaluate_2: {
evaluate_2_unsafe_math: { evaluate_2_unsafe_math: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true,
unsafe_math: true, unsafe_math: true,
} }
input: { input: {
var x = "42", y = null; function f(num) {
[ var x = "" + num, y = null;
x + 1 + 2, [
x * 1 * 2, x + 1 + 2,
+x + 1 + 2, x * 1 * 2,
1 + x + 2 + 3, +x + 1 + 2,
1 | x | 2 | 3, 1 + x + 2 + 3,
1 + x-- + 2 + 3, 1 | x | 2 | 3,
1 + (x*y + 2) + 3, 1 + x-- + 2 + 3,
1 + (2 + x + 3), 1 + (x*y + 2) + 3,
1 + (2 + ~x + 3), 1 + (2 + x + 3),
-y + (2 + ~x + 3), 1 + (2 + ~x + 3),
1 & (2 & x & 3), -y + (2 + ~x + 3),
1 + (2 + (x |= 0) + 3), 1 & (2 & x & 3),
].forEach(function(n) { 1 + (2 + (x |= 0) + 3),
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect: { expect: {
var x = "42", y = null; function f(num) {
[ var x = "" + num, y = null;
x + 1 + 2, [
2 * x, x + "12",
+x + 3, 2 * x,
1 + x + 2 + 3, +x + 3,
3 | x, 1 + x + "23",
6 + x--, 3 | x,
x*y + 6, 6 + x--,
1 + (2 + x + 3), x*y + 6,
6 + ~x, 6 + x,
5 + ~x - y, 6 + ~x,
0 & x, 5 + ~x,
6 + (x |= 0), 0 & x,
].forEach(function(n) { 6 + (x |= 0),
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect_stdout: [ expect_stdout: [
"string 4212", "string 4212",
@@ -310,45 +324,52 @@ evaluate_4: {
evaluate_5: { evaluate_5: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true,
unsafe_math: false, unsafe_math: false,
} }
input: { input: {
var a = "1"; function f(num) {
[ var a = "" + num;
+a + 2 + 3, [
+a + 2 - 3, +a + 2 + 3,
+a - 2 + 3, +a + 2 - 3,
+a - 2 - 3, +a - 2 + 3,
2 + +a + 3, +a - 2 - 3,
2 + +a - 3, 2 + +a + 3,
2 - +a + 3, 2 + +a - 3,
2 - +a - 3, 2 - +a + 3,
2 + 3 + +a, 2 - +a - 3,
2 + 3 - +a, 2 + 3 + +a,
2 - 3 + +a, 2 + 3 - +a,
2 - 3 - +a, 2 - 3 + +a,
].forEach(function(n) { 2 - 3 - +a,
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(1);
} }
expect: { expect: {
var a = "1"; function f(num) {
[ var a = "" + num;
+a + 2 + 3, [
+a + 2 - 3, +a + 2 + 3,
a - 2 + 3, +a + 2 - 3,
a - 2 - 3, a - 2 + 3,
+a + 2 + 3, a - 2 - 3,
+a + 2 - 3, +a + 2 + 3,
2 - a + 3, +a + 2 - 3,
2 - a - 3, 2 - a + 3,
+a + 5, 2 - a - 3,
5 - a, +a + 5,
+a - 1, 5 - a,
-1 - a, +a - 1,
].forEach(function(n) { -1 - a,
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(1);
} }
expect_stdout: [ expect_stdout: [
"number 6", "number 6",
@@ -369,45 +390,52 @@ evaluate_5: {
evaluate_5_unsafe_math: { evaluate_5_unsafe_math: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true,
unsafe_math: true, unsafe_math: true,
} }
input: { input: {
var a = "1"; function f(num) {
[ var a = "" + num;
+a + 2 + 3, [
+a + 2 - 3, +a + 2 + 3,
+a - 2 + 3, +a + 2 - 3,
+a - 2 - 3, +a - 2 + 3,
2 + +a + 3, +a - 2 - 3,
2 + +a - 3, 2 + +a + 3,
2 - +a + 3, 2 + +a - 3,
2 - +a - 3, 2 - +a + 3,
2 + 3 + +a, 2 - +a - 3,
2 + 3 - +a, 2 + 3 + +a,
2 - 3 + +a, 2 + 3 - +a,
2 - 3 - +a, 2 - 3 + +a,
].forEach(function(n) { 2 - 3 - +a,
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(1);
} }
expect: { expect: {
var a = "1"; function f(num) {
[ var a = "" + num;
+a + 5, [
+a + -1, +a + 5,
a - -1, +a + -1,
a - 5, a - -1,
+a + 5, a - 5,
+a + -1, +a + 5,
5 - a, +a + -1,
-1 - a, 5 - a,
+a + 5, -1 - a,
5 - a, +a + 5,
+a - 1, 5 - a,
-1 - a, +a - 1,
].forEach(function(n) { -1 - a,
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(1);
} }
expect_stdout: [ expect_stdout: [
"number 6", "number 6",
@@ -546,37 +574,44 @@ evaluate_6_unsafe_math: {
evaluate_7: { evaluate_7: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true,
unsafe_math: false, unsafe_math: false,
} }
input: { input: {
var x = "42", y; function f(num, y) {
[ var x = "" + num;
+x + 2 + (3 + !y), [
+x + 2 + (3 - !y), +x + 2 + (3 + !y),
+x + 2 - (3 + !y), +x + 2 + (3 - !y),
+x + 2 - (3 - !y), +x + 2 - (3 + !y),
+x - 2 + (3 + !y), +x + 2 - (3 - !y),
+x - 2 + (3 - !y), +x - 2 + (3 + !y),
+x - 2 - (3 + !y), +x - 2 + (3 - !y),
+x - 2 - (3 - !y), +x - 2 - (3 + !y),
].forEach(function(n) { +x - 2 - (3 - !y),
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect: { expect: {
var x = "42", y; function f(num, y) {
[ var x = "" + num;
+x + 2 + (3 + !y), [
+x + 2 + (3 - !y), +x + 2 + (3 + !y),
+x + 2 - (3 + !y), +x + 2 + (3 - !y),
+x + 2 - (3 - !y), +x + 2 - (3 + !y),
x - 2 + (3 + !y), +x + 2 - (3 - !y),
x - 2 + (3 - !y), x - 2 + (3 + !y),
x - 2 - (3 + !y), x - 2 + (3 - !y),
x - 2 - (3 - !y), x - 2 - (3 + !y),
].forEach(function(n) { x - 2 - (3 - !y),
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect_stdout: [ expect_stdout: [
"number 48", "number 48",
@@ -593,37 +628,44 @@ evaluate_7: {
evaluate_7_unsafe_math: { evaluate_7_unsafe_math: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true,
unsafe_math: true, unsafe_math: true,
} }
input: { input: {
var x = "42", y; function f(num, y) {
[ var x = "" + num;
+x + 2 + (3 + !y), [
+x + 2 + (3 - !y), +x + 2 + (3 + !y),
+x + 2 - (3 + !y), +x + 2 + (3 - !y),
+x + 2 - (3 - !y), +x + 2 - (3 + !y),
+x - 2 + (3 + !y), +x + 2 - (3 - !y),
+x - 2 + (3 - !y), +x - 2 + (3 + !y),
+x - 2 - (3 + !y), +x - 2 + (3 - !y),
+x - 2 - (3 - !y), +x - 2 - (3 + !y),
].forEach(function(n) { +x - 2 - (3 - !y),
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect: { expect: {
var x = "42", y; function f(num, y) {
[ var x = "" + num;
+x + 5 + !y, [
+x + 5 - !y, +x + 5 + !y,
+x + -1 - !y, +x + 5 - !y,
+x + -1 + !y, +x + -1 - !y,
x - -1 + !y, +x + -1 + !y,
x - -1 - !y, x - -1 + !y,
x - 5 - !y, x - -1 - !y,
x - 5 + !y, x - 5 - !y,
].forEach(function(n) { x - 5 + !y,
console.log(typeof n, n); ].forEach(function(n) {
}); console.log(typeof n, n);
});
}
f(42);
} }
expect_stdout: [ expect_stdout: [
"number 48", "number 48",
@@ -637,6 +679,22 @@ evaluate_7_unsafe_math: {
] ]
} }
evaluate_8_unsafe_math: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = [ "42" ];
console.log(a * (1 / 7));
}
expect: {
var a = [ "42" ];
console.log(+a / 7);
}
expect_stdout: "6"
}
NaN_redefined: { NaN_redefined: {
options = { options = {
evaluate: true, evaluate: true,
@@ -1251,3 +1309,29 @@ issue_3695: {
} }
expect_stdout: "NaN" expect_stdout: "NaN"
} }
issue_4137: {
options = {
evaluate: true,
}
input: {
console.log(+(A = []) * (A[0] = 1));
}
expect: {
console.log(+(A = []) * (A[0] = 1));
}
expect_stdout: "0"
}
issue_4142: {
options = {
evaluate: true,
}
input: {
console.log("" + +(0 === console));
}
expect: {
console.log("" + +(0 === console));
}
expect_stdout: "0"
}

View File

@@ -130,7 +130,7 @@ evaluate_string_length: {
} }
} }
mangle_properties: { mangle_properties_1: {
mangle = { mangle = {
properties: { properties: {
keep_quoted: false, keep_quoted: false,
@@ -152,6 +152,53 @@ mangle_properties: {
} }
} }
mangle_properties_2: {
mangle = {
properties: {
reserved: [
"value",
]
},
}
input: {
var o = {
prop1: 1,
};
Object.defineProperty(o, "prop2", {
value: 2,
});
Object.defineProperties(o, {
prop3: {
value: 3,
},
});
console.log("prop1", o.prop1, "prop1" in o);
console.log("prop2", o.prop2, o.hasOwnProperty("prop2"));
console.log("prop3", o.prop3, Object.getOwnPropertyDescriptor(o, "prop3").value);
}
expect: {
var o = {
o: 1,
};
Object.defineProperty(o, "p", {
value: 2,
});
Object.defineProperties(o, {
r: {
value: 3,
},
});
console.log("prop1", o.o, "o" in o);
console.log("prop2", o.p, o.hasOwnProperty("p"));
console.log("prop3", o.r, Object.getOwnPropertyDescriptor(o, "r").value);
}
expect_stdout: [
"prop1 1 true",
"prop2 2 true",
"prop3 3 3",
]
}
mangle_unquoted_properties: { mangle_unquoted_properties: {
options = { options = {
evaluate: true, evaluate: true,
@@ -1046,16 +1093,22 @@ array_hole: {
side_effects: true, side_effects: true,
} }
input: { input: {
console.log( Array.prototype[2] = "PASS";
[ 1, 2, , 3][1], console.log([ 1, 2, , 3 ][1]);
[ 1, 2, , 3][2], console.log([ 1, 2, , 3 ][2]);
[ 1, 2, , 3][3] console.log([ 1, 2, , 3 ][3]);
);
} }
expect: { expect: {
console.log(2, void 0, 3); Array.prototype[2] = "PASS";
console.log(2);
console.log([ , , , ][2]);
console.log(3);
} }
expect_stdout: "2 undefined 3" expect_stdout: [
"2",
"PASS",
"3",
]
} }
new_this: { new_this: {
@@ -1070,11 +1123,7 @@ new_this: {
} }
}.f(42); }.f(42);
} }
expect: { expect: {}
new function(a) {
this.a = a;
}(42);
}
} }
issue_2513: { issue_2513: {
@@ -1143,558 +1192,6 @@ const_prop_assign_pure: {
} }
} }
join_object_assignments_1: {
options = {
evaluate: true,
join_vars: true,
}
input: {
console.log(function() {
var x = {
a: 1,
c: (console.log("c"), "C"),
};
x.b = 2;
x[3] = function() {
console.log(x);
},
x["a"] = /foo/,
x.bar = x;
return x;
}());
}
expect: {
console.log(function() {
var x = {
a: 1,
c: (console.log("c"), "C"),
b: 2,
3: function() {
console.log(x);
},
a: /foo/,
};
x.bar = x;
return x;
}());
}
expect_stdout: true
}
join_object_assignments_2: {
options = {
evaluate: true,
hoist_props: true,
join_vars: true,
passes: 3,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
foo: 1,
};
o.bar = 2;
o.baz = 3;
console.log(o.foo, o.bar + o.bar, o.foo * o.bar * o.baz);
}
expect: {
console.log(1, 4, 6);
}
expect_stdout: "1 4 6"
}
join_object_assignments_3: {
options = {
evaluate: true,
join_vars: true,
}
input: {
console.log(function() {
var o = {
a: "PASS",
}, a = o.a;
o.a = "FAIL";
return a;
}());
}
expect: {
console.log(function() {
var o = {
a: "PASS",
}, a = o.a;
o.a = "FAIL";
return a;
}());
}
expect_stdout: "PASS"
}
join_object_assignments_4: {
options = {
join_vars: true,
sequences: true,
}
input: {
var o;
console.log(o);
o = {};
o.a = "foo";
console.log(o.b);
o.b = "bar";
console.log(o.a);
}
expect: {
var o;
console.log(o),
o = {
a: "foo",
},
console.log(o.b),
o.b = "bar",
console.log(o.a);
}
expect_stdout: [
"undefined",
"undefined",
"foo",
]
}
join_object_assignments_return_1: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = "foo";
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: "foo"
};
return o.q;
}());
}
expect_stdout: "foo"
}
join_object_assignments_return_2: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = /foo/,
o.r = "bar";
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: /foo/,
r: "bar"
};
return o.r;
}());
}
expect_stdout: "bar"
}
join_object_assignments_return_3: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = "foo",
o.p += "",
console.log(o.q),
o.p;
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: "foo"
};
return o.p += "",
console.log(o.q),
o.p;
}());
}
expect_stdout: [
"foo",
"3",
]
}
join_object_assignments_for: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
for (o.q = "foo"; console.log(o.q););
return o.p;
}());
}
expect: {
console.log(function() {
for (var o = {
p: 3,
q: "foo"
}; console.log(o.q););
return o.p;
}());
}
expect_stdout: [
"foo",
"3",
]
}
join_object_assignments_if: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {};
if (o.a = "PASS") return o.a;
}())
}
expect: {
console.log(function() {
var o = { a: "PASS" };
if (o.a) return o.a;
}());
}
expect_stdout: "PASS"
}
join_object_assignments_forin: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {};
for (var a in o.a = "PASS", o)
return o[a];
}())
}
expect: {
console.log(function() {
var o = { a: "PASS" };
for (var a in o)
return o[a];
}());
}
expect_stdout: "PASS"
}
join_object_assignments_negative: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[0] = 0;
o[-0] = 1;
o[-1] = 2;
console.log(o[0], o[-0], o[-1]);
}
expect: {
var o = {
0: 0,
0: 1,
"-1": 2
};
console.log(o[0], o[-0], o[-1]);
}
expect_stdout: "1 1 2"
}
join_object_assignments_NaN_1: {
options = {
join_vars: true,
}
input: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect_stdout: "2 2"
}
join_object_assignments_NaN_2: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect: {
var o = {
NaN: 1,
NaN: 2
};
console.log(o.NaN, o.NaN);
}
expect_stdout: "2 2"
}
join_object_assignments_null_0: {
options = {
join_vars: true,
}
input: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect_stdout: "1"
}
join_object_assignments_null_1: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect: {
var o = {
null: 1
};
console.log(o.null);
}
expect_stdout: "1"
}
join_object_assignments_void_0: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var o = {};
o[void 0] = 1;
console.log(o[void 0]);
}
expect: {
var o = {
undefined: 1
};
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_undefined_1: {
options = {
join_vars: true,
}
input: {
var o = {};
o[undefined] = 1;
console.log(o[undefined]);
}
expect: {
var o = {};
o[void 0] = 1;
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_undefined_2: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[undefined] = 1;
console.log(o[undefined]);
}
expect: {
var o = {
undefined : 1
};
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_Infinity: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[Infinity] = 1;
o[1/0] = 2;
o[-Infinity] = 3;
o[-1/0] = 4;
console.log(o[Infinity], o[1/0], o[-Infinity], o[-1/0]);
}
expect: {
var o = {
Infinity: 1,
Infinity: 2,
"-Infinity": 3,
"-Infinity": 4
};
console.log(o[1/0], o[1/0], o[-1/0], o[-1/0]);
}
expect_stdout: "2 2 4 4"
}
join_object_assignments_regex: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[/rx/] = 1;
console.log(o[/rx/]);
}
expect: {
var o = {
"/rx/": 1
};
console.log(o[/rx/]);
}
expect_stdout: "1"
}
issue_2816: {
options = {
join_vars: true,
}
input: {
"use strict";
var o = {
a: 1
};
o.b = 2;
o.a = 3;
o.c = 4;
console.log(o.a, o.b, o.c);
}
expect: {
"use strict";
var o = {
a: 1,
b: 2
};
o.a = 3;
o.c = 4;
console.log(o.a, o.b, o.c);
}
expect_stdout: "3 2 4"
}
issue_2893_1: {
options = {
join_vars: true,
}
input: {
var o = {
get a() {
return "PASS";
},
};
o.a = "FAIL";
console.log(o.a);
}
expect: {
var o = {
get a() {
return "PASS";
},
};
o.a = "FAIL";
console.log(o.a);
}
expect_stdout: "PASS"
}
issue_2893_2: {
options = {
join_vars: true,
}
input: {
var o = {
set a(v) {
this.b = v;
},
b: "FAIL",
};
o.a = "PASS";
console.log(o.b);
}
expect: {
var o = {
set a(v) {
this.b = v;
},
b: "FAIL",
};
o.a = "PASS";
console.log(o.b);
}
expect_stdout: "PASS"
}
issue_869_1: { issue_869_1: {
mangle = { mangle = {
properties: { properties: {
@@ -1856,36 +1353,6 @@ issue_3188_3: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
join_expr: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var c = "FAIL";
(function() {
var a = 0;
switch ((a = {}) && (a.b = 0)) {
case 0:
c = "PASS";
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
var a = 0;
switch (a = { b: 0 }, a.b) {
case 0:
c = "PASS";
}
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3389: { issue_3389: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -136,7 +136,7 @@ relational: {
side_effects :true, side_effects :true,
} }
input: { input: {
foo() in foo(); foo() in new foo();
foo() instanceof bar(); foo() instanceof bar();
foo() < "bar"; foo() < "bar";
bar() > foo(); bar() > foo();
@@ -680,3 +680,130 @@ issue_3325_2: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3858: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: "strict",
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {
var f = function(a) {
return function() {
console.log(a);
}();
};
f("PASS");
}
expect_stdout: "PASS"
}
inline_pure_call_1: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
f("PASS");
}
expect: {}
}
inline_pure_call_2: {
options = {
collapse_vars: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
}
expect: {}
}
inline_pure_call_3: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
keep_fargs: "strict",
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function(a) {
return /*@__PURE__*/ function(b) {
console.log(b);
}(a);
};
var a = f("PASS");
console.log(a);
}
expect: {
var a = function() {
console.log("PASS");
}();
console.log(a);
}
expect_stdout: [
"PASS",
"undefined",
]
}
inline_pure_call_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = /*@__PURE__*/ function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect: {
var a = function() {
return console.log("PASS"), 42;
}();
console.log(a);
}
expect_stdout: [
"PASS",
"42",
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -255,3 +255,221 @@ issue_3434_4: {
"false true", "false true",
] ]
} }
exec: {
options = {
evaluate: true,
loops: true,
unsafe: true,
}
input: {
while (/a/.exec("AAA"))
console.log("FAIL");
console.log("PASS");
}
expect: {
for (;null;)
console.log("FAIL");
console.log("PASS");
}
expect_stdout: "PASS"
}
exec_global: {
options = {
evaluate: true,
loops: true,
unsafe: true,
}
input: {
while (/a/g.exec("AAA"))
console.log("FAIL");
console.log("PASS");
}
expect: {
for (;null;)
console.log("FAIL");
console.log("PASS");
}
expect_stdout: "PASS"
}
test: {
options = {
evaluate: true,
unsafe: true,
}
input: {
while (/a/.test("AAA"))
console.log("FAIL");
console.log("PASS");
}
expect: {
while (false)
console.log("FAIL");
console.log("PASS");
}
expect_stdout: "PASS"
}
test_global: {
options = {
evaluate: true,
unsafe: true,
}
input: {
while (/a/g.test("AAA"))
console.log("FAIL");
console.log("PASS");
}
expect: {
while (false)
console.log("FAIL");
console.log("PASS");
}
expect_stdout: "PASS"
}
var_exec: {
options = {
evaluate: true,
loops: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var r = /a/;
while (r.exec("AAA"))
console.log("FAIL");
console.log("PASS");
}
expect: {
var r = /a/;
for (;null;)
console.log("FAIL");
console.log("PASS");
}
expect_stdout: "PASS"
}
var_exec_global: {
options = {
evaluate: true,
loops: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var r = /a/g;
while (r.exec("aaa"))
console.log("PASS");
}
expect: {
var r = /a/g;
for (;r.exec("aaa");)
console.log("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
}
var_test: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var r = /a/;
while (r.test("AAA"))
console.log("FAIL");
console.log("PASS");
}
expect: {
var r = /a/;
while (false)
console.log("FAIL");
console.log("PASS");
}
expect_stdout: "PASS"
}
var_test_global: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var r = /a/g;
while (r.test("aaa"))
console.log("PASS");
}
expect: {
var r = /a/g;
while (r.test("aaa"))
console.log("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
}
lazy_boolean: {
options = {
evaluate: true,
passes: 2,
side_effects: true,
unsafe: true,
}
input: {
/b/.exec({}) && console.log("PASS");
/b/.test({}) && console.log("PASS");
/b/g.exec({}) && console.log("PASS");
/b/g.test({}) && console.log("PASS");
}
expect: {
console.log("PASS");
console.log("PASS");
console.log("PASS");
console.log("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
"PASS",
]
}
reset_state_between_evaluate: {
options = {
evaluate: true,
passes: 2,
unsafe: true,
}
input: {
console.log(function() {
for (var a in /[abc4]/g.exec("a"))
return "PASS";
return "FAIL";
}());
}
expect: {
console.log(function() {
for (var a in /[abc4]/g.exec("a"))
return "PASS";
return "FAIL";
}());
}
expect_stdout: "PASS"
}

View File

@@ -80,3 +80,21 @@ log_global: {
} }
expect_stdout: "[object global]" expect_stdout: "[object global]"
} }
issue_4054: {
input: {
console.log({
set p(v) {
throw "FAIL";
},
});
}
expect: {
console.log({
set p(v) {
throw "FAIL";
},
});
}
expect_stdout: "{ p: [Setter] }"
}

View File

@@ -877,7 +877,7 @@ for_init_var: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
forin: { forin_1: {
options = { options = {
sequences: true, sequences: true,
} }
@@ -895,6 +895,49 @@ forin: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
forin_2: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var o = {
p: 1,
q: 2,
};
var k = "k";
for ((console.log("exp"), o)[function() {
console.log("prop");
return k;
}()] in function() {
console.log("obj");
return o;
}())
console.log(o.k, o[o.k]);
}
expect: {
var o = {
p: 1,
q: 2,
};
for ((console.log("exp"), o)[console.log("prop"), "k"] in console.log("obj"), o)
console.log(o.k, o[o.k]);
}
expect_stdout: [
"obj",
"exp",
"prop",
"p 1",
"exp",
"prop",
"q 2",
]
}
call: { call: {
options = { options = {
sequences: true, sequences: true,
@@ -1112,3 +1155,25 @@ issue_3703: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_4079: {
options = {
sequences: true,
side_effects: true,
}
input: {
try {
typeof (0, A);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
A;
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -97,9 +97,8 @@ issue_2233_2: {
var RegExp; var RegExp;
UndeclaredGlobal; UndeclaredGlobal;
function foo() { function foo() {
var Number;
AnotherUndeclaredGlobal; AnotherUndeclaredGlobal;
Number.isNaN; (void 0).isNaN;
} }
} }
} }
@@ -246,6 +245,31 @@ unsafe_builtin_2: {
expect_stdout: "object PASS PASS" expect_stdout: "object PASS PASS"
} }
unsafe_builtin_3: {
options = {
conditionals: true,
side_effects: true,
toplevel: true,
unsafe: true,
}
input: {
var o = {};
if (42 < Math.random())
o.p = "FAIL";
else
o.p = "PASS";
for (var k in o)
console.log(k, o[k]);
}
expect: {
var o = {};
o.p = 42 < Math.random() ? "FAIL" : "PASS";
for (var k in o)
console.log(k, o[k]);
}
expect_stdout: "p PASS"
}
unsafe_string_replace: { unsafe_string_replace: {
options = { options = {
side_effects: true, side_effects: true,
@@ -275,3 +299,137 @@ drop_value: {
foo(), bar(); foo(), bar();
} }
} }
operator_in: {
options = {
side_effects: true,
}
input: {
try {
"foo" in true;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
0 in true;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_3983_1: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f() {
g && g();
}
f();
function g() {
0 ? a : 0;
}
var b = a;
console.log(a);
}
expect: {
var a = "PASS";
g();
function g() {}
console.log(a);
}
expect_stdout: "PASS"
}
issue_3983_2: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f() {
g && g();
}
f();
function g() {
0 ? a : 0;
}
var b = a;
console.log(a);
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4008: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
function f(b, b) {
console.log(b);
}
f && f(a && a[a]);
console.log(a);
}
expect: {
var a = "PASS";
function f(b, b) {
console.log(b);
}
f(a[a]);
console.log(a);
}
expect_stdout: [
"undefined",
"PASS",
]
}
trim_new: {
options = {
side_effects: true,
}
input: {
new function(a) {
console.log(a);
}("PASS");
}
expect: {
(function(a) {
console.log(a);
})("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -1,5 +1,6 @@
constant_switch_1: { constant_switch_1: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -19,6 +20,7 @@ constant_switch_1: {
constant_switch_2: { constant_switch_2: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -39,6 +41,7 @@ constant_switch_2: {
constant_switch_3: { constant_switch_3: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -60,6 +63,7 @@ constant_switch_3: {
constant_switch_4: { constant_switch_4: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -86,6 +90,7 @@ constant_switch_4: {
constant_switch_5: { constant_switch_5: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -120,6 +125,7 @@ constant_switch_5: {
constant_switch_6: { constant_switch_6: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -154,6 +160,7 @@ constant_switch_6: {
constant_switch_7: { constant_switch_7: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -197,6 +204,7 @@ constant_switch_7: {
constant_switch_8: { constant_switch_8: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -226,6 +234,7 @@ constant_switch_8: {
constant_switch_9: { constant_switch_9: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -315,6 +324,7 @@ keep_default: {
issue_1663: { issue_1663: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -551,6 +561,7 @@ issue_441_2: {
issue_1674: { issue_1674: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -876,6 +887,7 @@ beautify: {
issue_1758: { issue_1758: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
switches: true, switches: true,
} }
@@ -898,15 +910,16 @@ issue_1758: {
issue_2535: { issue_2535: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
switches: true, switches: true,
} }
input: { input: {
switch(w(), 42) { switch(w(), 42) {
case 13: x(); case 13: x();
case 42: y(); case 42: y();
default: z(); default: z();
} }
} }
expect: { expect: {
@@ -919,6 +932,7 @@ issue_2535: {
issue_1750: { issue_1750: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
switches: true, switches: true,
@@ -963,6 +977,7 @@ drop_switch_1: {
drop_switch_2: { drop_switch_2: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
switches: true, switches: true,
} }
@@ -1007,6 +1022,7 @@ drop_switch_3: {
drop_switch_4: { drop_switch_4: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
switches: true, switches: true,
} }
@@ -1028,3 +1044,140 @@ drop_switch_4: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
drop_switch_5: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
input: {
switch (A) {
case B:
x();
default:
}
switch (C) {
default:
y();
case D:
}
}
expect: {
A === B && x();
C !== D && y();
}
}
drop_switch_6: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
input: {
switch (A) {
case B:
default:
x();
}
switch (C) {
default:
case D:
y();
}
}
expect: {
A === B;
x();
C !== D;
y();
}
}
drop_switch_7: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
input: {
switch (A) {
case B:
w();
default:
x();
}
switch (C) {
default:
y();
case D:
z();
}
}
expect: {
A === B && w();
x();
C !== D && y();
z();
}
}
drop_switch_8: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
input: {
switch (A) {
case B:
w();
break;
default:
x();
}
switch (C) {
default:
y();
break;
case D:
z();
}
}
expect: {
(A === B ? w : x)();
(C !== D ? y : z)();
}
}
issue_4059: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch (0) {
default:
case 1:
break;
case a:
break;
var a;
}
console.log("PASS");
}
expect: {
switch (0) {
default:
break;
case a:
break;
var a;
}
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -69,6 +69,7 @@ label_if_break: {
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
unused: true,
} }
input: { input: {
L: if (true) { L: if (true) {

View File

@@ -435,3 +435,25 @@ emberjs_global: {
} }
expect_stdout: Error("PASS") expect_stdout: Error("PASS")
} }
issue_3817: {
options = {
comparisons: true,
conditionals: true,
passes: 2,
typeofs: true,
}
input: {
if ("A" == typeof A || !console.log("PASS")) switch (false) {
case "undefined" == typeof A:
console.log("FAIL");
}
}
expect: {
if ("A" == typeof A || !console.log("PASS")) switch (false) {
case "undefined" == typeof A:
console.log("FAIL");
}
}
expect_stdout: "PASS"
}

View File

@@ -13,3 +13,4 @@ exports["to_ascii"] = to_ascii;
exports["tokenizer"] = tokenizer; exports["tokenizer"] = tokenizer;
exports["TreeTransformer"] = TreeTransformer; exports["TreeTransformer"] = TreeTransformer;
exports["TreeWalker"] = TreeWalker; exports["TreeWalker"] = TreeWalker;
exports["vlq_decode"] = vlq_decode;

View File

@@ -0,0 +1,4 @@
switch (0) {
default:
default:
}

View File

@@ -1,2 +1,41 @@
function _toConsumableArray(arr){if(Array.isArray(arr)){for(var i=0,arr2=Array(arr.length);i<arr.length;i++){arr2[i]=arr[i]}return arr2}else{return Array.from(arr)}}var _require=require("bar"),foo=_require.foo;var _require2=require("world"),hello=_require2.hello;foo.x.apply(foo,_toConsumableArray(foo.y(hello.z))); "use strict";
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImlucHV0Mi5qcyJdLCJuYW1lcyI6WyJyZXF1aXJlIiwiYXJyIl0sIm1hcHBpbmdzIjoiMEpBQWNBLEtBQVFDIiwic291cmNlc0NvbnRlbnQiOlsiY29uc3Qge2Zvb30gPSByZXF1aXJlKFwiYmFyXCIpO1xuY29uc3Qge2hlbGxvfSA9IHJlcXVpcmUoXCJ3b3JsZFwiKTtcblxuZm9vLngoLi4uZm9vLnkoaGVsbG8ueikpO1xuIl19
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) {
arr2[i] = arr[i];
}
return arr2;
}
var _require = require("bar"), foo = _require.foo;
var _require2 = require("world"), hello = _require2.hello;
foo.x.apply(foo, _toConsumableArray(foo.y(hello.z)));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImlucHV0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IHtmb299ID0gcmVxdWlyZShcImJhclwiKTtcbmNvbnN0IHtoZWxsb30gPSByZXF1aXJlKFwid29ybGRcIik7XG5cbmZvby54KC4uLmZvby55KGhlbGxvLnopKTtcbiJdLCJuYW1lcyI6WyJyZXF1aXJlIiwiZm9vIiwiaGVsbG8iLCJ4IiwiYXBwbHkiLCJfdG9Db25zdW1hYmxlQXJyYXkiLCJ5IiwieiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7ZUFBY0EsUUFBUSxRQUFmQyxNLFNBQUFBOztBLGdCQUNTRCxRQUFRLFVBQWpCRSxRLFVBQUFBOztBQUVQRCxJQUFJRSxFQUFKQyxNQUFBSCxLQUFHSSxtQkFBTUosSUFBSUssRUFBRUosTUFBTUsifQ==

View File

@@ -1,4 +1,16 @@
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } "use strict";
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
var _require = require("bar"), var _require = require("bar"),
foo = _require.foo; foo = _require.foo;

View File

@@ -1 +1,11 @@
{"version":3,"sources":["input2.js"],"names":["require","foo","hello","x","apply","_toConsumableArray","y","z"],"mappings":"kLAAcA,QAAQ,OAAfC,aAAAA,kBACSD,QAAQ,SAAjBE,gBAAAA,MAEPD,IAAIE,EAAJC,MAAAH,IAAAI,mBAASJ,IAAIK,EAAEJ,MAAMK","sourcesContent":["const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"]} {
"version": 3,
"sources": [
"input.js"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH",
"sourcesContent": [
"const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n"
]
}

View File

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

View File

@@ -2,4 +2,4 @@ function test(a){
"aaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaa"
;a(err,data),a(err, ;a(err,data),a(err,
data)} data)}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQztBQUFLQyJ9 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7O0NBRVZBLEVBQVNDLElBQUtDLE1BQ2RGLEVBQVNDO0FBQUtDIn0=

View File

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

View File

@@ -0,0 +1,5 @@
(function f(a) {
do {
console.log(f.length);
} while (console.log(f += 0));
})();

View File

@@ -0,0 +1,19 @@
// (beautified)
(function f(a) {
do {
console.log(f.length);
} while (console.log(f += 0));
})();
// output: 1
// function(){}0
//
// minify: 0
// function(){}0
//
// options: {
// "compress": {
// "keep_fargs": false,
// "unsafe": true
// },
// "mangle": false
// }

View File

@@ -1,9 +1,3 @@
var o = this; UNUSED: {
console.log(0 - .1 - .1 - .1);
for (var k in o) L17060: {
a++;
} }
var a;
console.log(k);

View File

@@ -1,16 +1,12 @@
var o = this; // (beautified)
console.log(0 - 1 - .1 - .1);
for (var k in o) { // output: -1.2000000000000002
0;
}
var a;
console.log(k);
// output: a
// //
// minify: k // minify: -1.2
// //
// options: { // options: {
// "compress": {
// "unsafe_math": true
// },
// "mangle": false // "mangle": false
// } // }

View File

@@ -0,0 +1,8 @@
console.log(function f(a) {
({
set p(v) {
f++;
}
});
return f.length;
}());

View File

@@ -0,0 +1,20 @@
// (beautified)
console.log(function f(a) {
({
set p(v) {
f++;
}
});
return f.length;
}());
// output: 1
//
// minify: 0
//
// options: {
// "compress": {
// "keep_fargs": false,
// "unsafe": true
// },
// "mangle": false
// }

View File

@@ -1,3 +1,4 @@
// (beautified)
var b = 0; var b = 0;
var expr2 = (0 - 1 - .1 - .1).toString(); var expr2 = (0 - 1 - .1 - .1).toString();

View File

@@ -1,6 +1,6 @@
function f(x) { function f(x) {
return g(x); return g(x);
function g(x) { function g(x) {
return x; return x + x;
} }
} }

View File

@@ -8,16 +8,15 @@ if (typeof phantom == "undefined") {
require("../tools/exit"); require("../tools/exit");
var args = process.argv.slice(2); var args = process.argv.slice(2);
var debug = args.indexOf("--debug"); var debug = args.indexOf("--debug");
if (debug >= 0) { if (debug < 0) {
debug = false;
} else {
args.splice(debug, 1); args.splice(debug, 1);
debug = true; debug = true;
} else {
debug = false;
} }
if (!args.length) { if (!args.length) args.push("-mcb", "beautify=false,webkit");
args.push("-mcb", "beautify=false,webkit"); args.unshift("bin/uglifyjs");
} args.push("--validate", "--timings");
args.push("--timings");
var child_process = require("child_process"); var child_process = require("child_process");
var fetch = require("./fetch"); var fetch = require("./fetch");
var http = require("http"); var http = require("http");
@@ -39,10 +38,10 @@ if (typeof phantom == "undefined") {
}); });
if (/\.js$/.test(url)) { if (/\.js$/.test(url)) {
var stderr = ""; var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, { var uglifyjs = child_process.spawn(process.argv[0], args, {
silent: true silent: true
}).on("exit", function(code) { }).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), args.join(" ")); console.log("uglifyjs", url.slice(site.length + 1), args.slice(1).join(" "));
console.log(stderr); console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code); if (code) throw new Error("uglifyjs failed with code " + code);
}); });

View File

@@ -2,6 +2,7 @@ var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var fs = require("fs"); var fs = require("fs");
var run_code = require("../sandbox").run_code; var run_code = require("../sandbox").run_code;
var to_ascii = require("../node").to_ascii;
function read(path) { function read(path) {
return fs.readFileSync(path, "utf8"); return fs.readFileSync(path, "utf8");
@@ -48,6 +49,62 @@ describe("bin/uglifyjs", function() {
done(); done();
}); });
}); });
it("Should work with --source-map names=true", function(done) {
exec([
uglifyjscmd,
"--beautify",
"--source-map", [
"names=true",
"url=inline",
].join(","),
].join(" "), function(err, stdout) {
if (err) throw err;
var expected = [
"var obj = {",
" p: a,",
" q: b",
"};",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,",
].join("\n")
assert.strictEqual(stdout.slice(0, expected.length), expected);
var map = JSON.parse(to_ascii(stdout.slice(expected.length).trim()));
assert.deepEqual(map.names, [ "obj", "p", "a", "q", "b" ]);
done();
}).stdin.end([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"));
});
it("Should work with --source-map names=false", function(done) {
exec([
uglifyjscmd,
"--beautify",
"--source-map", [
"names=false",
"url=inline",
].join(","),
].join(" "), function(err, stdout) {
if (err) throw err;
var expected = [
"var obj = {",
" p: a,",
" q: b",
"};",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,",
].join("\n")
assert.strictEqual(stdout.slice(0, expected.length), expected);
var map = JSON.parse(to_ascii(stdout.slice(expected.length).trim()));
assert.deepEqual(map.names, []);
done();
}).stdin.end([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"));
});
it("Should give sensible error against invalid input source map", function(done) { it("Should give sensible error against invalid input source map", function(done) {
var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline --verbose"; var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline --verbose";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
@@ -109,9 +166,12 @@ describe("bin/uglifyjs", function() {
} }
var command = [ var command = [
uglifyjscmd, uglifyjscmd,
"--source-map", "content=" + mapFile, "--beautify",
"--source-map", "includeSources=true", "--source-map", [
"--source-map", "url=inline", "content=" + mapFile,
"includeSources",
"url=inline",
].join(","),
].join(" "); ].join(" ");
var child = exec(command, function(err, stdout) { var child = exec(command, function(err, stdout) {
@@ -189,7 +249,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should work with `--output-opts`", function(done) { it("Should work with `--output-opts`", function(done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -O'; var command = uglifyjscmd + ' test/input/issue-1482/input.js -O ascii_only';
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js")); assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
@@ -197,7 +257,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail when both --beautify & --output-opts are specified", function(done) { it("Should fail when both --beautify & --output-opts are specified", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js -bO"; var command = uglifyjscmd + " test/input/issue-520/input.js -bO ascii_only";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n"); assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n");
@@ -245,7 +305,7 @@ describe("bin/uglifyjs", function() {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
"var Foo=function Foo(){console.log(1+2)};new Foo;var bar=function(){function foo(bar){return bar}return foo}();", "var Foo=function Foo(){console.log(1+2)};new Foo;var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwidGVzdC9pbnB1dC9pc3N1ZS0xMzIzL3NhbXBsZS5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFNBQUFBLE1BQWdCQyxRQUFRQyxJQUFJLEVBQUUsSUFBTyxJQUFJRixJQ0FuRCxJQUFJRyxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwidGVzdC9pbnB1dC9pc3N1ZS0xMzIzL3NhbXBsZS5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFNBQUVBLE1BQWNDLFFBQVFDLElBQUksRUFBRSxJQUFPLElBQUlGLElDQW5ELElBQUlHLElBQU0sV0FDTixTQUFTQyxJQUFLRCxLQUNWLE9BQU9BLElBR1gsT0FBT0MsSUFMRCJ9",
"", "",
].join("\n")); ].join("\n"));
var stderrLines = stderr.split("\n"); var stderrLines = stderr.split("\n");
@@ -270,7 +330,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with invalid syntax", function(done) { it("Should fail with invalid syntax", function(done) {
var command = uglifyjscmd + ' test/input/invalid/simple.js'; var command = uglifyjscmd + " test/input/invalid/simple.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -282,7 +342,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with correct marking of tabs", function(done) { it("Should fail with correct marking of tabs", function(done) {
var command = uglifyjscmd + ' test/input/invalid/tab.js'; var command = uglifyjscmd + " test/input/invalid/tab.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -294,7 +354,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with correct marking at start of line", function(done) { it("Should fail with correct marking at start of line", function(done) {
var command = uglifyjscmd + ' test/input/invalid/eof.js'; var command = uglifyjscmd + " test/input/invalid/eof.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -306,7 +366,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with a missing loop body", function(done) { it("Should fail with a missing loop body", function(done) {
var command = uglifyjscmd + ' test/input/invalid/loop-no-body.js'; var command = uglifyjscmd + " test/input/invalid/loop-no-body.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -318,7 +378,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (5--)", function(done) { it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js'; var command = uglifyjscmd + " test/input/invalid/assign_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -332,7 +392,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (Math.random() /= 2)", function(done) { it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js'; var command = uglifyjscmd + " test/input/invalid/assign_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -346,7 +406,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (++this)", function(done) { it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js'; var command = uglifyjscmd + " test/input/invalid/assign_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -360,7 +420,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (++null)", function(done) { it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js'; var command = uglifyjscmd + " test/input/invalid/assign_4.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -374,7 +434,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (a.=)", function(done) { it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js'; var command = uglifyjscmd + " test/input/invalid/dot_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -388,7 +448,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (%.a)", function(done) { it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js'; var command = uglifyjscmd + " test/input/invalid/dot_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -402,7 +462,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (a./();)", function(done) { it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js'; var command = uglifyjscmd + " test/input/invalid/dot_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -416,7 +476,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error ({%: 1})", function(done) { it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js'; var command = uglifyjscmd + " test/input/invalid/object.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -430,7 +490,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (delete x)", function(done) { it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js'; var command = uglifyjscmd + " test/input/invalid/delete.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -444,7 +504,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (function g(arguments))", function(done) { it("Should throw syntax error (function g(arguments))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_1.js'; var command = uglifyjscmd + " test/input/invalid/function_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -458,7 +518,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (function eval())", function(done) { it("Should throw syntax error (function eval())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_2.js'; var command = uglifyjscmd + " test/input/invalid/function_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -472,7 +532,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (iife arguments())", function(done) { it("Should throw syntax error (iife arguments())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_3.js'; var command = uglifyjscmd + " test/input/invalid/function_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -486,7 +546,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (catch (eval))", function(done) { it("Should throw syntax error (catch (eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js'; var command = uglifyjscmd + " test/input/invalid/try.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -500,7 +560,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (var eval)", function(done) { it("Should throw syntax error (var eval)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/var.js'; var command = uglifyjscmd + " test/input/invalid/var.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -514,7 +574,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (else)", function(done) { it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js'; var command = uglifyjscmd + " test/input/invalid/else.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -528,7 +588,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (return)", function(done) { it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js'; var command = uglifyjscmd + " test/input/invalid/return.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -542,7 +602,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (for-in init)", function(done) { it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js'; var command = uglifyjscmd + " test/input/invalid/for-in_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -556,7 +616,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (for-in var)", function(done) { it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js'; var command = uglifyjscmd + " test/input/invalid/for-in_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -569,6 +629,18 @@ describe("bin/uglifyjs", function() {
done(); done();
}); });
}); });
it("Should throw syntax error (switch defaults)", function(done) {
var command = uglifyjscmd + " test/input/invalid/switch.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/switch.js:3,2");
assert.strictEqual(lines[1], " default:");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: More than one default clause in switch statement");
done();
});
});
it("Should handle literal string as source map input", function(done) { it("Should handle literal string as source map input", function(done) {
var command = [ var command = [
uglifyjscmd, uglifyjscmd,
@@ -587,7 +659,7 @@ describe("bin/uglifyjs", function() {
}); });
function read_map() { function read_map() {
var map = JSON.parse(read("./test/input/issue-1236/simple.js.map")); var map = JSON.parse(read("test/input/issue-1236/simple.js.map"));
delete map.sourcesContent; delete map.sourcesContent;
return JSON.stringify(map).replace(/"/g, '\\"'); return JSON.stringify(map).replace(/"/g, '\\"');
} }
@@ -657,7 +729,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/issue-3315/input.js --config-file test/input/issue-3315/config.json"; var command = uglifyjscmd + " test/input/issue-3315/input.js --config-file test/input/issue-3315/config.json";
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, 'function f(){"aaaaaaaaaa";var a={prop:1,a:2};return a.prop+a.a}\n'); assert.strictEqual(stdout, 'function f(){"aaaaaaaaaa";var a={prop:1,t:2};return a.prop+a.t}\n');
done(); done();
}); });
}); });
@@ -674,7 +746,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/rename/input.js --rename"; var command = uglifyjscmd + " test/input/rename/input.js --rename";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c}}\n"); assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c+c}}\n");
done(); done();
}); });
}); });
@@ -682,7 +754,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2 --no-rename"; var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2 --no-rename";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n"); assert.strictEqual(stdout, "function f(n){return function(n){return n+n}(n)}\n");
done(); done();
}); });
}); });
@@ -690,7 +762,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2"; var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(n){return n}\n"); assert.strictEqual(stdout, "function f(n){return n+n}\n");
done(); done();
}); });
}); });
@@ -698,7 +770,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/rename/input.js -c passes=2"; var command = uglifyjscmd + " test/input/rename/input.js -c passes=2";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n"); assert.strictEqual(stdout, "function f(x){return function(x){return x+x}(x)}\n");
done(); done();
}); });
}); });

View File

@@ -1,7 +1,7 @@
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code; var run_code = require("../sandbox").run_code;
var UglifyJS = require("../../"); var UglifyJS = require("../..");
function read(path) { function read(path) {
return readFileSync(path, "utf8"); return readFileSync(path, "utf8");
@@ -9,18 +9,30 @@ function read(path) {
describe("minify", function() { describe("minify", function() {
it("Should test basic sanity of minify with default options", function() { it("Should test basic sanity of minify with default options", function() {
var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }'; var js = "function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }";
var result = UglifyJS.minify(js); var result = UglifyJS.minify(js);
assert.strictEqual(result.code, 'function foo(n){return n?3:7}'); if (result.error) throw result.error;
assert.strictEqual(result.code, "function foo(n){return n?3:7}");
}); });
it("Should not mutate minify `options`", function() {
var options = {
compress: true,
mangle: false,
output: {},
};
var value = JSON.stringify(options);
var result = UglifyJS.minify("print(6 * 7);", options);
if (result.error) throw result.error;
assert.strictEqual(result.code, "print(42);");
assert.strictEqual(JSON.stringify(options), value);
})
it("Should skip inherited keys from `files`", function() { it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this }); var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)"; files[0] = "alert(1 + 1)";
var result = UglifyJS.minify(files); var result = UglifyJS.minify(files);
if (result.error) throw result.error;
assert.strictEqual(result.code, "alert(2);"); assert.strictEqual(result.code, "alert(2);");
}); });
it("Should work with mangle.cache", function() { it("Should work with mangle.cache", function() {
var cache = {}; var cache = {};
var original = ""; var original = "";
@@ -51,9 +63,8 @@ describe("minify", function() {
"var a=n(3),b=r(12);", "var a=n(3),b=r(12);",
'c("qux",a,b),o(11);', 'c("qux",a,b),o(11);',
].join("")); ].join(""));
assert.strictEqual(run_code(compressed), run_code(original)); assert.strictEqual(run_code(compressed, true), run_code(original, true));
}); });
it("Should work with nameCache", function() { it("Should work with nameCache", function() {
var cache = {}; var cache = {};
var original = ""; var original = "";
@@ -84,9 +95,8 @@ describe("minify", function() {
"var a=n(3),b=r(12);", "var a=n(3),b=r(12);",
'c("qux",a,b),o(11);', 'c("qux",a,b),o(11);',
].join("")); ].join(""));
assert.strictEqual(run_code(compressed), run_code(original)); assert.strictEqual(run_code(compressed, true), run_code(original, true));
}); });
it("Should avoid cached names when mangling top-level variables", function() { it("Should avoid cached names when mangling top-level variables", function() {
var cache = {}; var cache = {};
var original = ""; var original = "";
@@ -113,9 +123,8 @@ describe("minify", function() {
'"xxyyy";var y={y:2,a:3},a=4;', '"xxyyy";var y={y:2,a:3},a=4;',
'console.log(x.x,y.y,y.a,a);', 'console.log(x.x,y.y,y.a,a);',
].join("")); ].join(""));
assert.strictEqual(run_code(compressed), run_code(original)); assert.strictEqual(run_code(compressed, true), run_code(original, true));
}); });
it("Should avoid cached names when mangling inner-scoped variables", function() { it("Should avoid cached names when mangling inner-scoped variables", function() {
var cache = {}; var cache = {};
var original = ""; var original = "";
@@ -137,9 +146,8 @@ describe("minify", function() {
'var o=function(o,n){console.log("extend");o();n()};function n(){console.log("A")}', 'var o=function(o,n){console.log("extend");o();n()};function n(){console.log("A")}',
'var e=function(n){function e(){console.log("B")}o(e,n);return e}(n);', 'var e=function(n){function e(){console.log("B")}o(e,n);return e}(n);',
].join("")); ].join(""));
assert.strictEqual(run_code(compressed), run_code(original)); assert.strictEqual(run_code(compressed, true), run_code(original, true));
}); });
it("Should not parse invalid use of reserved words", function() { it("Should not parse invalid use of reserved words", function() {
assert.strictEqual(UglifyJS.minify("function enum(){}").error, undefined); assert.strictEqual(UglifyJS.minify("function enum(){}").error, undefined);
assert.strictEqual(UglifyJS.minify("function static(){}").error, undefined); assert.strictEqual(UglifyJS.minify("function static(){}").error, undefined);
@@ -155,7 +163,6 @@ describe("minify", function() {
}}); }});
assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};'); assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};');
}); });
it("Should preserve quote styles when quote_style is 3", function() { it("Should preserve quote styles when quote_style is 3", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = UglifyJS.minify(js, { var result = UglifyJS.minify(js, {
@@ -165,7 +172,6 @@ describe("minify", function() {
}}); }});
assert.strictEqual(result.code, 'var foo={"x":1,y:2,\'z\':3};'); assert.strictEqual(result.code, 'var foo={"x":1,y:2,\'z\':3};');
}); });
it("Should not preserve quotes in object literals when disabled", function() { it("Should not preserve quotes in object literals when disabled", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = UglifyJS.minify(js, { var result = UglifyJS.minify(js, {

View File

@@ -28,4 +28,65 @@ describe("Number literals", function() {
assert.throws(test(inputs[i]), error, inputs[i]); assert.throws(test(inputs[i]), error, inputs[i]);
} }
}); });
it("Should parse binary, hexadecimal, octal and underscore correctly", function() {
[
"42",
"4_2",
"052",
"0o52",
"0O52",
"0o5_2",
"0x2a",
"0X2A",
"0x2_a",
"0b101010",
"0B101010",
"0b101_010",
"0.0000000042e+10",
"0.0000000042E+10",
"0.0_000000042e+10",
"0.0000000042e+1_0",
"0.000_000_004_2e+1_0",
"0.000_000_004_2e+1_0-0B101_010+0x2_A-0o5_2+4_2",
].forEach(function(code) {
var result = UglifyJS.minify(code, {
compress: {
expression: true,
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "42;");
});
});
it("Should reject invalid use of underscore", function() {
[
"_42",
"_+42",
"+_42",
].forEach(function(code) {
var node = UglifyJS.parse(code, {
expression: true,
});
assert.ok(!node.is_constant(), code);
assert.ok(!(node instanceof UglifyJS.AST_Statement), code);
});
[
"42_",
"4__2",
"0_52",
"05_2",
"0_o52",
"0o_52",
"0.0000000042_e10",
"0.0000000042e_10",
"0.0000000042e_+10",
"0.0000000042e+_10",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
}); });

View File

@@ -486,4 +486,19 @@ describe("operator", function() {
assert.strictEqual(UglifyJS.parse(exp[0]).print_to_string(), exp[1] + ";"); assert.strictEqual(UglifyJS.parse(exp[0]).print_to_string(), exp[1] + ";");
}); });
}); });
it("Should preserve space between /regex/ and `in`", function() {
[
"/regex/ in {}",
"/regex/g in {}",
"0 + /regex/ in {}",
"0 + /regex/g in {}",
].forEach(function(exp) {
var code = UglifyJS.parse(exp).print_to_string();
try {
assert.strictEqual(UglifyJS.parse(code).print_to_string(), code);
} catch (ex) {
assert.fail("Failed to reparse: " + exp + "\n" + ex);
}
});
});
}); });

View File

@@ -24,6 +24,9 @@ describe("test/reduce.js", function() {
}); });
it("Should eliminate unreferenced labels", function() { it("Should eliminate unreferenced labels", function() {
var result = reduce_test(read("test/input/reduce/label.js"), { var result = reduce_test(read("test/input/reduce/label.js"), {
compress: {
unsafe_math: true,
},
mangle: false, mangle: false,
}, { }, {
verbose: false, verbose: false,
@@ -31,6 +34,19 @@ describe("test/reduce.js", function() {
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/label.reduced.js")); assert.strictEqual(result.code, read("test/input/reduce/label.reduced.js"));
}); });
it("Should retain setter arguments", function() {
var result = reduce_test(read("test/input/reduce/setter.js"), {
compress: {
keep_fargs: false,
unsafe: true,
},
mangle: false,
}, {
verbose: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/setter.reduced.js"));
});
it("Should handle test cases with --toplevel", function() { it("Should handle test cases with --toplevel", function() {
var result = reduce_test([ var result = reduce_test([
"var Infinity = 42;", "var Infinity = 42;",
@@ -40,21 +56,56 @@ describe("test/reduce.js", function() {
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:", "// Can't reproduce test failure",
"// {", "// minify options: {",
'// "toplevel": true', '// "toplevel": true',
"// }", "// }",
].join("\n")); ].join("\n"));
}); });
it("Should handle test cases with --compress toplevel", function() {
var result = reduce_test([
"var NaN = 42;",
"console.log(NaN);",
].join("\n"), {
compress: {
toplevel: true,
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure",
"// minify options: {",
'// "compress": {',
'// "toplevel": true',
"// }",
"// }",
].join("\n"));
});
it("Should handle test cases with --mangle toplevel", function() {
var result = reduce_test([
"var undefined = 42;",
"console.log(undefined);",
].join("\n"), {
mangle: {
toplevel: true,
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure",
"// minify options: {",
'// "mangle": {',
'// "toplevel": true',
"// }",
"// }",
].join("\n"));
});
it("Should handle test result of NaN", function() { it("Should handle test result of NaN", function() {
var result = reduce_test("throw 0 / 0;"); var result = reduce_test("throw 0 / 0;");
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:", "// Can't reproduce test failure",
"// {", "// minify options: {}",
'// "compress": {},',
'// "mangle": false',
"// }",
].join("\n")); ].join("\n"));
}); });
it("Should print correct output for irreducible test case", function() { it("Should print correct output for irreducible test case", function() {
@@ -70,6 +121,7 @@ describe("test/reduce.js", function() {
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"// (beautified)",
"console.log(function f(a) {", "console.log(function f(a) {",
" return f.length;", " return f.length;",
"}());", "}());",
@@ -118,6 +170,7 @@ describe("test/reduce.js", function() {
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"// (beautified)",
code, code,
"// output: 0.8", "// output: 0.8",
"// 1.6", "// 1.6",
@@ -136,13 +189,43 @@ describe("test/reduce.js", function() {
].join("\n")); ].join("\n"));
}); });
it("Should reduce infinite loops with reasonable performance", function() { it("Should reduce infinite loops with reasonable performance", function() {
if (semver.satisfies(process.version, "0.10")) return; if (semver.satisfies(process.version, "<=0.10")) return;
this.timeout(120000); this.timeout(120000);
var result = reduce_test("while (/9/.test(1 - .8));", {
compress: {
unsafe_math: true,
},
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code.replace(/ timed out after [0-9]+ms/, " timed out."), [
"// (beautified)",
"while (/9/.test(1 - .8)) {}",
"// output: Error: Script execution timed out.",
"// minify: ",
"// options: {",
'// "compress": {',
'// "unsafe_math": true',
"// },",
'// "mangle": false',
"// }",
].join("\n"));
});
it("Should ignore difference in Error.message", function() {
var result = reduce_test("null[function() {\n}];");
if (result.error) throw result.error;
assert.strictEqual(result.code, (semver.satisfies(process.version, "<=0.10") ? [
"// Can't reproduce test failure",
"// minify options: {}",
] : [
"// No differences except in error message",
"// minify options: {}",
]).join("\n"));
});
it("Should report trailing whitespace difference in stringified format", function() {
var code = [ var code = [
"var a = 9007199254740992, b = 1;", "for (var a in (1 - .8).toString()) {",
"", " console.log();",
"while (a++ + (1 - b) < a) {",
" 0;",
"}", "}",
].join("\n"); ].join("\n");
var result = reduce_test(code, { var result = reduce_test(code, {
@@ -152,16 +235,82 @@ describe("test/reduce.js", function() {
mangle: false, mangle: false,
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code.replace(/ timed out after [0-9]+ms/, " timed out."), [ assert.strictEqual(result.code, [
"// (beautified)",
code, code,
"// output: ", "// (stringified)",
"// minify: Error: Script execution timed out.", '// output: "\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n"',
'// minify: "\\n\\n\\n"',
"// options: {", "// options: {",
'// "compress": {', '// "compress": {',
'// "unsafe_math": true', '// "unsafe_math": true',
"// },", '// },',
'// "mangle": false', '// "mangle": false',
"// }", "// }",
].join("\n")); ].join("\n"));
}); });
it("Should reduce test case which differs only in Error.message", function() {
var code = [
"var a=0;",
"try{",
"null[function(){}]",
"}catch(e){",
"for(var i in e.toString())",
"a++,console.log()",
"}",
"console.log(a);",
].join("");
var result = reduce_test(code, {
compress: false,
mangle: false,
output: {
beautify: true,
},
});
if (result.error) throw result.error;
assert.deepEqual(result.warnings, []);
assert.strictEqual(result.code.replace(/function \(/g, "function("), (semver.satisfies(process.version, "<=0.10") ? [
"// Can't reproduce test failure",
"// minify options: {",
'// "compress": false,',
'// "mangle": false,',
'// "output": {',
'// "beautify": true',
"// }",
"// }",
] : [
[
"try{",
"null[function(){}]",
"}catch(e){",
"console.log(e)",
"}",
].join(""),
"// output: TypeError: Cannot read property 'function(){}' of null",
"// ",
"// minify: TypeError: Cannot read property 'function() {}' of null",
"// ",
"// options: {",
'// "compress": false,',
'// "mangle": false,',
'// "output": {',
'// "beautify": true',
"// }",
"// }",
]).join("\n"));
});
it("Should handle corner cases when intermediate case differs only in Error.message", function() {
if (semver.satisfies(process.version, "<=0.10")) return;
var result = reduce_test(read("test/input/reduce/diff_error.js"), {
compress: {
keep_fargs: false,
unsafe: true,
},
mangle: false,
}, {
verbose: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/diff_error.reduced.js"));
});
}); });

View File

@@ -1,18 +1,19 @@
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var fs = require("fs");
var SourceMapConsumer = require("source-map").SourceMapConsumer;
var UglifyJS = require("../node"); var UglifyJS = require("../node");
function read(path) { function read(path) {
return readFileSync(path, "utf8"); return fs.readFileSync(path, "utf8");
} }
function source_map(code) { function source_map(code) {
return JSON.parse(UglifyJS.minify(code, { var result = UglifyJS.minify(code, {
compress: false, compress: false,
mangle: false, mangle: false,
sourceMap: true, sourceMap: true,
}).map); });
if (result.error) throw result.error;
return JSON.parse(result.map);
} }
function get_map() { function get_map() {
@@ -44,7 +45,7 @@ function prepare_map(sourceMap) {
} }
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
return new SourceMapConsumer(result.map); return JSON.parse(result.map);
} }
describe("sourcemaps", function() { describe("sourcemaps", function() {
@@ -66,6 +67,40 @@ describe("sourcemaps", function() {
].join("\n")); ].join("\n"));
assert.deepEqual(map.names, [ "enabled", "x" ]); assert.deepEqual(map.names, [ "enabled", "x" ]);
}); });
it("Should work with sourceMap.names=true", function() {
var result = UglifyJS.minify([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"), {
compress: false,
mangle: false,
sourceMap: {
names: true,
},
});
if (result.error) throw result.error;
var map = JSON.parse(result.map);
assert.deepEqual(map.names, [ "obj", "p", "a", "q", "b" ]);
});
it("Should work with sourceMap.names=false", function() {
var result = UglifyJS.minify([
"var obj = {",
" p: a,",
" q: b",
"};",
].join("\n"), {
compress: false,
mangle: false,
sourceMap: {
names: false,
},
});
if (result.error) throw result.error;
var map = JSON.parse(result.map);
assert.deepEqual(map.names, []);
});
it("Should mark array/object literals", function() { it("Should mark array/object literals", function() {
var result = UglifyJS.minify([ var result = UglifyJS.minify([
"var obj = {};", "var obj = {};",
@@ -87,14 +122,30 @@ describe("sourcemaps", function() {
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, code); assert.strictEqual(result.code, code);
assert.strictEqual(result.map, '{"version":3,"sources":["0"],"names":["console","log"],"mappings":"AAAAA,QAAQC,IAAI","sourceRoot":"//foo.bar/"}'); assert.strictEqual(result.map, '{"version":3,"sourceRoot":"//foo.bar/","sources":["0"],"names":["console","log"],"mappings":"AAAAA,QAAQC,IAAI"}');
});
it("Should produce same source map with DOS or UNIX line endings", function() {
var code = [
'console.log("\\',
'hello",',
'"world");',
];
var dos = UglifyJS.minify(code.join("\r\n"), {
sourceMap: true,
});
if (dos.error) throw dos.error;
var unix = UglifyJS.minify(code.join("\n"), {
sourceMap: true,
});
if (unix.error) throw unix.error;
assert.strictEqual(dos.map, unix.map);
}); });
describe("inSourceMap", function() { describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled", function() { it("Should read the given string filename correctly when sourceMapIncludeSources is enabled", function() {
var result = UglifyJS.minify(read("./test/input/issue-1236/simple.js"), { var result = UglifyJS.minify(read("test/input/issue-1236/simple.js"), {
sourceMap: { sourceMap: {
content: read("./test/input/issue-1236/simple.js.map"), content: read("test/input/issue-1236/simple.js.map"),
filename: "simple.min.js", filename: "simple.min.js",
includeSources: true includeSources: true
} }
@@ -106,7 +157,7 @@ describe("sourcemaps", function() {
assert.equal(map.sourcesContent[0], 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); assert.equal(map.sourcesContent[0], 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
}); });
it("Should process inline source map", function() { it("Should process inline source map", function() {
var result = UglifyJS.minify(read("./test/input/issue-520/input.js"), { var result = UglifyJS.minify(read("test/input/issue-520/input.js"), {
compress: { toplevel: true }, compress: { toplevel: true },
sourceMap: { sourceMap: {
content: "inline", content: "inline",
@@ -115,10 +166,10 @@ describe("sourcemaps", function() {
} }
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-520/output.js", "utf8")); assert.strictEqual(result.code + "\n", read("test/input/issue-520/output.js"));
}); });
it("Should warn for missing inline source map", function() { it("Should warn for missing inline source map", function() {
var result = UglifyJS.minify(read("./test/input/issue-1323/sample.js"), { var result = UglifyJS.minify(read("test/input/issue-1323/sample.js"), {
mangle: false, mangle: false,
sourceMap: { sourceMap: {
content: "inline" content: "inline"
@@ -130,8 +181,8 @@ describe("sourcemaps", function() {
}); });
it("Should handle multiple input and inline source map", function() { it("Should handle multiple input and inline source map", function() {
var result = UglifyJS.minify([ var result = UglifyJS.minify([
read("./test/input/issue-520/input.js"), read("test/input/issue-520/input.js"),
read("./test/input/issue-1323/sample.js"), read("test/input/issue-1323/sample.js"),
], { ], {
sourceMap: { sourceMap: {
content: "inline", content: "inline",
@@ -147,7 +198,7 @@ describe("sourcemaps", function() {
assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]); assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]);
}); });
it("Should drop source contents for includeSources=false", function() { it("Should drop source contents for includeSources=false", function() {
var result = UglifyJS.minify(read("./test/input/issue-520/input.js"), { var result = UglifyJS.minify(read("test/input/issue-520/input.js"), {
compress: false, compress: false,
mangle: false, mangle: false,
sourceMap: { sourceMap: {
@@ -170,7 +221,7 @@ describe("sourcemaps", function() {
assert.ok(!("sourcesContent" in map)); assert.ok(!("sourcesContent" in map));
}); });
it("Should parse the correct sourceMappingURL", function() { it("Should parse the correct sourceMappingURL", function() {
var result = UglifyJS.minify(read("./test/input/issue-3294/input.js"), { var result = UglifyJS.minify(read("test/input/issue-3294/input.js"), {
compress: { toplevel: true }, compress: { toplevel: true },
sourceMap: { sourceMap: {
content: "inline", content: "inline",
@@ -179,10 +230,10 @@ describe("sourcemaps", function() {
} }
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8")); assert.strictEqual(result.code + "\n", read("test/input/issue-3294/output.js"));
}); });
it("Should work in presence of unrecognised annotations", function() { it("Should work in presence of unrecognised annotations", function() {
var result = UglifyJS.minify(read("./test/input/issue-3441/input.js"), { var result = UglifyJS.minify(read("test/input/issue-3441/input.js"), {
compress: false, compress: false,
mangle: false, mangle: false,
sourceMap: { sourceMap: {
@@ -214,7 +265,7 @@ describe("sourcemaps", function() {
assert.strictEqual(code, "var a=function(n){return n};"); assert.strictEqual(code, "var a=function(n){return n};");
}); });
it("Should work with max_line_len", function() { it("Should work with max_line_len", function() {
var result = UglifyJS.minify(read("./test/input/issue-505/input.js"), { var result = UglifyJS.minify(read("test/input/issue-505/input.js"), {
compress: { compress: {
directives: false, directives: false,
}, },
@@ -226,7 +277,7 @@ describe("sourcemaps", function() {
} }
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, read("./test/input/issue-505/output.js")); assert.strictEqual(result.code, read("test/input/issue-505/output.js"));
}); });
it("Should work with unicode characters", function() { it("Should work with unicode characters", function() {
var code = [ var code = [
@@ -262,32 +313,42 @@ describe("sourcemaps", function() {
}); });
describe("input sourcemaps", function() { describe("input sourcemaps", function() {
it("Should not modify input source map", function() {
var orig = get_map();
var original = JSON.stringify(orig);
var map = prepare_map(orig);
assert.strictEqual(JSON.stringify(orig), original);
});
it("Should copy over original sourcesContent", function() { it("Should copy over original sourcesContent", function() {
var orig = get_map(); var orig = get_map();
var map = prepare_map(orig); var map = prepare_map(orig);
assert.equal(map.sourceContentFor("index.js"), orig.sourcesContent[0]); assert.strictEqual(map.sources.length, 1);
assert.strictEqual(map.sources[0], "index.js");
assert.strictEqual(map.sourcesContent.length, 1);
assert.equal(map.sourcesContent[0], orig.sourcesContent[0]);
}); });
it("Should copy sourcesContent if sources are relative", function() { it("Should copy sourcesContent if sources are relative", function() {
var relativeMap = get_map(); var relativeMap = get_map();
relativeMap.sources = ['./index.js']; relativeMap.sources = ['./index.js'];
var map = prepare_map(relativeMap); var map = prepare_map(relativeMap);
assert.notEqual(map.sourcesContent, null); assert.strictEqual(map.sources.length, 1);
assert.equal(map.sourcesContent.length, 1); assert.strictEqual(map.sources[0], "./index.js");
assert.equal(map.sourceContentFor("index.js"), relativeMap.sourcesContent[0]); assert.strictEqual(map.sourcesContent.length, 1);
assert.equal(map.sourcesContent[0], relativeMap.sourcesContent[0]);
}); });
it("Should not have invalid mappings from inputSourceMap", function() { it("Should not have invalid mappings from inputSourceMap", function() {
var map = prepare_map(get_map()); var map = prepare_map(get_map());
// The original source has only 2 lines, check that mappings don't have more lines // The original source has only 2 lines, check that mappings don't have more lines
var msg = "Mapping should not have higher line number than the original file had"; var msg = "Mapping should not have higher line number than the original file had";
map.eachMapping(function(mapping) { var lines = map.mappings.split(/;/);
assert.ok(mapping.originalLine <= 2, msg); assert.ok(lines.length <= 2, msg);
}); var indices = [ 0, 0, 1, 0, 0];
map.allGeneratedPositionsFor({ lines.forEach(function(segments) {
source: "index.js", indices[0] = 0;
line: 1, segments.split(/,/).forEach(function(segment) {
column: 1 UglifyJS.vlq_decode(indices, segment);
}).forEach(function(pos) { assert.ok(indices[2] <= 2, msg);
assert.ok(pos.line <= 2, msg); });
}); });
}); });
}); });

View File

@@ -17,7 +17,7 @@ describe("With", function() {
var ast = UglifyJS.parse("with(e) {f(1, 2)}"); var ast = UglifyJS.parse("with(e) {f(1, 2)}");
ast.figure_out_scope(); ast.figure_out_scope();
assert.equal(ast.uses_with, true); assert.equal(ast.uses_with, true);
assert.equal(ast.body[0].expression.scope.uses_with, true); assert.equal(ast.body[0].expression.scope.resolve().uses_with, true);
assert.equal(ast.body[0].body.body[0].body.expression.scope.uses_with, true); assert.equal(ast.body[0].body.body[0].body.expression.scope.resolve().uses_with, true);
}); });
}); });

View File

@@ -24,11 +24,22 @@ function try_beautify(code) {
} }
} }
function test(original, estree, description) { function validate(ast) {
var transformed = UglifyJS.minify(UglifyJS.AST_Node.from_mozilla_ast(estree), { try {
ast.walk(new UglifyJS.TreeWalker(function(node) {
node.validate();
}));
} catch (e) {
return { error: e };
}
return UglifyJS.minify(ast, {
compress: false, compress: false,
mangle: false mangle: false,
}); });
}
function test(original, estree, description) {
var transformed = validate(UglifyJS.AST_Node.from_mozilla_ast(estree));
if (transformed.error || original !== transformed.code) { if (transformed.error || original !== transformed.code) {
console.log("//============================================================="); console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round); console.log("// !!!!!! Failed... round", round);
@@ -44,30 +55,37 @@ function test(original, estree, description) {
try_beautify(transformed.code); try_beautify(transformed.code);
} }
console.log("!!!!!! Failed... round", round); console.log("!!!!!! Failed... round", round);
process.exit(1); return false;
} }
return true;
} }
var num_iterations = ufuzz.num_iterations; var num_iterations = ufuzz.num_iterations;
var minify_options = require("./ufuzz/options.json").map(JSON.stringify);
minify_options.unshift(null);
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r"); process.stdout.write(round + " of " + num_iterations + "\r");
var code = ufuzz.createTopLevelCode(); var code = ufuzz.createTopLevelCode();
var uglified = UglifyJS.minify(code, { minify_options.forEach(function(options) {
compress: false, var input = options ? UglifyJS.minify(code, JSON.parse(options)).code : code;
mangle: false, var uglified = UglifyJS.minify(input, {
output: { compress: false,
ast: true mangle: false,
output: {
ast: true
}
});
var ok = test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
try {
ok = test(uglified.code, acorn.parse(input), "acorn.parse()") && ok;
} catch (e) {
console.log("//=============================================================");
console.log("// acorn parser failed... round", round);
console.log(e);
console.log("// original code");
console.log(input);
} }
if (!ok) process.exit(1);
}); });
test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
try {
test(uglified.code, acorn.parse(code), "acorn.parse()");
} catch (e) {
console.log("//=============================================================");
console.log("// acorn parser failed... round", round);
console.log(e);
console.log("// original code");
console.log(code);
}
} }
console.log(); console.log();

View File

@@ -1,6 +1,6 @@
var fs = require("fs"); var fs = require("fs");
new Function("MOZ_SourceMap", "exports", require("../tools/node").FILES.map(function(file) { new Function("exports", require("../tools/node").FILES.map(function(file) {
if (/exports\.js$/.test(file)) file = require.resolve("./exports"); if (/exports\.js$/.test(file)) file = require.resolve("./exports");
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}).join("\n\n"))(require("source-map"), exports); }).join("\n\n"))(exports);

View File

@@ -1,51 +1,76 @@
var crypto = require("crypto"); var crypto = require("crypto");
var U = require("./node"); var U = require("..");
var List = U.List; var List = U.List;
var os = require("os"); var os = require("os");
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
// Reduce a ufuzz-style `console.log` based test case by iteratively replacing // Reduce a test case by iteratively replacing AST nodes with various
// AST nodes with various permutations. Each AST_Statement in the tree is also // permutations. Each AST_Statement in the tree is also speculatively dropped
// speculatively dropped to determine whether it is needed. If the altered // to determine whether it is needed. If the altered tree and the last known
// tree and the last known good tree produce the same non-nil error-free output // good tree produce the same output after being run, then the permutation
// after being run, then the permutation survives to the next generation and // survives to the next generation and is the basis for subsequent iterations.
// is the basis for subsequent iterations. The test case is reduced as a // The test case is reduced as a consequence of complex expressions being
// consequence of complex expressions being replaced with simpler ones. // replaced with simpler ones. Note that a reduced test case will have
// This function assumes that the testcase will not result in a parse or // different runtime output - it is not functionally equivalent to the
// runtime Error. Note that a reduced test case will have different runtime // original. The only criteria is that once the generated reduced test case is
// output - it is not functionally equivalent to the original. The only criteria // run without minification, it will produce different output from the code
// is that once the generated reduced test case is run without minification, it // minified with `minify_options`. Returns a `minify` result object.
// will produce different output from the code minified with `minify_options`.
// Returns a `minify` result object with an additonal boolean property `reduced`.
Error.stackTraceLimit = Infinity;
module.exports = function reduce_test(testcase, minify_options, reduce_options) { module.exports = function reduce_test(testcase, minify_options, reduce_options) {
if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string(); if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string();
minify_options = minify_options || { compress: {}, mangle: false }; minify_options = minify_options || {};
reduce_options = reduce_options || {}; reduce_options = reduce_options || {};
var max_iterations = reduce_options.max_iterations || 1000; var max_iterations = reduce_options.max_iterations || 1000;
var max_timeout = reduce_options.max_timeout || 10000; var max_timeout = reduce_options.max_timeout || 10000;
var warnings = [];
var log = reduce_options.log || function(msg) {
warnings.push(msg);
};
var verbose = reduce_options.verbose; var verbose = reduce_options.verbose;
var minify_options_json = JSON.stringify(minify_options, null, 2); var minify_options_json = JSON.stringify(minify_options, null, 2);
var result_cache = Object.create(null); var result_cache = Object.create(null);
var test_for_diff = compare_run_code;
// the initial timeout to assess the viability of the test case must be large // the initial timeout to assess the viability of the test case must be large
var differs = producesDifferentResultWhenMinified(result_cache, testcase, minify_options, max_timeout); var differs = test_for_diff(testcase, minify_options, result_cache, max_timeout);
if (verbose) { if (verbose) {
console.error("// Node.js " + process.version + " on " + os.platform() + " " + os.arch()); log("// Node.js " + process.version + " on " + os.platform() + " " + os.arch());
}
if (differs.error && [ "DefaultsError", "SyntaxError" ].indexOf(differs.error.name) < 0) {
test_for_diff = test_minify;
differs = test_for_diff(testcase, minify_options, result_cache, max_timeout);
} }
if (!differs) { if (!differs) {
// same stdout result produced when minified // same stdout result produced when minified
return { return {
code: "// Can't reproduce test failure with minify options provided:" code: [
+ "\n// " + to_comment(minify_options_json) "// Can't reproduce test failure",
"// minify options: " + to_comment(minify_options_json)
].join("\n"),
warnings: warnings,
}; };
} else if (differs.timed_out) { } else if (differs.timed_out) {
return { return {
code: "// Can't reproduce test failure within " + max_timeout + "ms:" code: [
+ "\n// " + to_comment(minify_options_json) "// Can't reproduce test failure within " + max_timeout + "ms",
"// minify options: " + to_comment(minify_options_json)
].join("\n"),
warnings: warnings,
}; };
} else if (differs.error) { } else if (differs.error) {
differs.warnings = warnings;
return differs; return differs;
} else if (is_error(differs.unminified_result)
&& is_error(differs.minified_result)
&& differs.unminified_result.name == differs.minified_result.name) {
return {
code: [
"// No differences except in error message",
"// minify options: " + to_comment(minify_options_json)
].join("\n"),
warnings: warnings,
};
} else { } else {
max_timeout = Math.min(100 * differs.elapsed, max_timeout); max_timeout = Math.min(100 * differs.elapsed, max_timeout);
// Replace expressions with constants that will be parsed into // Replace expressions with constants that will be parsed into
@@ -71,12 +96,13 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// quick ignores // quick ignores
if (node instanceof U.AST_Accessor) return; if (node instanceof U.AST_Accessor) return;
if (node instanceof U.AST_Directive) return; if (node instanceof U.AST_Directive) return;
if (!in_list && node instanceof U.AST_EmptyStatement) return;
if (node instanceof U.AST_Label) return; if (node instanceof U.AST_Label) return;
if (node instanceof U.AST_LabelRef) return; if (node instanceof U.AST_LabelRef) return;
if (!in_list && node instanceof U.AST_SymbolDeclaration) return; if (!in_list && node instanceof U.AST_SymbolDeclaration) return;
if (node instanceof U.AST_Toplevel) return; if (node instanceof U.AST_Toplevel) return;
var parent = tt.parent(); var parent = tt.parent();
if (node instanceof U.AST_SymbolFunarg && parent instanceof U.AST_Accessor) return;
// ensure that the _permute prop is a number. // ensure that the _permute prop is a number.
// can not use `node.start._permute |= 0;` as it will erase fractional part. // can not use `node.start._permute |= 0;` as it will erase fractional part.
@@ -86,19 +112,18 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// no structural AST changes before this point. // no structural AST changes before this point.
if (node.start._permute >= REPLACEMENTS.length) return; if (node.start._permute >= REPLACEMENTS.length) return;
if (parent instanceof U.AST_Assign // ignore lvalues
&& parent.left === node if (parent instanceof U.AST_Assign && parent.left === node) return;
|| parent instanceof U.AST_Unary if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
&& parent.expression === node case "++":
&& ["++", "--", "delete"].indexOf(parent.operator) >= 0) { case "--":
// ignore lvalues case "delete":
return; return;
} }
if ((parent instanceof U.AST_For || parent instanceof U.AST_ForIn) // preserve for (var xxx; ...)
&& parent.init === node && node instanceof U.AST_Var) { if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Var) return node;
// preserve for (var ...) // preserve for (xxx in ...)
return node; if (parent instanceof U.AST_ForIn && parent.init === node) return node;
}
// node specific permutations with no parent logic // node specific permutations with no parent logic
@@ -111,25 +136,13 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
} }
else if (node instanceof U.AST_Binary) { else if (node instanceof U.AST_Binary) {
CHANGED = true; var permute = ((node.start._permute += step) * steps | 0) % 4;
return [ var expr = [
node.left, node.left,
node.right, node.right,
][ ((node.start._permute += step) * steps | 0) % 2 ]; ][ permute & 1 ];
}
else if (node instanceof U.AST_Catch || node instanceof U.AST_Finally) {
// drop catch or finally block
node.start._permute++;
CHANGED = true; CHANGED = true;
return null; return permute < 2 ? expr : wrap_with_console_log(expr);
}
else if (node instanceof U.AST_Conditional) {
CHANGED = true;
return [
node.condition,
node.consequent,
node.alternative,
][ ((node.start._permute += step) * steps | 0) % 3 ];
} }
else if (node instanceof U.AST_BlockStatement) { else if (node instanceof U.AST_BlockStatement) {
if (in_list) { if (in_list) {
@@ -164,12 +177,26 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_sequence(seq); return to_sequence(seq);
} }
} }
else if (node instanceof U.AST_Catch) {
// drop catch block
node.start._permute++;
CHANGED = true;
return null;
}
else if (node instanceof U.AST_Conditional) {
CHANGED = true;
return [
node.condition,
node.consequent,
node.alternative,
][ ((node.start._permute += step) * steps | 0) % 3 ];
}
else if (node instanceof U.AST_Defun) { else if (node instanceof U.AST_Defun) {
switch (((node.start._permute += step) * steps | 0) % 2) { switch (((node.start._permute += step) * steps | 0) % 2) {
case 0: case 0:
CHANGED = true; CHANGED = true;
return List.skip; return List.skip;
case 1: default:
if (!has_exit(node)) { if (!has_exit(node)) {
// hoist function declaration body // hoist function declaration body
var body = node.body; var body = node.body;
@@ -201,15 +228,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(expr); return to_statement(expr);
} }
} }
else if (node instanceof U.AST_PropAccess) { else if (node instanceof U.AST_Finally) {
var expr = [ // drop finally block
node.expression, node.start._permute++;
node.property instanceof U.AST_Node && node.property, CHANGED = true;
][ node.start._permute++ % 2 ]; return null;
if (expr) {
CHANGED = true;
return expr;
}
} }
else if (node instanceof U.AST_For) { else if (node instanceof U.AST_For) {
var expr = [ var expr = [
@@ -258,6 +281,16 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return expr; return expr;
} }
} }
else if (node instanceof U.AST_PropAccess) {
var expr = [
node.expression,
node.property instanceof U.AST_Node && node.property,
][ node.start._permute++ % 2 ];
if (expr) {
CHANGED = true;
return expr;
}
}
else if (node instanceof U.AST_SimpleStatement) { else if (node instanceof U.AST_SimpleStatement) {
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) { if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body // hoist simple statement IIFE function expression body
@@ -357,15 +390,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
// replace this node // replace this node
var newNode = U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], { var newNode = is_statement(node) ? new U.AST_EmptyStatement({
start: {},
}) : U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
expression: true, expression: true,
}); });
if (is_statement(node)) {
newNode = new U.AST_SimpleStatement({
body: newNode,
start: {},
});
}
newNode.start._permute = ++node.start._permute; newNode.start._permute = ++node.start._permute;
CHANGED = true; CHANGED = true;
return newNode; return newNode;
@@ -389,19 +418,43 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
}); });
var diff_error_message;
for (var pass = 1; pass <= 3; ++pass) { for (var pass = 1; pass <= 3; ++pass) {
var testcase_ast = U.parse(testcase); var testcase_ast = U.parse(testcase);
if (diff_error_message === testcase) {
// only difference detected is in error message, so expose that and try again
testcase_ast.transform(new U.TreeTransformer(function(node, descend) {
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
return to_sequence(node.args);
}
if (node instanceof U.AST_Catch && node.argname) {
descend(node, this);
node.body.unshift(new U.AST_SimpleStatement({
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
start: {},
}));
return node;
}
}));
var code = testcase_ast.print_to_string();
var diff = test_for_diff(code, minify_options, result_cache, max_timeout);
if (diff && !diff.timed_out && !diff.error) {
testcase = code;
differs = diff;
} else {
testcase_ast = U.parse(testcase);
}
}
diff_error_message = null;
testcase_ast.walk(new U.TreeWalker(function(node) { testcase_ast.walk(new U.TreeWalker(function(node) {
// unshare start props to retain visit data between iterations // unshare start props to retain visit data between iterations
node.start = JSON.parse(JSON.stringify(node.start)); node.start = JSON.parse(JSON.stringify(node.start));
node.start._permute = 0; node.start._permute = 0;
})); }));
var before_iterations = testcase;
for (var c = 0; c < max_iterations; ++c) { for (var c = 0; c < max_iterations; ++c) {
if (verbose) { if (verbose && pass == 1 && c % 25 == 0) {
if (pass == 1 && c % 25 == 0) { log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
console.error("// reduce test pass "
+ pass + ", iteration " + c + ": " + testcase.length + " bytes");
}
} }
var CHANGED = false; var CHANGED = false;
var code_ast = testcase_ast.clone(true).transform(tt); var code_ast = testcase_ast.clone(true).transform(tt);
@@ -411,12 +464,12 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} catch (ex) { } catch (ex) {
// AST is not well formed. // AST is not well formed.
// no harm done - just log the error, ignore latest change and continue iterating. // no harm done - just log the error, ignore latest change and continue iterating.
console.error("*** Error generating code from AST."); log("*** Error generating code from AST.");
console.error(ex); log(ex.stack);
console.error("*** Discarding permutation and continuing."); log("*** Discarding permutation and continuing.");
continue; continue;
} }
var diff = producesDifferentResultWhenMinified(result_cache, code, minify_options, max_timeout); var diff = test_for_diff(code, minify_options, result_cache, max_timeout);
if (diff) { if (diff) {
if (diff.timed_out) { if (diff.timed_out) {
// can't trust the validity of `code_ast` and `code` when timed out. // can't trust the validity of `code_ast` and `code` when timed out.
@@ -424,14 +477,15 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} else if (diff.error) { } else if (diff.error) {
// something went wrong during minify() - could be malformed AST or genuine bug. // something went wrong during minify() - could be malformed AST or genuine bug.
// no harm done - just log code & error, ignore latest change and continue iterating. // no harm done - just log code & error, ignore latest change and continue iterating.
console.error("*** Error during minification."); log("*** Error during minification.");
console.error(code); log(code);
console.error(diff.error); log(diff.error.stack);
console.error("*** Discarding permutation and continuing."); log("*** Discarding permutation and continuing.");
} else if (is_error(diff.unminified_result) } else if (is_error(diff.unminified_result)
&& is_error(diff.minified_result) && is_error(diff.minified_result)
&& diff.unminified_result.name == diff.minified_result.name) { && diff.unminified_result.name == diff.minified_result.name) {
// ignore difference in error messages caused by minification // ignore difference in error messages caused by minification
diff_error_message = testcase;
} else { } else {
// latest permutation is valid, so use it as the basis of new changes // latest permutation is valid, so use it as the basis of new changes
testcase_ast = code_ast; testcase_ast = code_ast;
@@ -440,12 +494,12 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
} }
} }
if (c == 0) break; if (before_iterations === testcase) break;
if (verbose) { if (verbose) {
console.error("// reduce test pass " + pass + ": " + testcase.length + " bytes"); log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
} }
} }
testcase = U.minify(testcase, { var beautified = U.minify(testcase, {
compress: false, compress: false,
mangle: false, mangle: false,
output: { output: {
@@ -454,20 +508,55 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
comments: true, comments: true,
}, },
}); });
testcase.code += [ testcase = {
"", code: testcase,
"// output: " + to_comment(differs.unminified_result), };
"// minify: " + to_comment(differs.minified_result), if (!beautified.error) {
"// options: " + to_comment(minify_options_json), diff = test_for_diff(beautified.code, minify_options, result_cache, max_timeout);
].join("\n").replace(/\u001b\[\d+m/g, ""); if (diff && !diff.timed_out && !diff.error) {
testcase = beautified;
testcase.code = "// (beautified)\n" + testcase.code;
differs = diff;
}
}
var lines = [ "" ];
if (isNaN(max_timeout)) {
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack)));
} else {
var unminified_result = strip_color_codes(differs.unminified_result);
var minified_result = strip_color_codes(differs.minified_result);
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
lines.push(
"// (stringified)",
"// output: " + JSON.stringify(unminified_result),
"// minify: " + JSON.stringify(minified_result)
);
} else {
lines.push(
"// output: " + to_comment(unminified_result),
"// minify: " + to_comment(minified_result)
);
}
}
lines.push("// options: " + to_comment(minify_options_json));
testcase.code += lines.join("\n");
testcase.warnings = warnings;
return testcase; return testcase;
} }
}; };
function strip_color_codes(value) {
return ("" + value).replace(/\u001b\[\d+m/g, "");
}
function to_comment(value) { function to_comment(value) {
return ("" + value).replace(/\n/g, "\n// "); return ("" + value).replace(/\n/g, "\n// ");
} }
function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, "");
}
function has_exit(fn) { function has_exit(fn) {
var found = false; var found = false;
var tw = new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
@@ -534,31 +623,59 @@ function to_statement(node) {
}); });
} }
function run_code(result_cache, code, toplevel, timeout) { function wrap_with_console_log(node) {
var key = crypto.createHash("sha1").update(code).digest("base64"); // wrap with console.log()
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout)); return new U.AST_Call({
expression: new U.AST_Dot({
expression: new U.AST_SymbolRef({
name: "console",
start: {},
}),
property: "log",
start: {},
}),
args: [ node ],
start: {},
});
} }
function producesDifferentResultWhenMinified(result_cache, code, minify_options, max_timeout) { function run_code(code, toplevel, result_cache, timeout) {
var key = crypto.createHash("sha1").update(code).digest("base64");
var value = result_cache[key];
if (!value) {
var start = Date.now();
result_cache[key] = value = {
result: sandbox.run_code(code, toplevel, timeout),
elapsed: Date.now() - start,
};
}
return value;
}
function compare_run_code(code, minify_options, result_cache, max_timeout) {
var minified = U.minify(code, minify_options); var minified = U.minify(code, minify_options);
if (minified.error) return minified; if (minified.error) return minified;
var toplevel = minify_options.toplevel; var toplevel = sandbox.has_toplevel(minify_options);
var elapsed = Date.now(); var unminified = run_code(code, toplevel, result_cache, max_timeout);
var unminified_result = run_code(result_cache, code, toplevel, max_timeout); var timeout = Math.min(100 * unminified.elapsed, max_timeout);
elapsed = Date.now() - elapsed; var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
var timeout = Math.min(100 * elapsed, max_timeout);
var minified_result = run_code(result_cache, minified.code, toplevel, timeout);
if (sandbox.same_stdout(unminified_result, minified_result)) { if (sandbox.same_stdout(unminified.result, minified_result)) {
return is_timed_out(unminified_result) && is_timed_out(minified_result) && { return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
timed_out: true, timed_out: true,
}; };
} }
return { return {
unminified_result: unminified_result, unminified_result: unminified.result,
minified_result: minified_result, minified_result: minified_result,
elapsed: elapsed, elapsed: unminified.elapsed,
};
}
function test_minify(code, minify_options) {
var minified = U.minify(code, minify_options);
return minified.error && {
minified_result: minified.error,
}; };
} }
Error.stackTraceLimit = Infinity;

View File

@@ -1,6 +1,3 @@
setInterval(function() {
process.stderr.write("\0");
}, 8 * 60 * 1000).unref();
require("./run")([ require("./run")([
"-b", "-b",
"-b braces", "-b braces",
@@ -12,6 +9,6 @@ require("./run")([
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) { ].map(function(options) {
var args = options.split(/ /); var args = options.split(/ /);
args.unshift("test/benchmark.js"); args.unshift("test/benchmark.js", "--validate");
return args; return args;
})); }));

View File

@@ -40,7 +40,7 @@ function createContext() {
arg.constructor.toString(); arg.constructor.toString();
if (level--) for (var key in arg) { if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key); var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level); if (!desc || !desc.get && !desc.set) arg[key] = safe_log(arg[key], level);
} }
} }
return arg; return arg;
@@ -87,3 +87,8 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
} : function(expected, actual) { } : function(expected, actual) {
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
}; };
exports.has_toplevel = function(options) {
return options.toplevel
|| options.mangle && options.mangle.toplevel
|| options.compress && options.compress.toplevel;
};

67
test/ufuzz/actions.js Normal file
View File

@@ -0,0 +1,67 @@
var get = require("https").get;
var parse = require("url").parse;
var base, token, run_number, eldest = true;
exports.init = function(url, auth, num) {
base = url;
token = auth;
run_number = num;
};
exports.should_stop = function(callback) {
read(base + "/actions/runs?per_page=100", function(reply) {
if (!reply || !Array.isArray(reply.workflow_runs)) return;
var runs = reply.workflow_runs.filter(function(workflow) {
return workflow.status != "completed";
}).sort(function(a, b) {
return b.run_number - a.run_number;
});
var found = false, remaining = 20;
(function next() {
if (!runs.length) return;
var workflow = runs.pop();
if (workflow.event == "schedule" && workflow.run_number == run_number) found = true;
read(workflow.jobs_url, function(reply) {
if (!reply || !Array.isArray(reply.jobs)) return;
if (!reply.jobs.every(function(job) {
if (job.status == "completed") return true;
remaining--;
return found || workflow.event != "schedule";
})) return;
if (remaining >= 0) {
next();
} else {
callback();
}
});
})();
});
};
function read(url, callback) {
var done = function(reply) {
done = function() {};
callback(reply);
};
var options = parse(url);
options.headers = {
"Authorization": "Token " + token,
"User-Agent": "UglifyJS",
};
get(options, function(response) {
var chunks = [];
response.setEncoding("utf8");
response.on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
var reply;
try {
reply = JSON.parse(chunks.join(""))
} catch (e) {}
done(reply);
}).on("error", function() {
done();
});
}).on("error", function() {
done();
});
}

View File

@@ -168,7 +168,7 @@ var VALUES = [
"this", "this",
]; ];
var BINARY_OPS_NO_COMMA = [ var BINARY_OPS = [
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
" - ", " - ",
"/", "/",
@@ -190,9 +190,15 @@ var BINARY_OPS_NO_COMMA = [
"%", "%",
"&&", "&&",
"||", "||",
"^" ]; "^",
",",
var BINARY_OPS = [","].concat(BINARY_OPS_NO_COMMA); ];
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in ");
var ASSIGNMENTS = [ var ASSIGNMENTS = [
"=", "=",
@@ -490,11 +496,16 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var label = createLabel(canBreak, canContinue); var label = createLabel(canBreak, canContinue);
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
var optElementVar = ""; var key = rng(10) ? "key" + loop : getVarName();
if (rng(5) > 1) { return [
optElementVar = "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[key" + loop + "]; "; "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
} label.target + " for (",
return "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; " + label.target + " for (var key" + loop + " in expr" + loop + ") {" + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}}"; /^key/.test(key) ? "var " : "",
key + " in expr" + loop + ") {",
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
"}}",
].join("");
case STMT_SEMI: case STMT_SEMI:
return use_strict && rng(20) === 0 ? '"use strict";' : ";"; return use_strict && rng(20) === 0 ? '"use strict";' : ";";
case STMT_EXPR: case STMT_EXPR:
@@ -742,6 +753,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() "; return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() ";
case p++: case p++:
return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) "; return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
case p++:
return " /[abc4]/g.exec(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
case p++: case p++:
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) +
") || " + rng(10) + ").toString()[" + ") || " + rng(10) + ").toString()[" +
@@ -761,11 +774,23 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
case p++: case p++:
var name = getVarName(); return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow);
return name + " && " + name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; case p++:
return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++: case p++:
var name = getVarName(); var name = getVarName();
return name + " && " + name + "." + getDotKey(); var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++:
var name = getVarName();
var s = name + "." + getDotKey();
return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++:
case p++:
var name = getVarName();
var s = name + "." + getDotKey();
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++: case p++:
case p++: case p++:
case p++: case p++:
@@ -869,7 +894,7 @@ function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
} }
function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow)
+ createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + createBinaryOp(noComma, canThrow) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
} }
function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
// intentionally generate more hardcore ops // intentionally generate more hardcore ops
@@ -919,9 +944,12 @@ function createValue() {
return VALUES[rng(VALUES.length)]; return VALUES[rng(VALUES.length)];
} }
function createBinaryOp(noComma) { function createBinaryOp(noComma, canThrow) {
if (noComma) return BINARY_OPS_NO_COMMA[rng(BINARY_OPS_NO_COMMA.length)]; var op;
return BINARY_OPS[rng(BINARY_OPS.length)]; do {
op = BINARY_OPS[rng(BINARY_OPS.length)];
} while (noComma && op == "," || !canThrow && op == " in ");
return op;
} }
function createAssignment() { function createAssignment() {
@@ -1008,20 +1036,24 @@ function log_suspects(minify_options, component) {
if (!options) return; if (!options) return;
if (typeof options != "object") options = {}; if (typeof options != "object") options = {};
var defs = default_options[component]; var defs = default_options[component];
var toplevel = sandbox.has_toplevel(minify_options);
var suspects = Object.keys(defs).filter(function(name) { var suspects = Object.keys(defs).filter(function(name) {
var flip = name == "keep_fargs"; var flip = name == "keep_fargs";
if (flip ? name in options : (name in options ? options : defs)[name]) { if (flip !== (name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options)); var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options)); var o = JSON.parse(JSON.stringify(options));
o[name] = flip; o[name] = flip;
m[component] = o; m[component] = o;
m.validate = true;
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (typeof uglify_code != "string") {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component + "." + name); errorln("Error testing options." + component + "." + name);
errorln(result.error); errorln(result.error);
} else { } else {
var r = sandbox.run_code(result.code, m.toplevel); var r = sandbox.run_code(result.code, toplevel);
return sandbox.same_stdout(original_result, r); return !sandbox.same_stdout(uglify_result, r);
} }
} }
}); });
@@ -1034,55 +1066,82 @@ function log_suspects(minify_options, component) {
} }
} }
function log_rename(options) { function log_suspects_global(options, toplevel) {
var m = JSON.parse(JSON.stringify(options)); var suspects = Object.keys(default_options).filter(function(component) {
m.rename = false; return typeof default_options[component] != "object";
var result = UglifyJS.minify(original_code, m); }).filter(function(component) {
if (result.error) { var m = JSON.parse(options);
errorln("Error testing options.rename"); m[component] = false;
errorln(result.error); m.validate = true;
} else { var result = UglifyJS.minify(original_code, m);
var r = sandbox.run_code(result.code, m.toplevel); if (typeof uglify_code != "string") {
if (sandbox.same_stdout(original_result, r)) { return !sandbox.same_stdout(uglify_code, result.error);
errorln("Suspicious options:"); } else if (result.error) {
errorln(" rename"); errorln("Error testing options." + component);
errorln(); errorln(result.error);
} else {
var r = sandbox.run_code(result.code, toplevel);
return !sandbox.same_stdout(uglify_result, r);
} }
});
if (suspects.length > 0) {
errorln("Suspicious options:");
suspects.forEach(function(name) {
errorln(" " + name);
});
errorln();
} }
} }
function log(options) { function log(options) {
var options_copy = JSON.parse(options); var toplevel = sandbox.has_toplevel(JSON.parse(options));
options = JSON.parse(options);
if (!ok) errorln("\n\n\n\n\n\n!!!!!!!!!!\n\n\n"); if (!ok) errorln("\n\n\n\n\n\n!!!!!!!!!!\n\n\n");
errorln("//============================================================="); errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round); if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code"); errorln("// original code");
try_beautify(original_code, options.toplevel, original_result, errorln); var beautified = UglifyJS.minify(original_code, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
},
});
if (beautified.error) {
errorln("// !!! beautify failed !!!");
errorln(beautified.error);
errorln("//");
errorln(original_code);
} else {
var uglified = UglifyJS.minify(beautified.code, JSON.parse(options));
var expected, actual;
if (typeof uglify_code != "string" || uglified.error) {
expected = uglify_code;
actual = uglified.error;
} else {
expected = uglify_result;
actual = sandbox.run_code(uglified.code, toplevel);
}
if (sandbox.same_stdout(expected, actual)) {
errorln("// (beautified)");
errorln(beautified.code);
} else {
errorln("//");
errorln(original_code);
}
}
errorln(); errorln();
errorln(); errorln();
errorln("//-------------------------------------------------------------"); errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") { if (typeof uglify_code == "string") {
errorln("// uglified code"); errorln("// uglified code");
try_beautify(uglify_code, options.toplevel, uglify_result, errorln); try_beautify(uglify_code, toplevel, uglify_result, errorln);
errorln(); errorln();
errorln(); errorln();
errorln("original result:"); errorln("original result:");
errorln(original_result); errorln(original_result);
errorln("uglified result:"); errorln("uglified result:");
errorln(uglify_result); errorln(uglify_result);
errorln("//-------------------------------------------------------------");
var reduced = reduce_test(original_code, options_copy, {
verbose: false,
}).code;
if (reduced) {
errorln();
errorln("// reduced test case (output will differ)");
errorln();
errorln(reduced);
errorln();
errorln("//-------------------------------------------------------------");
}
} else { } else {
errorln("// !!! uglify failed !!!"); errorln("// !!! uglify failed !!!");
errorln(uglify_code); errorln(uglify_code);
@@ -1093,16 +1152,38 @@ function log(options) {
errorln(original_result); errorln(original_result);
} }
} }
errorln("//-------------------------------------------------------------");
var reduce_options = JSON.parse(options);
reduce_options.validate = true;
var reduced = reduce_test(original_code, reduce_options, {
verbose: false,
}).code;
if (reduced) {
errorln();
errorln("// reduced test case (output will differ)");
errorln();
errorln(reduced);
errorln();
errorln("//-------------------------------------------------------------");
}
errorln("minify(options):"); errorln("minify(options):");
errorln(JSON.stringify(options, null, 2)); errorln(JSON.stringify(JSON.parse(options), null, 2));
errorln(); errorln();
if (!ok && typeof uglify_code == "string") { if (!ok) {
Object.keys(default_options).forEach(log_suspects.bind(null, options)); Object.keys(default_options).filter(function(component) {
log_rename(options); var defs = default_options[component];
return defs && typeof defs == "object";
}).forEach(log_suspects.bind(null, JSON.parse(options)));
log_suspects_global(options, toplevel);
errorln("!!!!!! Failed... round " + round); errorln("!!!!!! Failed... round " + round);
} }
} }
function sort_globals(code) {
var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
return globals.length ? "var " + globals.join(",") + ";" + code : code;
}
function fuzzy_match(original, uglified) { function fuzzy_match(original, uglified) {
uglified = uglified.split(" "); uglified = uglified.split(" ");
var i = uglified.length; var i = uglified.length;
@@ -1117,6 +1198,66 @@ function fuzzy_match(original, uglified) {
return true; return true;
} }
function patch_try_catch(orig, toplevel) {
var stack = [ {
code: orig,
index: 0,
offset: 0,
tries: [],
} ];
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g;
while (stack.length) {
var code = stack[0].code;
var offset = stack[0].offset;
var tries = stack[0].tries;
var match;
re.lastIndex = stack.shift().index;
while (match = re.exec(code)) {
var index = match.index + match[0].length + 1;
if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset });
continue;
}
var insert;
if (/}\s*finally\s*$/.test(match[0])) {
tries.shift();
insert = 'if (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;';
} else {
while (tries.length && tries[0].catch) tries.shift();
tries[0].catch = index - offset;
insert = [
"if (!" + match[1] + ".ufuzz_var) {",
match[1] + '.ufuzz_var = "' + match[1] + '";',
match[1] + ".ufuzz_try = " + tries[0].try + ";",
match[1] + ".ufuzz_catch = " + tries[0].catch + ";",
"UFUZZ_ERROR = " + match[1] + ";",
"}",
"throw " + match[1] + ";",
].join("\n");
}
var new_code = code.slice(0, index) + insert + code.slice(index);
var result = sandbox.run_code(new_code, toplevel);
if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") {
if (!stack.filled && match[1]) stack.push({
code: code,
index: index && index - 1,
offset: offset,
tries: JSON.parse(JSON.stringify(tries)),
});
offset += insert.length;
code = new_code;
} else if (result.name == "TypeError" && /'in'/.test(result.message)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("invalid `in`");' + orig.slice(index);
} else if (result.name == "RangeError" && result.message == "Maximum call stack size exceeded") {
index = result.ufuzz_try;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
}
}
stack.filled = true;
}
}
var fallback_options = [ JSON.stringify({ var fallback_options = [ JSON.stringify({
compress: false, compress: false,
mangle: false mangle: false
@@ -1133,30 +1274,44 @@ for (var round = 1; round <= num_iterations; round++) {
if (!errored) orig_result.push(sandbox.run_code(original_code, true)); if (!errored) orig_result.push(sandbox.run_code(original_code, true));
(errored ? fallback_options : minify_options).forEach(function(options) { (errored ? fallback_options : minify_options).forEach(function(options) {
var o = JSON.parse(options); var o = JSON.parse(options);
var toplevel = sandbox.has_toplevel(o);
o.validate = true;
uglify_code = UglifyJS.minify(original_code, o); uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[o.toplevel ? 1 : 0]; original_result = orig_result[toplevel ? 1 : 0];
if (!uglify_code.error) { if (!uglify_code.error) {
uglify_code = uglify_code.code; uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code, o.toplevel); uglify_result = sandbox.run_code(uglify_code, toplevel);
ok = sandbox.same_stdout(original_result, uglify_result); ok = sandbox.same_stdout(original_result, uglify_result);
if (!ok && typeof uglify_result == "string" && o.compress.unsafe_math) { // ignore declaration order of global variables
if (!ok && !toplevel) {
ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code)));
}
// ignore numerical imprecision caused by `unsafe_math`
if (!ok && typeof uglify_result == "string" && o.compress && o.compress.unsafe_math) {
ok = fuzzy_match(original_result, uglify_result); ok = fuzzy_match(original_result, uglify_result);
if (!ok) { if (!ok) {
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3")); var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel);
ok = sandbox.same_stdout(fuzzy_result, uglify_result); ok = sandbox.same_stdout(fuzzy_result, uglify_result);
} }
} }
// ignore difference in error message caused by `in`
// ignore difference in depth of termination caused by infinite recursion
if (!ok) {
var orig_skipped = patch_try_catch(original_code, toplevel);
var uglify_skipped = patch_try_catch(uglify_code, toplevel);
if (orig_skipped && uglify_skipped) {
ok = sandbox.same_stdout(sandbox.run_code(orig_skipped, toplevel), sandbox.run_code(uglify_skipped, toplevel));
}
}
} else { } else {
uglify_code = uglify_code.error; uglify_code = uglify_code.error;
if (errored) { ok = errored && uglify_code.name == original_result.name;
ok = uglify_code.name == original_result.name;
}
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (errored) { else if (errored) {
println("//============================================================="); println("//=============================================================");
println("// original code"); println("// original code");
try_beautify(original_code, o.toplevel, original_result, println); try_beautify(original_code, toplevel, original_result, println);
println(); println();
println(); println();
println("original result:"); println("original result:");

View File

@@ -1,39 +1,69 @@
var actions = require("./actions");
var child_process = require("child_process"); var child_process = require("child_process");
var ping = 5 * 60 * 1000; var args = [
var period = +process.argv[2]; "--max-old-space-size=2048",
var endTime = Date.now() + period; "test/ufuzz",
for (var i = 0; i < 2; i++) spawn(endTime); ];
var iterations;
function spawn(endTime) { switch (process.argv.length) {
var child = child_process.spawn("node", [ case 3:
"--max-old-space-size=2048", iterations = +process.argv[2];
"test/ufuzz" args.push(iterations);
], { break;
stdio: [ "ignore", "pipe", "pipe" ] case 5:
}).on("exit", respawn); actions.init(process.argv[2], process.argv[3], +process.argv[4]);
var stdout = ""; break;
child.stdout.on("data", function(data) { default:
stdout += data; throw new Error("invalid parameters");
}
var tasks = [ run(), run() ];
if (iterations) return;
var alive = setInterval(function() {
actions.should_stop(function() {
clearInterval(alive);
tasks.forEach(function(kill) {
kill();
});
}); });
var stderr = ""; }, 8 * 60 * 1000);
child.stderr.on("data", trap).pipe(process.stdout);
var keepAlive = setInterval(function() { function run() {
var end = stdout.lastIndexOf("\r"); var child, stdout, stderr, log;
console.log(stdout.slice(stdout.lastIndexOf("\r", end - 1) + 1, end)); spawn();
stdout = stdout.slice(end + 1); return function() {
}, ping); clearInterval(log);
var timer = setTimeout(function() {
clearInterval(keepAlive);
child.removeListener("exit", respawn); child.removeListener("exit", respawn);
child.kill(); child.kill();
}, endTime - Date.now()); };
function spawn() {
child = child_process.spawn("node", args, {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
stdout = "";
child.stdout.on("data", function(data) {
stdout += data;
});
stderr = "";
child.stderr.on("data", trap).pipe(process.stdout);
log = setInterval(function() {
var end = stdout.lastIndexOf("\r");
console.log(stdout.slice(stdout.lastIndexOf("\r", end - 1) + 1, end));
stdout = stdout.slice(end + 1);
}, 5 * 60 * 1000);
}
function respawn() { function respawn() {
console.log(stdout.replace(/[^\r\n]*\r/g, "")); console.log(stdout.replace(/[^\r\n]*\r/g, ""));
clearInterval(keepAlive); clearInterval(log);
clearTimeout(timer); if (!iterations) {
spawn(endTime); spawn();
} else if (process.exitCode) {
tasks.forEach(function(kill) {
kill();
});
}
} }
function trap(data) { function trap(data) {

456
tools/domprops.html Normal file
View File

@@ -0,0 +1,456 @@
<!doctype html>
<html>
<body>
<script>
!function(G) {
var domprops = [];
var objs = [ G ];
var tagNames = [
"a",
"abbr",
"acronym",
"address",
"applet",
"area",
"article",
"aside",
"audio",
"b",
"base",
"basefont",
"bdi",
"bdo",
"bgsound",
"big",
"blink",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"center",
"checked",
"cite",
"code",
"col",
"colgroup",
"command",
"comment",
"compact",
"content",
"data",
"datalist",
"dd",
"declare",
"defer",
"del",
"details",
"dfn",
"dialog",
"dir",
"disabled",
"div",
"dl",
"dt",
"element",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"font",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"image",
"img",
"input",
"ins",
"isindex",
"ismap",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"listing",
"main",
"map",
"mark",
"marquee",
"math",
"menu",
"menuitem",
"meta",
"meter",
"multicol",
"multiple",
"nav",
"nextid",
"nobr",
"noembed",
"noframes",
"nohref",
"noresize",
"noscript",
"noshade",
"nowrap",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"plaintext",
"pre",
"progress",
"q",
"rb",
"readonly",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"selected",
"shadow",
"slot",
"small",
"source",
"spacer",
"span",
"strike",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"tt",
"u",
"ul",
"var",
"video",
"wbr",
"xmp",
"XXX",
];
for (var n = 0; n < tagNames.length; n++) {
add(document.createElement(tagNames[n]));
}
var nsNames = {
"http://www.w3.org/1998/Math/MathML": [
"annotation",
"annotation-xml",
"maction",
"maligngroup",
"malignmark",
"math",
"menclose",
"merror",
"mfenced",
"mfrac",
"mglyph",
"mi",
"mlabeledtr",
"mlongdiv",
"mmultiscripts",
"mn",
"mo",
"mover",
"mpadded",
"mphantom",
"mprescripts",
"mroot",
"mrow",
"ms",
"mscarries",
"mscarry",
"msgroup",
"msline",
"mspace",
"msqrt",
"msrow",
"mstack",
"mstyle",
"msub",
"msubsup",
"msup",
"mtable",
"mtd",
"mtext",
"mtr",
"munder",
"munderover",
"none",
"semantics",
],
"http://www.w3.org/2000/svg": [
"a",
"altGlyph",
"altGlyphDef",
"altGlyphItem",
"animate",
"animateColor",
"animateMotion",
"animateTransform",
"circle",
"clipPath",
"color-profile",
"cursor",
"defs",
"desc",
"discard",
"ellipse",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"filter",
"font",
"font-face",
"font-face-format",
"font-face-name",
"font-face-src",
"font-face-uri",
"foreignObject",
"g",
"glyph",
"glyphRef",
"hatch",
"hatchpath",
"hkern",
"image",
"line",
"linearGradient",
"marker",
"mask",
"mesh",
"meshgradient",
"meshpatch",
"meshrow",
"metadata",
"missing-glyph",
"mpath",
"path",
"pattern",
"polygon",
"polyline",
"radialGradient",
"rect",
"script",
"set",
"solidcolor",
"stop",
"style",
"svg",
"switch",
"symbol",
"text",
"textPath",
"title",
"tref",
"tspan",
"unknown",
"use",
"view",
"vkern",
],
};
if (document.createElementNS) for (var ns in nsNames) {
for (var n = 0; n < nsNames[ns].length; n++) {
add(document.createElementNS(ns, nsNames[ns][n]));
}
}
var skips = [
G.alert,
G.back,
G.blur,
G.captureEvents,
G.clearImmediate,
G.clearInterval,
G.clearTimeout,
G.close,
G.confirm,
G.console,
G.dump,
G.fetch,
G.find,
G.focus,
G.forward,
G.getAttention,
G.history,
G.home,
G.location,
G.moveBy,
G.moveTo,
G.navigator,
G.open,
G.openDialog,
G.print,
G.process,
G.prompt,
G.resizeBy,
G.resizeTo,
G.setImmediate,
G.setInterval,
G.setTimeout,
G.showModalDialog,
G.sizeToContent,
G.stop,
];
var types = [];
var interfaces = [
"beforeunloadevent",
"compositionevent",
"customevent",
"devicemotionevent",
"deviceorientationevent",
"dragevent",
"event",
"events",
"focusevent",
"hashchangeevent",
"htmlevents",
"keyboardevent",
"messageevent",
"mouseevent",
"mouseevents",
"storageevent",
"svgevents",
"textevent",
"touchevent",
"uievent",
"uievents",
];
var i = 0, full = false;
var addEvent = document.createEvent ? function(type) {
if (~indexOf(types, type)) return;
types.push(type);
for (var j = 0; j < interfaces.length; j++) try {
var event = document.createEvent(interfaces[j]);
event.initEvent(type, true, true);
add(event);
} catch (e) {}
} : function() {};
var scanProperties = Object.getOwnPropertyNames ? function(o, fn) {
var names = Object.getOwnPropertyNames(o);
names.forEach(fn);
for (var k in o) if (!~indexOf(names, k)) fn(k);
} : function(o, fn) {
for (var k in o) fn(k);
};
setTimeout(function next() {
for (var j = 10; --j >= 0 && i < objs.length; i++) {
var o = objs[i];
var skip = ~indexOf(skips, o);
try {
scanProperties(o, function(k) {
if (!~indexOf(domprops, k)) domprops.push(k);
if (/^on/.test(k)) addEvent(k.slice(2));
if (!full) try {
add(o[k]);
} catch (e) {}
});
} catch (e) {}
if (skip || full) continue;
try {
add(o.__proto__);
} catch (e) {}
try {
add(o.prototype);
} catch (e) {}
try {
add(new o());
} catch (e) {}
try {
add(o());
} catch (e) {}
}
if (!full && objs.length > 20000) {
alert(objs.length);
full = true;
}
if (i < objs.length) {
setTimeout(next, 0);
} else {
document.write('<pre>[\n "' + domprops.sort().join('",\n "').replace(/&/g, "&amp;").replace(/</g, "&lt;") + '"\n]</pre>');
}
}, 0);
function add(o) {
if (o) switch (typeof o) {
case "function":
case "object":
if (!~indexOf(objs, o)) objs.push(o);
}
}
function indexOf(list, value) {
var j = list.length;
while (--j >= 0) {
if (list[j] === value) break;
}
return j;
}
}(function() {
return this;
}());
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ process.exit = function() {
process.once("uncaughtException", function() { process.once("uncaughtException", function() {
(function callback() { (function callback() {
if (process.stdout.bufferSize || process.stderr.bufferSize) { if (process.stdout.bufferSize || process.stderr.bufferSize) {
setImmediate(callback); setTimeout(callback, 1);
} else { } else {
exit.apply(process, args); exit.apply(process, args);
} }

View File

@@ -15,13 +15,13 @@ exports.FILES = [
require.resolve("./exports.js"), require.resolve("./exports.js"),
]; ];
new Function("MOZ_SourceMap", "exports", function() { new Function("exports", function() {
var code = exports.FILES.map(function(file) { var code = exports.FILES.map(function(file) {
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}); });
code.push("exports.describe_ast = " + describe_ast.toString()); code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n"); return code.join("\n\n");
}())(require("source-map"), exports); }())(exports);
function describe_ast() { function describe_ast() {
var out = OutputStream({ beautify: true }); var out = OutputStream({ beautify: true });
@@ -66,8 +66,8 @@ function infer_options(options) {
} }
exports.default_options = function() { exports.default_options = function() {
var defs = {}; var defs = infer_options({ 0: 0 });
Object.keys(infer_options({ 0: 0 })).forEach(function(component) { Object.keys(defs).forEach(function(component) {
var options = {}; var options = {};
options[component] = { 0: 0 }; options[component] = { 0: 0 };
if (options = infer_options(options)) { if (options = infer_options(options)) {

View File

@@ -1,540 +0,0 @@
<!doctype html>
<html>
<body>
<script>
!function() {
var names = [];
var scanned = [];
var to_scan = [];
function scan(obj) {
if (obj && typeof obj == "object" && !~scanned.indexOf(obj)) {
scanned.push(obj);
to_scan.push(obj);
}
}
scan(self);
[
"a",
"abbr",
"acronym",
"address",
"applet",
"area",
"article",
"aside",
"audio",
"b",
"base",
"basefont",
"bdi",
"bdo",
"bgsound",
"big",
"blink",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"center",
"checked",
"cite",
"code",
"col",
"colgroup",
"command",
"comment",
"compact",
"content",
"data",
"datalist",
"dd",
"declare",
"defer",
"del",
"details",
"dfn",
"dialog",
"dir",
"disabled",
"div",
"dl",
"dt",
"element",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"font",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"image",
"img",
"input",
"ins",
"isindex",
"ismap",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"listing",
"main",
"map",
"mark",
"marquee",
"math",
"menu",
"menuitem",
"meta",
"meter",
"multicol",
"multiple",
"nav",
"nobr",
"noembed",
"noframes",
"nohref",
"noresize",
"noscript",
"noshade",
"nowrap",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"plaintext",
"pre",
"progress",
"q",
"rb",
"readonly",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"selected",
"shadow",
"small",
"source",
"spacer",
"span",
"strike",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"tt",
"u",
"ul",
"var",
"video",
"wbr",
"xmp",
"XXX",
].forEach(function(tag) {
scan(document.createElement(tag));
});
[
"abort",
"absolutedeviceorientation",
"activate",
"active",
"addsourcebuffer",
"addstream",
"addtrack",
"afterprint",
"afterscriptexecute",
"afterupdate",
"animationcancel",
"animationend",
"animationiteration",
"animationstart",
"appinstalled",
"audioend",
"audioprocess",
"audiostart",
"autocomplete",
"autocompleteerror",
"auxclick",
"beforeactivate",
"beforecopy",
"beforecut",
"beforedeactivate",
"beforeeditfocus",
"beforeinstallprompt",
"beforepaste",
"beforeprint",
"beforescriptexecute",
"beforeunload",
"beforeupdate",
"blocked",
"blur",
"bounce",
"boundary",
"cached",
"cancel",
"candidatewindowhide",
"candidatewindowshow",
"candidatewindowupdate",
"canplay",
"canplaythrough",
"cellchange",
"change",
"chargingchange",
"chargingtimechange",
"checking",
"click",
"close",
"compassneedscalibration",
"complete",
"connect",
"connecting",
"connectionstatechange",
"contextmenu",
"controllerchange",
"controlselect",
"copy",
"cuechange",
"cut",
"dataavailable",
"datachannel",
"datasetchanged",
"datasetcomplete",
"dblclick",
"deactivate",
"devicechange",
"devicelight",
"devicemotion",
"deviceorientation",
"deviceorientationabsolute",
"deviceproximity",
"dischargingtimechange",
"disconnect",
"display",
"downloading",
"drag",
"dragend",
"dragenter",
"dragexit",
"dragleave",
"dragover",
"dragstart",
"drop",
"durationchange",
"emptied",
"encrypted",
"end",
"ended",
"enter",
"enterpictureinpicture",
"error",
"errorupdate",
"exit",
"filterchange",
"finish",
"focus",
"focusin",
"focusout",
"freeze",
"fullscreenchange",
"fullscreenerror",
"gesturechange",
"gestureend",
"gesturestart",
"gotpointercapture",
"hashchange",
"help",
"icecandidate",
"iceconnectionstatechange",
"icegatheringstatechange",
"inactive",
"input",
"invalid",
"keydown",
"keypress",
"keyup",
"languagechange",
"layoutcomplete",
"leavepictureinpicture",
"levelchange",
"load",
"loadeddata",
"loadedmetadata",
"loadend",
"loading",
"loadingdone",
"loadingerror",
"loadstart",
"losecapture",
"lostpointercapture",
"mark",
"message",
"messageerror",
"mousedown",
"mouseenter",
"mouseleave",
"mousemove",
"mouseout",
"mouseover",
"mouseup",
"mousewheel",
"move",
"moveend",
"movestart",
"mozfullscreenchange",
"mozfullscreenerror",
"mozorientationchange",
"mozpointerlockchange",
"mozpointerlockerror",
"mscontentzoom",
"msfullscreenchange",
"msfullscreenerror",
"msgesturechange",
"msgesturedoubletap",
"msgestureend",
"msgesturehold",
"msgesturestart",
"msgesturetap",
"msgotpointercapture",
"msinertiastart",
"mslostpointercapture",
"msmanipulationstatechanged",
"msneedkey",
"msorientationchange",
"mspointercancel",
"mspointerdown",
"mspointerenter",
"mspointerhover",
"mspointerleave",
"mspointermove",
"mspointerout",
"mspointerover",
"mspointerup",
"mssitemodejumplistitemremoved",
"msthumbnailclick",
"negotiationneeded",
"nomatch",
"noupdate",
"obsolete",
"offline",
"online",
"open",
"orientationchange",
"pagechange",
"pagehide",
"pageshow",
"paste",
"pause",
"play",
"playing",
"pluginstreamstart",
"pointercancel",
"pointerdown",
"pointerenter",
"pointerleave",
"pointerlockchange",
"pointerlockerror",
"pointermove",
"pointerout",
"pointerover",
"pointerup",
"popstate",
"progress",
"propertychange",
"ratechange",
"reading",
"readystatechange",
"rejectionhandled",
"removesourcebuffer",
"removestream",
"removetrack",
"reset",
"resize",
"resizeend",
"resizestart",
"resourcetimingbufferfull",
"result",
"resume",
"rowenter",
"rowexit",
"rowsdelete",
"rowsinserted",
"scroll",
"search",
"seeked",
"seeking",
"select",
"selectionchange",
"selectstart",
"show",
"signalingstatechange",
"soundend",
"soundstart",
"sourceclose",
"sourceclosed",
"sourceended",
"sourceopen",
"speechend",
"speechstart",
"stalled",
"start",
"statechange",
"stop",
"storage",
"storagecommit",
"submit",
"success",
"suspend",
"textinput",
"timeout",
"timeupdate",
"toggle",
"touchcancel",
"touchend",
"touchmove",
"touchstart",
"track",
"transitioncancel",
"transitionend",
"transitionrun",
"transitionstart",
"unhandledrejection",
"unload",
"updateready",
"upgradeneeded",
"userproximity",
"versionchange",
"visibilitychange",
"voiceschanged",
"volumechange",
"vrdisplayactivate",
"vrdisplayconnect",
"vrdisplaydeactivate",
"vrdisplaydisconnect",
"vrdisplaypresentchange",
"waiting",
"waitingforkey",
"warning",
"webkitanimationend",
"webkitanimationiteration",
"webkitanimationstart",
"webkitcurrentplaybacktargetiswirelesschanged",
"webkitfullscreenchange",
"webkitfullscreenerror",
"webkitkeyadded",
"webkitkeyerror",
"webkitkeymessage",
"webkitneedkey",
"webkitorientationchange",
"webkitplaybacktargetavailabilitychanged",
"webkitpointerlockchange",
"webkitpointerlockerror",
"webkitresourcetimingbufferfull",
"webkittransitionend",
"wheel",
"zoom",
].forEach(function(type) {
[
"beforeunloadevent",
"compositionevent",
"customevent",
"devicemotionevent",
"deviceorientationevent",
"dragevent",
"event",
"events",
"focusevent",
"hashchangeevent",
"htmlevents",
"keyboardevent",
"messageevent",
"mouseevent",
"mouseevents",
"storageevent",
"svgevents",
"textevent",
"touchevent",
"uievent",
"uievents",
].forEach(function(interface) {
try {
var event = document.createEvent(interface);
event.initEvent(type, true, true);
scan(event);
} catch (e) {}
});
});
var obj;
while (obj = to_scan.shift()) {
var proto = obj;
do {
Object.getOwnPropertyNames(proto).forEach(function(name) {
var visited = ~names.indexOf(name);
if (!visited) names.push(name);
try {
scan(obj[name]);
if (visited) return;
if (/^create/.test(name)) {
scan(obj[name]());
}
if (/^[A-Z]/.test(name)) {
scan(new obj[name]());
}
} catch (e) {}
});
} while (proto = Object.getPrototypeOf(proto));
}
names.sort();
document.write('<pre>[\n "');
document.write(names.join('",\n "'));
document.write('"\n]</pre>');
}();
</script>
</body>
</html>