Compare commits

...

192 Commits

Author SHA1 Message Date
Alex Lam S.L
6c7226c10e v3.11.3 2020-10-19 07:25:47 +08:00
Alex Lam S.L
dc575919e2 fix corner case in side_effects (#4226)
fixes #4225
2020-10-18 22:13:10 +08:00
Alex Lam S.L
4298201938 flush stdout from ufuzz jobs properly (#4224) 2020-10-16 21:56:54 +08:00
Alex Lam S.L
4f833937fe fix corner case in inline (#4223)
fixes #4222
2020-10-15 21:52:40 +08:00
Alex Lam S.L
3d71e97dd1 fix corner cases in braces & sequences (#4221)
fixes #4220
2020-10-14 23:39:35 +08:00
Alex Lam S.L
7f35d9cee0 fix corner case in reduce_vars (#4219)
fixes #4218
2020-10-14 07:58:04 +08:00
Alex Lam S.L
9f8106e1d8 fix corner case in collapse_vars (#4217)
fixes #4216
2020-10-14 07:18:26 +08:00
Alex Lam S.L
b7b8435721 fix corner case in evaluate (#4215)
fixes #4214
2020-10-14 02:49:45 +08:00
Alex Lam S.L
c0c04c33bb fix corner cases in dead_code & reduce_vars (#4213)
fixes #4212
2020-10-14 00:09:17 +08:00
Alex Lam S.L
0e234a25c5 fix corner case in reduce_vars (#4211)
fixes #4210
2020-10-13 15:52:03 +08:00
Alex Lam S.L
3096f6fdad restore inline functionality disabled by #4204 (#4209) 2020-10-13 09:33:49 +08:00
Alex Lam S.L
176c09c6a5 fix corner case in reduce_vars & unused (#4208)
fixes #4207
2020-10-13 07:32:17 +08:00
Alex Lam S.L
9272f662c0 fix corner case in collapse_vars (#4206)
fixes #4205
2020-10-13 01:30:21 +08:00
Alex Lam S.L
4d33cb2f94 fix corner case in inilne (#4204)
fixes #4202
2020-10-12 23:10:32 +08:00
Alex Lam S.L
00d0eda85b fix corner case in arguments (#4201)
fixes #4200
2020-10-12 19:03:21 +08:00
Alex Lam S.L
1cdf810f0b fix corner case in reduce_vars (#4203)
fixes #4198
2020-10-12 19:02:44 +08:00
Alex Lam S.L
b512726cf3 fix corner case in collapse_vars (#4199)
fixes #4197
2020-10-12 14:13:17 +08:00
Alex Lam S.L
9b7a13c8c7 fix corner case in ie8 & mangle (#4196)
fixes #4195
2020-10-12 12:43:26 +08:00
Alex Lam S.L
74ff6ce261 fix corner case in dead_code (#4194)
fixes #4193
2020-10-12 11:09:26 +08:00
Alex Lam S.L
b1b8898e7c fix corner case in functions (#4192)
fixes #4191
2020-10-12 09:26:56 +08:00
Alex Lam S.L
55451e7b78 support const (#4190) 2020-10-12 01:18:57 +08:00
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
68 changed files with 14055 additions and 2941 deletions

View File

@@ -1,5 +1,8 @@
name: CI
on: [ push, pull_request ]
on:
pull_request:
push:
branches: [ master ]
jobs:
test:
strategy:
@@ -19,7 +22,7 @@ jobs:
TYPE: ${{ matrix.script }}
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
- uses: actions/cache@v2
with:
path: tmp
key: tmp ${{ matrix.script }}

View File

@@ -2,7 +2,12 @@ name: Fuzzing
on:
pull_request:
schedule:
- cron: "*/8 * * * *"
- 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:
ufuzz:
strategy:
@@ -17,13 +22,13 @@ jobs:
shell: bash
run: |
git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add 10 && nvs use 10'; do
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 use 10
nvs use 8
node --version
npm config set audit false
npm config set optional false
@@ -32,4 +37,8 @@ jobs:
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
node test/ufuzz/job 3600000
if [[ $CAUSE == "schedule" ]]; then
node test/ufuzz/job $BASE_URL $TOKEN $RUN_NUM
else
node test/ufuzz/job 5000
fi

View File

@@ -212,17 +212,16 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(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
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=['$','require','exports']
uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed.
to prevent the `require`, `exports` and `$` names from being changed.
### CLI mangling property names (`--mangle-props`)
@@ -657,8 +656,8 @@ to be `false` and all symbol names will be omitted.
- `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
and the `compress` option `toplevel` enabled.
works best with `toplevel` and `mangle` enabled, alongside with `compress` option
`passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
@@ -689,6 +688,8 @@ to be `false` and all symbol names will be omitted.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.
- `merge_vars` (default: `true`) -- combine and reuse variables.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
@@ -1157,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.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`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,185 +8,252 @@ require("../tools/exit");
var fs = require("fs");
var info = require("../package.json");
var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var files = {};
var options = {
compress: false,
mangle: false
var options = {};
var short_forms = {
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);
program.parseArgv = program.parse;
program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var toplevels = [];
var padding = "";
var options = UglifyJS.default_options();
for (var name in options) {
var option = options[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]);
var args = process.argv.slice(2);
var paths = [];
var output, nameCache;
var specified = {};
while (args.length) {
var arg = args.shift();
if (arg[0] != "-") {
paths.push(arg);
} else if (arg == "--") {
paths = paths.concat(args);
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);
});
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js());
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js());
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
program.option("-O, --output-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("--validate", "Perform validation during AST manipulations.");
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;
}
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);
}
function read_value(required) {
if (no_value || !args.length || args[0][0] == "-") {
if (required) fatal("missing option argument for --" + name);
return true;
}
return args.shift();
}
}
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
fatal("cannot write source map to STDOUT");
}
[
"compress",
"enclose",
"ie8",
"mangle",
"sourceMap",
"toplevel",
"validate",
"wrap"
].forEach(function(name) {
if (name in program) {
options[name] = program[name];
}
if (!output && options.sourceMap && options.sourceMap.url != "inline") 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;
});
if (program.verbose) {
options.warnings = "verbose";
} else if (program.warn) {
options.warnings = true;
if (options.mangle && options.mangle.properties) {
if (options.mangle.properties.domprops) {
delete options.mangle.properties.domprops;
} 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) {
UglifyJS.AST_Node.log_function(print_error, options.warnings == "verbose");
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) {
return name;
};
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
if (typeof options.sourceMap == "object" && "base" in options.sourceMap) {
convert_path = function() {
var base = program.sourceMap.base;
var base = options.sourceMap.base;
delete options.sourceMap.base;
return function(name) {
return path.relative(base, name);
};
}();
}
if (program.self) {
if (program.args.length) UglifyJS.AST_Node.warn("Ignoring input files since --self was passed");
if (specified["self"]) {
if (paths.length) UglifyJS.AST_Node.warn("Ignoring input files since --self was passed");
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) {
files[convert_path(name)] = read_file(name);
});
run();
} else if (program.args.length) {
simple_glob(program.args).forEach(function(name) {
paths = UglifyJS.FILES;
}
if (paths.length) {
simple_glob(paths).forEach(function(name) {
files[convert_path(name)] = read_file(name);
});
run();
@@ -207,15 +274,14 @@ function convert_ast(fn) {
}
function run() {
var content = program.sourceMap && program.sourceMap.content;
var content = options.sourceMap && options.sourceMap.content;
if (content && content != "inline") {
UglifyJS.AST_Node.info("Using input source map: " + content);
options.sourceMap.content = read_file(content, content);
}
if (program.timings) options.timings = true;
try {
if (program.parse) {
if (program.parse.acorn) {
if (options.parse) {
if (options.parse.acorn) {
files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], {
locations: true,
@@ -223,7 +289,7 @@ function run() {
sourceFile: name
});
});
} else if (program.parse.spidermonkey) {
} else if (options.parse.spidermonkey) {
files = convert_ast(function(toplevel, name) {
var obj = JSON.parse(files[name]);
if (!toplevel) return obj;
@@ -235,17 +301,17 @@ function run() {
} catch (ex) {
fatal(ex);
}
if (program.reduceTest) {
// load on demand - assumes dev tree checked out
var result;
if (specified["reduce-test"]) {
// load on demand - assumes cloned repository
var reduce_test = require("../test/reduce");
var testcase = files[0] || files[Object.keys(files)[0]];
var result = reduce_test(testcase, options, {
if (Object.keys(files).length != 1) fatal("can only test on a single file");
result = reduce_test(files[Object.keys(files)[0]], options, {
log: print_error,
verbose: true,
});
}
else {
var result = UglifyJS.minify(files, options);
} else {
result = UglifyJS.minify(files, options);
}
if (result.error) {
var ex = result.error;
@@ -275,9 +341,18 @@ function run() {
print_error(format_object(ex.defs));
}
fatal(ex);
} else if (program.output == "ast") {
} else if (output == "ast") {
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) {
if (value) switch (key) {
@@ -304,26 +379,22 @@ function run() {
}
return value;
}, 2));
} else if (program.output == "spidermonkey") {
} else if (output == "spidermonkey") {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
},
}).ast.to_mozilla_ast(), null, 2));
} else if (program.output) {
fs.writeFileSync(program.output, result.code);
if (result.map) {
fs.writeFileSync(program.output + ".map", result.map);
}
} else if (output) {
fs.writeFileSync(output, result.code);
if (result.map) fs.writeFileSync(output + ".map", result.map);
} else {
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
}
if (nameCache) fs.writeFileSync(nameCache, JSON.stringify(options.nameCache));
if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
@@ -379,47 +450,45 @@ function read_file(path, default_value) {
}
}
function parse_js(flag) {
return function(value, options) {
options = options || {};
try {
UglifyJS.parse(value, {
expression: true
}).walk(new UglifyJS.TreeWalker(function(node) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (flag) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
} else {
options[name] = to_string(value);
}
return true;
function parse_js(value, options, flag) {
if (!options || typeof options != "object") options = {};
if (typeof value == "string") try {
UglifyJS.parse(value, {
expression: true
}).walk(new UglifyJS.TreeWalker(function(node) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (flag) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
} else {
options[name] = to_string(value);
}
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 true;
}
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) {

View File

@@ -120,6 +120,20 @@ var AST_Node = DEFNODE("Node", "start end", {
ctor.prototype._validate.call(this);
} while (ctor = ctor.BASE);
},
validate_ast: function() {
var marker = {};
this.walk(new TreeWalker(function(node) {
if (node.validate_visited === marker) {
throw new Error(string_template("cannot reuse {type} from [{file}:{line},{col}]", {
type: "AST_" + node.TYPE,
file: node.start.file,
line: node.start.line,
col: node.start.col,
}));
}
node.validate_visited = marker;
}));
},
}, null);
(AST_Node.log_function = function(fn, verbose) {
@@ -189,6 +203,10 @@ var AST_Directive = DEFNODE("Directive", "value quote", {
},
}, AST_Statement);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)"
}, AST_Statement);
function must_be_expression(node, prop) {
if (!(node[prop] instanceof AST_Node)) throw new Error(prop + " must be AST_Node");
if (node[prop] instanceof AST_Statement && !(node[prop] instanceof AST_Function)) {
@@ -212,6 +230,34 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
},
}, AST_Statement);
var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", {
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
functions: "[Object/S] like `variables`, but only lists function declarations",
parent_scope: "[AST_Scope?/S] link to the parent scope",
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
},
clone: function(deep) {
var node = this._clone(deep);
if (this.enclosed) node.enclosed = this.enclosed.slice();
if (this.functions) node.functions = this.functions.clone();
if (this.variables) node.variables = this.variables.clone();
return node;
},
pinned: function() {
return this.resolve().pinned();
},
resolve: function() {
return this.parent_scope.resolve();
},
_validate: function() {
if (this.parent_scope == null) return;
if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
},
}, AST_Statement);
function walk_body(node, visitor) {
node.body.forEach(function(node) {
node.walk(visitor);
@@ -235,16 +281,12 @@ var AST_Block = DEFNODE("Block", "body", {
if (node instanceof AST_Function) throw new Error("body cannot contain AST_Function");
});
},
}, AST_Statement);
}, AST_BlockScope);
var AST_BlockStatement = DEFNODE("BlockStatement", null, {
$documentation: "A block statement",
}, AST_Block);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)"
}, AST_Statement);
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
$propdoc: {
@@ -254,7 +296,7 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
if (this.body instanceof AST_Function) throw new Error("body cannot be AST_Function");
},
}, AST_Statement);
}, AST_BlockScope);
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
$documentation: "Statement with a label",
@@ -274,10 +316,13 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
var label = node.label;
var def = this.label;
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_LoopControl && node.label && node.label.thedef === def) {
if (node instanceof AST_LoopControl) {
if (!node.label || node.label.thedef !== def) return;
node.label.thedef = label;
label.references.push(node);
return true;
}
if (node instanceof AST_Scope) return true;
}));
}
return node;
@@ -395,32 +440,16 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
functions: "[Object/S] like `variables`, but only lists function declarations",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
parent_scope: "[AST_Scope?/S] link to the parent scope",
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
},
clone: function(deep) {
var node = this._clone(deep);
if (this.variables) node.variables = this.variables.clone();
if (this.functions) node.functions = this.functions.clone();
if (this.enclosed) node.enclosed = this.enclosed.slice();
return node;
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
},
pinned: function() {
return this.uses_eval || this.uses_with;
},
_validate: function() {
if (this.parent_scope != null) {
if (!(this.parent_scope instanceof AST_Scope)) throw new Error("parent_scope must be AST_Scope");
}
},
resolve: return_this,
}, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
@@ -614,6 +643,9 @@ var AST_Switch = DEFNODE("Switch", "expression", {
},
_validate: function() {
must_be_expression(this, "expression");
this.body.forEach(function(node) {
if (!(node instanceof AST_SwitchBranch)) throw new Error("body must be AST_SwitchBranch[]");
});
},
}, AST_Block);
@@ -671,17 +703,19 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
var AST_Catch = DEFNODE("Catch", "argname", {
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
$propdoc: {
argname: "[AST_SymbolCatch] symbol for the exception"
argname: "[AST_SymbolCatch?] symbol for the exception, or null if not present",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.argname.walk(visitor);
if (node.argname) node.argname.walk(visitor);
walk_body(node, visitor);
});
},
_validate: function() {
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
if (this.argname != null) {
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
}
},
}, AST_Block);
@@ -703,14 +737,30 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
defn.walk(visitor);
});
});
}
},
_validate: function() {
if (this.definitions.length < 1) throw new Error("must have at least one definition");
},
}, AST_Statement);
var AST_Const = DEFNODE("Const", null, {
$documentation: "A `const` statement",
_validate: function() {
this.definitions.forEach(function(node) {
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
if (!(node.name instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst");
must_be_expression(node, "value");
});
},
}, AST_Definitions);
var AST_Var = DEFNODE("Var", null, {
$documentation: "A `var` statement",
_validate: function() {
this.definitions.forEach(function(node) {
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
if (!(node.name instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
if (node.value != null) must_be_expression(node, "value");
});
},
}, AST_Definitions);
@@ -728,10 +778,6 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
if (node.value) node.value.walk(visitor);
});
},
_validate: function() {
if (!(this.name instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
if (this.value != null) must_be_expression(this, "value");
},
});
/* -----[ OTHER ]----- */
@@ -997,12 +1043,12 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
}, AST_ObjectProperty);
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
$documentation: "Base class for all symbols",
$propdoc: {
name: "[string] name of this symbol",
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
thedef: "[SymbolDef/S] the definition of this symbol"
},
$documentation: "Base class for all symbols",
_validate: function() {
if (typeof this.name != "string") throw new Error("name must be string");
},
@@ -1016,6 +1062,10 @@ var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
}, AST_Symbol);
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
$documentation: "Symbol defining a constant",
}, AST_SymbolDeclaration);
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
$documentation: "Symbol defining a variable",
}, AST_SymbolDeclaration);

File diff suppressed because it is too large Load Diff

View File

@@ -178,13 +178,17 @@ function minify(files, options) {
toplevel = toplevel[action](option);
files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
});
if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now();
if (options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
}
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (options.compress) {
toplevel = new Compressor(options.compress).compress(toplevel);
if (options.validate) toplevel.validate_ast();
}
if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
@@ -193,9 +197,7 @@ function minify(files, options) {
toplevel.mangle_names(options.mangle);
}
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
if (timings) timings.output = Date.now();
var result = {};
if (options.output.ast) {

View File

@@ -100,7 +100,7 @@ function OutputStream(options) {
}
}
var indentation = 0;
var indentation = options.indent_start;
var current_col = 0;
var current_line = 1;
var current_pos = 0;
@@ -191,10 +191,6 @@ function OutputStream(options) {
return name;
}
function make_indent(back) {
return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
}
/* -----[ beautification/minification ]----- */
var has_parens = false;
@@ -305,6 +301,7 @@ function OutputStream(options) {
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
OUTPUT += " ";
current_col++;
@@ -344,9 +341,7 @@ function OutputStream(options) {
};
var indent = options.beautify ? function(half) {
if (options.beautify) {
print(make_indent(half ? 0.5 : 0));
}
print(repeat_string(" ", half ? indentation - (options.indent_level >> 1) : indentation));
} : noop;
var with_indent = options.beautify ? function(col, cont) {
@@ -574,9 +569,9 @@ function OutputStream(options) {
get : get,
toString : get,
indent : indent,
indentation : function() { return indentation },
current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width },
should_break : readonly ? noop : function() {
return options.width && current_col - indentation >= options.width;
},
has_parens : function() { return has_parens },
newline : newline,
print : print,
@@ -629,13 +624,7 @@ function OutputStream(options) {
var use_asm = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
var self = this, generator = self._codegen;
function doit() {
stream.prepend_comments(self);
self.add_source_map(stream);
generator(self, stream);
stream.append_comments(self);
}
var self = this;
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(doit);
@@ -643,9 +632,14 @@ function OutputStream(options) {
doit();
}
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) {
var s = OutputStream(options);
this.print(s);
@@ -688,78 +682,66 @@ function OutputStream(options) {
PARENS(AST_Unary, function(output) {
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this;
return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this;
});
PARENS(AST_Sequence, function(output) {
var p = output.parent();
// (foo, bar)() or foo(1, (2, 3), 4)
return p instanceof AST_Call
// !(foo, bar, baz)
|| p instanceof AST_Unary
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
return p instanceof AST_Array
// 1 + (2, 3) + 4 ==> 8
|| p instanceof AST_Binary
// var a = (1, 2), b = a + a; ==> b == 4
|| p instanceof AST_VarDef
// (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
// new (foo, bar) or foo(1, (2, 3), 4)
|| p instanceof AST_Call
// (false, true) ? (a = 10, b = 20) : (c = 30)
// ==> 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) {
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)
if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so];
if (pp > sp
|| (pp == sp
&& this === p.right)) {
return true;
}
return pp > sp || (pp == sp && this === p.right);
}
// (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) {
var node = this;
var p = output.parent();
if (p instanceof AST_New && p.expression === this) {
// i.e. new (foo.bar().baz)
if (p instanceof AST_New && p.expression === node) {
// i.e. new (foo().bar)
//
// if there's one call into this subtree, then we need
// parens around it too, otherwise the call will be
// interpreted as passing the arguments to the upper New
// expression.
var parens = false;
this.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Call) {
parens = true;
return true;
}
}));
return parens;
do {
node = node.expression;
} while (node instanceof AST_PropAccess);
return node.TYPE == "Call";
}
});
PARENS(AST_Call, function(output) {
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
if (output.option('webkit')) {
var g = output.parent(1);
@@ -772,11 +754,12 @@ function OutputStream(options) {
});
PARENS(AST_New, function(output) {
if (need_constructor_parens(this, output)) return false;
var p = output.parent();
if (!need_constructor_parens(this, output)
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
return true;
// (new foo)(bar)
if (p instanceof AST_Call) return p.expression === this;
// (new Date).getTime(), (new Date)["getTime"]()
return p instanceof AST_PropAccess;
});
PARENS(AST_Number, function(output) {
@@ -785,36 +768,29 @@ function OutputStream(options) {
var value = this.value;
// https://github.com/mishoo/UglifyJS/issues/115
// https://github.com/mishoo/UglifyJS/pull/1009
if (value < 0 || /^0/.test(make_num(value))) {
return true;
}
return value < 0 || /^0/.test(make_num(value));
}
});
PARENS([ AST_Assign, AST_Conditional ], function(output) {
var p = output.parent();
// !(a = false) → true
if (p instanceof AST_Unary)
return true;
// 1 + (a = 2) + 3 → 6, side effect setting a = 2
if (p instanceof AST_Binary && !(p instanceof AST_Assign))
return true;
if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
// (a = func)() —or— new (a = Object)()
if (p instanceof AST_Call && p.expression === this)
return true;
if (p instanceof AST_Call) return p.expression === this;
// (a = foo) ? bar : baz
if (p instanceof AST_Conditional && p.condition === this)
return true;
if (p instanceof AST_Conditional) return p.condition === this;
// (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess && p.expression === this)
return true;
if (p instanceof AST_PropAccess) return p.expression === this;
// !(a = false) → true
if (p instanceof AST_Unary) return true;
});
/* -----[ PRINTERS ]----- */
DEFPRINT(AST_Directive, function(self, output) {
var quote = self.quote;
var value = self.value;
DEFPRINT(AST_Directive, function(output) {
var quote = this.quote;
var value = this.value;
switch (output.option("quote_style")) {
case 0:
case 2:
@@ -827,7 +803,7 @@ function OutputStream(options) {
output.print(quote + value + quote);
output.semicolon();
});
DEFPRINT(AST_Debugger, function(self, output) {
DEFPRINT(AST_Debugger, function(output) {
output.print("debugger");
output.semicolon();
});
@@ -859,25 +835,21 @@ function OutputStream(options) {
use_asm = was_asm;
}
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
force_statement(this.body, output);
});
DEFPRINT(AST_Statement, function(self, output) {
self.body.print(output);
DEFPRINT(AST_Statement, function(output) {
this.body.print(output);
output.semicolon();
});
DEFPRINT(AST_Toplevel, function(self, output) {
display_body(self.body, true, output, true);
DEFPRINT(AST_Toplevel, function(output) {
display_body(this.body, true, output, true);
output.print("");
});
DEFPRINT(AST_LabeledStatement, function(self, output) {
self.label.print(output);
DEFPRINT(AST_LabeledStatement, function(output) {
this.label.print(output);
output.colon();
self.body.print(output);
this.body.print(output);
});
DEFPRINT(AST_SimpleStatement, function(self, output) {
self.body.print(output);
DEFPRINT(AST_SimpleStatement, function(output) {
this.body.print(output);
output.semicolon();
});
function print_braced_empty(self, output) {
@@ -894,13 +866,14 @@ function OutputStream(options) {
});
} else print_braced_empty(self, output);
}
DEFPRINT(AST_BlockStatement, function(self, output) {
print_braced(self, output);
DEFPRINT(AST_BlockStatement, function(output) {
print_braced(this, output);
});
DEFPRINT(AST_EmptyStatement, function(self, output) {
DEFPRINT(AST_EmptyStatement, function(output) {
output.semicolon();
});
DEFPRINT(AST_Do, function(self, output) {
DEFPRINT(AST_Do, function(output) {
var self = this;
output.print("do");
output.space();
make_block(self.body, output);
@@ -912,16 +885,18 @@ function OutputStream(options) {
});
output.semicolon();
});
DEFPRINT(AST_While, function(self, output) {
DEFPRINT(AST_While, function(output) {
var self = this;
output.print("while");
output.space();
output.with_parens(function() {
self.condition.print(output);
});
output.space();
self._do_print_body(output);
force_statement(self.body, output);
});
DEFPRINT(AST_For, function(self, output) {
DEFPRINT(AST_For, function(output) {
var self = this;
output.print("for");
output.space();
output.with_parens(function() {
@@ -948,9 +923,10 @@ function OutputStream(options) {
}
});
output.space();
self._do_print_body(output);
force_statement(self.body, output);
});
DEFPRINT(AST_ForIn, function(self, output) {
DEFPRINT(AST_ForIn, function(output) {
var self = this;
output.print("for");
output.space();
output.with_parens(function() {
@@ -961,20 +937,21 @@ function OutputStream(options) {
self.object.print(output);
});
output.space();
self._do_print_body(output);
force_statement(self.body, output);
});
DEFPRINT(AST_With, function(self, output) {
DEFPRINT(AST_With, function(output) {
var self = this;
output.print("with");
output.space();
output.with_parens(function() {
self.expression.print(output);
});
output.space();
self._do_print_body(output);
force_statement(self.body, output);
});
/* -----[ functions ]----- */
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) {
DEFPRINT(AST_Lambda, function(output, nokeyword) {
var self = this;
if (!nokeyword) {
output.print("function");
@@ -992,37 +969,28 @@ function OutputStream(options) {
output.space();
print_braced(self, output, true);
});
DEFPRINT(AST_Lambda, function(self, output) {
self._do_print(output);
});
/* -----[ jumps ]----- */
function print_jump(output, kind, target) {
output.print(kind);
if (target) {
output.space();
target.print(output);
}
output.semicolon();
function print_jump(kind, prop) {
return function(output) {
output.print(kind);
var target = this[prop];
if (target) {
output.space();
target.print(output);
}
output.semicolon();
};
}
DEFPRINT(AST_Return, function(self, output) {
print_jump(output, "return", self.value);
});
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);
});
DEFPRINT(AST_Return, print_jump("return", "value"));
DEFPRINT(AST_Throw, print_jump("throw", "value"));
DEFPRINT(AST_Break, print_jump("break", "label"));
DEFPRINT(AST_Continue, print_jump("continue", "label"));
/* -----[ if ]----- */
function make_then(self, output) {
var b = self.body;
if (output.option("braces")
if (output.option("braces") && !(b instanceof AST_Const)
|| output.option("ie8") && b instanceof AST_Do)
return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single
@@ -1046,7 +1014,8 @@ function OutputStream(options) {
}
force_statement(self.body, output);
}
DEFPRINT(AST_If, function(self, output) {
DEFPRINT(AST_If, function(output) {
var self = this;
output.print("if");
output.space();
output.with_parens(function() {
@@ -1063,12 +1032,13 @@ function OutputStream(options) {
else
force_statement(self.alternative, output);
} else {
self._do_print_body(output);
force_statement(self.body, output);
}
});
/* -----[ switch ]----- */
DEFPRINT(AST_Switch, function(self, output) {
DEFPRINT(AST_Switch, function(output) {
var self = this;
output.print("switch");
output.space();
output.with_parens(function() {
@@ -1086,28 +1056,30 @@ function OutputStream(options) {
});
});
});
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) {
function print_branch_body(self, output) {
output.newline();
this.body.forEach(function(stmt) {
self.body.forEach(function(stmt) {
output.indent();
stmt.print(output);
output.newline();
});
});
DEFPRINT(AST_Default, function(self, output) {
}
DEFPRINT(AST_Default, function(output) {
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.space();
self.expression.print(output);
output.print(":");
self._do_print_body(output);
print_branch_body(self, output);
});
/* -----[ exceptions ]----- */
DEFPRINT(AST_Try, function(self, output) {
DEFPRINT(AST_Try, function(output) {
var self = this;
output.print("try");
output.space();
print_braced(self, output);
@@ -1120,31 +1092,39 @@ function OutputStream(options) {
self.bfinally.print(output);
}
});
DEFPRINT(AST_Catch, function(self, output) {
DEFPRINT(AST_Catch, function(output) {
var self = this;
output.print("catch");
output.space();
output.with_parens(function() {
self.argname.print(output);
});
if (self.argname) {
output.space();
output.with_parens(function() {
self.argname.print(output);
});
}
output.space();
print_braced(self, output);
});
DEFPRINT(AST_Finally, function(self, output) {
DEFPRINT(AST_Finally, function(output) {
output.print("finally");
output.space();
print_braced(self, output);
print_braced(this, output);
});
DEFPRINT(AST_Var, function(self, output) {
output.print("var");
output.space();
self.definitions.forEach(function(def, i) {
if (i) output.comma();
def.print(output);
});
var p = output.parent();
if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
});
function print_definitinos(type) {
return function(output) {
var self = this;
output.print(type);
output.space();
self.definitions.forEach(function(def, i) {
if (i) output.comma();
def.print(output);
});
var p = output.parent();
if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
};
}
DEFPRINT(AST_Const, print_definitinos("const"));
DEFPRINT(AST_Var, print_definitinos("var"));
function parenthesize_for_noin(node, output, noin) {
var parens = false;
@@ -1160,7 +1140,8 @@ function OutputStream(options) {
node.print(output, parens);
}
DEFPRINT(AST_VarDef, function(self, output) {
DEFPRINT(AST_VarDef, function(output) {
var self = this;
self.name.print(output);
if (self.value) {
output.space();
@@ -1173,10 +1154,7 @@ function OutputStream(options) {
});
/* -----[ other expressions ]----- */
DEFPRINT(AST_Call, function(self, output) {
self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
function print_call_args(self, output) {
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
}
@@ -1186,14 +1164,20 @@ function OutputStream(options) {
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.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) {
self.expressions.forEach(function(node, index) {
DEFPRINT(AST_Sequence, function(output) {
this.expressions.forEach(function(node, index) {
if (index > 0) {
output.comma();
if (output.should_break()) {
@@ -1204,7 +1188,8 @@ function OutputStream(options) {
node.print(output);
});
});
DEFPRINT(AST_Dot, function(self, output) {
DEFPRINT(AST_Dot, function(output) {
var self = this;
var expr = self.expression;
expr.print(output);
var prop = self.property;
@@ -1225,35 +1210,38 @@ function OutputStream(options) {
output.print_name(prop);
}
});
DEFPRINT(AST_Sub, function(self, output) {
self.expression.print(output);
DEFPRINT(AST_Sub, function(output) {
this.expression.print(output);
output.print("[");
self.property.print(output);
this.property.print(output);
output.print("]");
});
DEFPRINT(AST_UnaryPrefix, function(self, output) {
var op = self.operator;
DEFPRINT(AST_UnaryPrefix, function(output) {
var op = this.operator;
var exp = this.expression;
output.print(op);
if (/^[a-z]/i.test(op)
|| (/[+-]$/.test(op)
&& self.expression instanceof AST_UnaryPrefix
&& /^[+-]/.test(self.expression.operator))) {
&& exp instanceof AST_UnaryPrefix
&& /^[+-]/.test(exp.operator))) {
output.space();
}
self.expression.print(output);
exp.print(output);
});
DEFPRINT(AST_UnaryPostfix, function(self, output) {
self.expression.print(output);
output.print(self.operator);
DEFPRINT(AST_UnaryPostfix, function(output) {
this.expression.print(output);
output.print(this.operator);
});
DEFPRINT(AST_Binary, function(self, output) {
DEFPRINT(AST_Binary, function(output) {
var self = this;
self.left.print(output);
output.space();
output.print(self.operator);
output.space();
self.right.print(output);
});
DEFPRINT(AST_Conditional, function(self, output) {
DEFPRINT(AST_Conditional, function(output) {
var self = this;
self.condition.print(output);
output.space();
output.print("?");
@@ -1265,10 +1253,10 @@ function OutputStream(options) {
});
/* -----[ literals ]----- */
DEFPRINT(AST_Array, function(self, output) {
output.with_square(function() {
var a = self.elements, len = a.length;
if (len > 0) output.space();
DEFPRINT(AST_Array, function(output) {
var a = this.elements, len = a.length;
output.with_square(len > 0 ? function() {
output.space();
a.forEach(function(exp, i) {
if (i) output.comma();
exp.print(output);
@@ -1278,12 +1266,13 @@ function OutputStream(options) {
if (i === len - 1 && exp instanceof AST_Hole)
output.comma();
});
if (len > 0) output.space();
});
output.space();
} : noop);
});
DEFPRINT(AST_Object, function(self, output) {
if (self.properties.length > 0) output.with_block(function() {
self.properties.forEach(function(prop, i) {
DEFPRINT(AST_Object, function(output) {
var props = this.properties;
if (props.length > 0) output.with_block(function() {
props.forEach(function(prop, i) {
if (i) {
output.print(",");
output.newline();
@@ -1293,7 +1282,7 @@ function OutputStream(options) {
});
output.newline();
});
else print_braced_empty(self, output);
else print_braced_empty(this, 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);
output.colon();
self.value.print(output);
});
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
output.print(type);
output.space();
print_property_name(this.key.name, this.quote, output);
this.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output) {
self._print_getter_setter("set", output);
});
DEFPRINT(AST_ObjectGetter, function(self, output) {
self._print_getter_setter("get", output);
});
DEFPRINT(AST_Symbol, function(self, output) {
var def = self.definition();
output.print_name(def && def.mangled_name || self.name);
function print_accessor(type) {
return function(output) {
var self = this;
output.print(type);
output.space();
print_property_name(self.key.name, self.quote, output);
self.value._codegen(output, true);
};
}
DEFPRINT(AST_ObjectGetter, print_accessor("get"));
DEFPRINT(AST_ObjectSetter, print_accessor("set"));
DEFPRINT(AST_Symbol, function(output) {
var def = this.definition();
output.print_name(def && def.mangled_name || this.name);
});
DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output) {
DEFPRINT(AST_This, function(output) {
output.print("this");
});
DEFPRINT(AST_Constant, function(self, output) {
output.print(self.value);
DEFPRINT(AST_Constant, function(output) {
output.print(this.value);
});
DEFPRINT(AST_String, function(self, output) {
output.print_string(self.value, self.quote);
DEFPRINT(AST_String, function(output) {
output.print_string(this.value, this.quote);
});
DEFPRINT(AST_Number, function(self, output) {
if (use_asm && self.start && self.start.raw != null) {
output.print(self.start.raw);
DEFPRINT(AST_Number, function(output) {
var start = this.start;
if (use_asm && start && start.raw != null) {
output.print(start.raw);
} else {
output.print(make_num(self.value));
output.print(make_num(this.value));
}
});
DEFPRINT(AST_RegExp, function(self, output) {
var regexp = self.value;
DEFPRINT(AST_RegExp, function(output) {
var regexp = this.value;
var str = regexp.toString();
var end = str.lastIndexOf("/");
if (regexp.raw_source) {
@@ -1386,18 +1376,17 @@ function OutputStream(options) {
}
}));
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(" ");
});
function force_statement(stat, output) {
if (output.option("braces")) {
if (output.option("braces") && !(stat instanceof AST_Const)) {
make_block(stat, output);
} else if (!stat || stat instanceof AST_EmptyStatement) {
output.force_semicolon();
} else {
if (!stat || stat instanceof AST_EmptyStatement)
output.force_semicolon();
else
stat.print(output);
stat.print(output);
}
}

View File

@@ -44,11 +44,14 @@
"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_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'
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
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 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",
KEYWORDS_ATOM,
KEYWORDS,
].join(" ");
var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case";
KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -57,8 +60,9 @@ KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
var RE_OCT_NUMBER = /^0[0-7]+$/;
var RE_BIN_NUMBER = /^0b([01]+)$/i;
var RE_HEX_NUMBER = /^0x([0-9a-f]+)$/i;
var RE_OCT_NUMBER = /^0o?([0-7]+)$/i;
var OPERATORS = makePredicate([
"in",
@@ -144,10 +148,6 @@ function is_digit(code) {
return code >= 48 && code <= 57;
}
function is_alphanumeric_char(code) {
return is_digit(code) || is_letter(code);
}
function is_unicode_digit(code) {
return UNICODE.digit.test(String.fromCharCode(code));
}
@@ -181,14 +181,12 @@ function is_identifier_string(str) {
}
function parse_js_number(num) {
if (RE_HEX_NUMBER.test(num)) {
return parseInt(num.substr(2), 16);
} else if (RE_OCT_NUMBER.test(num)) {
return parseInt(num.substr(1), 8);
} else {
var val = parseFloat(num);
if (val == num) return val;
}
var match;
if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
if (match = RE_HEX_NUMBER.exec(num)) return parseInt(match[1], 16);
if (match = RE_OCT_NUMBER.exec(num)) return parseInt(match[1], 8);
var val = parseFloat(num);
if (val == num) return val;
}
function JS_Parse_Error(message, filename, line, col, pos) {
@@ -344,11 +342,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
case (after_e = false, 46): // .
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 (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
parse_error("Legacy octal literals are not allowed in strict mode");
if (/^0[0-7_]+$/.test(num)) {
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);
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 regex_allowed = S.regex_allowed;
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
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));
@@ -832,6 +832,12 @@ function parse($TEXT, options) {
next();
return break_cont(AST_Break);
case "const":
next();
var node = const_();
semicolon();
return node;
case "continue":
next();
return break_cont(AST_Continue);
@@ -988,7 +994,9 @@ function parse($TEXT, options) {
expect("(");
var init = null;
if (!is("punc", ";")) {
init = is("keyword", "var")
init = is("keyword", "const")
? (next(), const_(true))
: is("keyword", "var")
? (next(), var_(true))
: expression(true, true);
if (is("operator", "in")) {
@@ -1092,7 +1100,7 @@ function parse($TEXT, options) {
function switch_body_() {
expect("{");
var a = [], cur = null, branch = null, tmp;
var a = [], branch, cur, default_branch, tmp;
while (!is("punc", "}")) {
if (is("eof")) expect_token("punc", "}");
if (is("keyword", "case")) {
@@ -1107,12 +1115,14 @@ function parse($TEXT, options) {
expect(":");
} else if (is("keyword", "default")) {
if (branch) branch.end = prev();
if (default_branch) croak("More than one default clause in switch statement");
cur = [];
branch = new AST_Default({
start : (tmp = S.token, next(), expect(":"), tmp),
body : cur
});
a.push(branch);
default_branch = branch;
} else {
if (!cur) unexpected();
cur.push(statement());
@@ -1128,9 +1138,12 @@ function parse($TEXT, options) {
if (is("keyword", "catch")) {
var start = S.token;
next();
expect("(");
var name = as_symbol(AST_SymbolCatch);
expect(")");
var name = null;
if (is("punc", "(")) {
next();
name = as_symbol(AST_SymbolCatch);
expect(")");
}
bcatch = new AST_Catch({
start : start,
argname : name,
@@ -1156,13 +1169,22 @@ function parse($TEXT, options) {
});
}
function vardefs(no_in) {
function vardefs(type, no_in, must_init) {
var a = [];
for (;;) {
var start = S.token;
var name = as_symbol(type);
var value = null;
if (is("operator", "=")) {
next();
value = expression(false, no_in);
} else if (must_init) {
croak("Missing initializer in declaration");
}
a.push(new AST_VarDef({
start : S.token,
name : as_symbol(AST_SymbolVar),
value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
start : start,
name : name,
value : value,
end : prev()
}));
if (!is("punc", ","))
@@ -1172,10 +1194,18 @@ function parse($TEXT, options) {
return a;
}
var const_ = function(no_in) {
return new AST_Const({
start : prev(),
definitions : vardefs(AST_SymbolConst, no_in, true),
end : prev()
});
};
var var_ = function(no_in) {
return new AST_Var({
start : prev(),
definitions : vardefs(no_in),
definitions : vardefs(AST_SymbolVar, no_in),
end : prev()
});
};

View File

@@ -43,7 +43,8 @@
"use strict";
function find_builtins(reserved) {
var builtins = function() {
var names = [];
// NaN will be included due to Number.NaN
[
"null",
@@ -67,19 +68,21 @@ function find_builtins(reserved) {
].forEach(function(ctor) {
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
Object.getOwnPropertyNames(new ctor()).map(add);
Object.getOwnPropertyNames(ctor.prototype).map(add);
}
});
return makePredicate(names);
function add(name) {
push_uniq(reserved, name);
names.push(name);
}
}
}();
function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
if (node instanceof AST_ObjectKeyVal) {
if (node.quote) add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
@@ -91,17 +94,14 @@ function reserve_quoted_keys(ast, reserved) {
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.tail_node(), add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
} else if (node instanceof AST_Sequence) {
addStrings(node.tail_node(), add);
} else if (node instanceof AST_String) {
add(node.value);
}
}
function mangle_properties(ast, options) {
@@ -110,21 +110,21 @@ function mangle_properties(ast, options) {
cache: null,
debug: false,
keep_quoted: false,
only_cache: false,
regex: null,
reserved: null,
}, true);
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var reserved = Object.create(options.builtins ? null : builtins);
if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
reserved[name] = true;
});
var cname = -1;
var cache;
if (options.cache) {
cache = options.cache.props;
cache.each(function(mangled_name) {
push_uniq(reserved, mangled_name);
cache.each(function(name) {
reserved[name] = true;
});
} else {
cache = new Dictionary();
@@ -139,62 +139,92 @@ function mangle_properties(ast, options) {
var debug_suffix;
if (debug) debug_suffix = options.debug === true ? "" : options.debug;
var names_to_mangle = [];
var unmangleable = [];
var names_to_mangle = Object.create(null);
var unmangleable = Object.create(reserved);
// step 1: find candidates to mangle
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);
} else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
} else if (node instanceof AST_Dot) {
add(node.property);
} else if (node instanceof AST_Sub) {
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
return ast.transform(new TreeTransformer(function(node) {
if (node instanceof AST_ObjectKeyVal) {
// step 2: renaming properties
ast.walk(new TreeWalker(function(node) {
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);
} else if (node instanceof AST_ObjectProperty) {
// setter or getter
node.key.name = mangle(node.key.name);
} else if (node instanceof AST_Dot) {
node.property = mangle(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]);
} else if (node instanceof AST_Sub) {
if (!options.keep_quoted) mangleStrings(node.property);
}
}));
// only function declarations after this line
function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) return cache.has(name);
if (unmangleable[name]) return false;
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true;
}
function should_mangle(name) {
if (reserved[name]) return false;
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.has(name) || names_to_mangle.indexOf(name) >= 0;
return cache.has(name) || names_to_mangle[name];
}
function add(name) {
if (can_mangle(name)) push_uniq(names_to_mangle, name);
if (!should_mangle(name)) push_uniq(unmangleable, name);
if (can_mangle(name)) names_to_mangle[name] = true;
if (!should_mangle(name)) unmangleable[name] = true;
}
function mangle(name) {
@@ -218,17 +248,13 @@ function mangle_properties(ast, options) {
}
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node) {
if (node instanceof AST_Sequence) {
var last = node.expressions.length - 1;
node.expressions[last] = mangleStrings(node.expressions[last]);
} else if (node instanceof AST_String) {
node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
node.alternative = mangleStrings(node.alternative);
}
return node;
}));
if (node instanceof AST_Sequence) {
mangleStrings(node.expressions.tail_node());
} else if (node instanceof AST_String) {
node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) {
mangleStrings(node.consequent);
mangleStrings(node.alternative);
}
}
}

View File

@@ -59,21 +59,17 @@ function SymbolDef(id, scope, orig, init) {
}
SymbolDef.prototype = {
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);
forEach: function(fn) {
this.orig.forEach(fn);
this.references.forEach(fn);
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
} else if (!this.mangled_name && !this.unmangleable(options)) {
var def;
if (def = this.redefined()) {
var def = this.redefined();
if (def) {
this.mangled_name = def.mangled_name || def.name;
} else {
this.mangled_name = next_mangled_name(this.scope, options, this);
@@ -84,8 +80,24 @@ SymbolDef.prototype = {
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
var scope = this.defun;
if (!scope) return;
var name = this.name;
var def = scope.variables.get(name)
|| scope instanceof AST_Toplevel && scope.globals.get(name)
|| this.orig[0] instanceof AST_SymbolConst && find_if(function(def) {
return def.name == name;
}, scope.enclosed);
if (def && def !== this) return def.redefined() || def;
},
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) {
@@ -96,30 +108,46 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var defun = null;
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
if (node instanceof AST_Defun) {
node.name.walk(tw);
walk_scope(function() {
node.argnames.forEach(function(argname) {
argname.walk(tw);
});
walk_body(node, tw);
});
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
defun = scope = node;
if (node instanceof AST_SwitchBranch) {
node.init_vars(scope);
descend();
scope = save_scope;
defun = save_defun;
return true;
}
if (node instanceof AST_Try) {
walk_scope(function() {
walk_body(node, tw);
});
if (node.bcatch) node.bcatch.walk(tw);
if (node.bfinally) node.bfinally.walk(tw);
return true;
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) s.uses_with = true;
return;
var s = scope;
do {
s = s.resolve();
if (s.uses_with) break;
s.uses_with = true;
} while (s = s.parent_scope);
walk_scope(descend);
return true;
}
if (node instanceof AST_BlockScope) {
walk_scope(descend);
return true;
}
if (node instanceof AST_Symbol) {
node.scope = scope;
@@ -128,28 +156,48 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolDefun) {
// This should be defined in the parent scope, as we encounter the
// AST_Defun node before getting to its AST_Symbol.
(node.scope = defun.parent_scope.resolve()).def_function(node, defun);
if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
} else if (node instanceof AST_SymbolConst) {
scope.def_variable(node).defun = defun;
} else if (node instanceof AST_SymbolDefun) {
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) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie8) def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolVar) {
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
}
node.reference(options);
}
} else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
defun.def_variable(node, null);
entangle(defun, scope);
}
function walk_scope(descend) {
node.init_vars(scope);
var save_defun = defun;
var save_scope = scope;
if (node instanceof AST_Scope) defun = node;
scope = node;
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.next_def_id = 0;
self.make_def = function(orig, init) {
return new SymbolDef(++next_def_id, this, orig, init);
};
self.walk(tw);
// pass 2: find back references and eval
@@ -164,15 +212,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
var sym = node.scope.find_variable(name);
if (!sym) {
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;
}
if (name == "eval") {
var parent = tw.parent();
if (parent.TYPE == "Call" && parent.expression === node) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
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;
}
@@ -181,7 +232,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.reference(options);
return true;
}
// ensure mangling works if catch reuses a scope variable
// ensure mangling works if `catch` reuses a scope variable
if (node instanceof AST_SymbolCatch) {
var def = node.definition().redefined();
if (def) for (var s = node.scope; s; s = s.parent_scope) {
@@ -190,6 +241,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
return true;
}
// ensure compression works if `const` reuses a scope variable
if (node instanceof AST_SymbolConst) {
var redef = node.definition().redefined();
if (redef) redef.const_redefs = true;
return true;
}
});
self.walk(tw);
@@ -220,8 +277,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
var old_def = node.thedef;
var new_def = scope.find_variable(name);
if (new_def) {
var redef;
while (redef = new_def.redefined()) new_def = redef;
var redef = new_def.redefined();
if (redef) new_def = redef;
} else {
new_def = self.globals.get(name);
}
@@ -230,7 +287,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
} else {
new_def = scope.def_variable(node);
}
old_def.orig.concat(old_def.references).forEach(function(node) {
old_def.defun = new_def.scope;
old_def.forEach(function(node) {
node.redef = true;
node.thedef = new_def;
node.reference(options);
});
@@ -239,12 +298,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
});
AST_Scope.DEFMETHOD("make_def", function(orig, init) {
var top = this;
while (top.parent_scope) top = top.parent_scope;
return new SymbolDef(++top.next_def_id, this, orig, init);
});
AST_Toplevel.DEFMETHOD("def_global", function(node) {
var globals = this.globals, name = node.name;
if (globals.has(name)) {
@@ -258,24 +311,35 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = parent_scope; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
});
function init_block_vars(scope, parent) {
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
scope.parent_scope = parent; // the parent scope (null if this is the top level)
scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
}
AST_Lambda.DEFMETHOD("init_scope_vars", function() {
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
function init_scope_vars(scope, parent) {
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.def_variable(new AST_SymbolFunarg({
name: "arguments",
start: this.start,
end: this.end
end: this.end,
}));
return this;
});
AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
@@ -296,20 +360,20 @@ AST_Symbol.DEFMETHOD("reference", function(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;
return this.variables.get(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);
if (!def.init || def.init instanceof AST_Defun) def.init = init;
this.functions.set(symbol.name, 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);
if (def) {
def.orig.push(symbol);
@@ -322,17 +386,12 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
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) {
var names = scope.names_in_use;
if (!names) {
scope.names_in_use = names = Object.create(scope.mangled_names || null);
scope.cname = -1;
scope.cname_holes = [];
scope.names_in_use = names = Object.create(null);
var cache = options.cache && options.cache.props;
scope.enclosed.forEach(function(def) {
if (def.unmangleable(options)) names[def.name] = true;
@@ -349,7 +408,7 @@ function next_mangled_name(scope, options, def) {
var holes = scope.cname_holes;
var names = Object.create(null);
var scopes = [ scope ];
def.references.forEach(function(sym) {
def.forEach(function(sym) {
var scope = sym.scope;
do {
if (scopes.indexOf(scope) < 0) {
@@ -365,7 +424,7 @@ function next_mangled_name(scope, options, def) {
name = base54(holes[i]);
if (names[name]) continue;
holes.splice(i, 1);
scope.names_in_use[name] = true;
in_use[name] = true;
return name;
}
while (true) {
@@ -374,7 +433,7 @@ function next_mangled_name(scope, options, def) {
if (!names[name]) break;
holes.push(scope.cname);
}
scope.names_in_use[name] = true;
in_use[name] = true;
return name;
}
@@ -386,18 +445,10 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options) {
// labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", return_false);
AST_Symbol.DEFMETHOD("unreferenced", function() {
return !this.definition().references.length && !this.scope.pinned();
});
AST_Symbol.DEFMETHOD("definition", function() {
return this.thedef;
});
AST_Symbol.DEFMETHOD("global", function() {
return this.definition().global;
});
function _default_mangler_options(options) {
options = defaults(options, {
eval : false,
@@ -423,7 +474,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var lname = -1;
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) {
mangled_names[mangled_name] = true;
});
@@ -438,7 +489,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
lname = save_nesting;
return true;
}
if (node instanceof AST_Scope) {
if (node instanceof AST_BlockScope) {
var to_mangle = [];
node.variables.each(function(def) {
if (!defer_redef(def)) to_mangle.push(def);
});
descend();
if (options.cache && node instanceof AST_Toplevel) {
node.globals.each(mangle);
@@ -448,9 +503,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
sym.scope = node;
sym.reference(options);
}
node.variables.each(function(def) {
if (!defer_redef(def)) mangle(def);
});
to_mangle.forEach(mangle);
return true;
}
if (node instanceof AST_Label) {
@@ -461,13 +514,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
node.mangled_name = name;
return true;
}
if (!options.ie8 && node instanceof AST_Catch) {
var def = node.argname.definition();
var redef = defer_redef(def, node.argname);
descend();
if (!redef) mangle(def);
return true;
}
});
this.walk(tw);
redefined.forEach(mangle);
@@ -482,7 +528,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
if (!redef) return false;
redefined.push(def);
def.references.forEach(reference);
if (node) reference(node);
var node = def.orig[0];
if (node instanceof AST_SymbolCatch || node instanceof AST_SymbolConst) reference(node);
return true;
function reference(sym) {
@@ -495,12 +542,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props;
var avoid = Object.create(null);
var avoid = Object.create(RESERVED_WORDS);
options.reserved.forEach(to_avoid);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
if (node instanceof AST_BlockScope) node.variables.each(add_def);
}));
return avoid;
@@ -524,15 +570,14 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var cname = 0;
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
if (node instanceof AST_BlockScope) node.variables.each(rename);
}));
function next_name() {
var name;
do {
name = base54(cname++);
} while (avoid[name] || RESERVED_WORDS[name]);
} while (avoid[name]);
return name;
}
@@ -543,11 +588,8 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var redef = def.redefined();
var name = redef ? redef.rename || redef.name : next_name();
def.rename = name;
def.orig.forEach(function(sym) {
sym.name = name;
});
def.references.forEach(function(sym) {
sym.name = name;
def.forEach(function(sym) {
if (sym.definition() === def) sym.name = name;
});
}
});
@@ -560,22 +602,24 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
options = _default_mangler_options(options);
base54.reset();
var fn = AST_Symbol.prototype.add_source_map;
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
AST_Symbol.prototype.add_source_map = function() {
if (!this.unmangleable(options)) base54.consider(this.name, -1);
};
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);
} 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();

View File

@@ -116,7 +116,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.bfinally) self.bfinally = self.bfinally.transform(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);
});
DEF(AST_Definitions, function(self, tw) {

View File

@@ -112,51 +112,29 @@ function return_this() { return this; }
function return_null() { return null; }
var List = (function() {
function List(a, f, backwards) {
var ret = [], top = [], i;
function doit() {
function List(a, f) {
var ret = [];
for (var i = 0; i < a.length; i++) {
var val = f(a[i], i);
var is_last = val instanceof Last;
if (is_last) val = val.v;
if (val instanceof AtTop) {
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();
if (val === skip) continue;
if (val instanceof Splice) {
ret.push.apply(ret, val.v);
} 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.is_op = function(val) {
return val === skip || val instanceof AtTop || val instanceof Last || val instanceof Splice;
return val === skip || val instanceof Splice;
};
List.splice = function(val) {
return new Splice(val);
};
List.at_top = function(val) { return new AtTop(val); };
List.splice = function(val) { return new Splice(val); };
List.last = function(val) { return new Last(val); };
var skip = List.skip = {};
function AtTop(val) { this.v = val; }
function Splice(val) { this.v = val; }
function Last(val) { this.v = val; }
function Splice(val) {
this.v = val;
}
return List;
})();

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.9.4",
"version": "3.11.3",
"engines": {
"node": ">=0.8.0"
},
@@ -22,9 +22,6 @@
"tools",
"LICENSE"
],
"dependencies": {
"commander": "~2.20.3"
},
"devDependencies": {
"acorn": "~7.1.0",
"semver": "~6.3.0"

View File

@@ -269,6 +269,7 @@ function test_case(test) {
quote_style: 3,
});
try {
input.validate_ast();
U.parse(input_code);
} catch (ex) {
log([
@@ -311,12 +312,10 @@ function test_case(test) {
if (test.mangle) {
output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle);
if (test.mangle.properties) {
output = U.mangle_properties(output, test.mangle.properties);
}
if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties);
}
output = make_code(output, output_options);
if (expect != output) {
var output_code = make_code(output, output_options);
if (expect != output_code) {
log([
"!!! failed",
"---INPUT---",
@@ -329,14 +328,15 @@ function test_case(test) {
"",
].join("\n"), {
input: input_formatted,
output: output,
output: output_code,
expected: expect
});
return false;
}
// expect == output
try {
U.parse(output);
output.validate_ast();
U.parse(output_code);
} catch (ex) {
log([
"!!! Test matched expected result but cannot parse output",
@@ -350,7 +350,7 @@ function test_case(test) {
"",
].join("\n"), {
input: input_formatted,
output: output,
output: output_code,
error: ex,
});
return false;
@@ -409,7 +409,7 @@ function test_case(test) {
});
return false;
}
actual = run_code(output, toplevel);
actual = run_code(output_code, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! failed",

View File

@@ -243,20 +243,18 @@ issue_3273: {
arguments: true,
}
input: {
function f(a) {
(function(a) {
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect: {
function f(a) {
(function(a) {
console.log(a, a);
a++;
console.log(a, a);
}
f(0);
})(0);
}
expect_stdout: [
"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: {
options = {
arguments: true,
reduce_vars: true,
}
input: {
function f(a) {
(function(a) {
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect: {
function f(a) {
(function(a) {
console.log(a, a);
a++;
console.log(a, a);
}
f(0);
})(0);
}
expect_stdout: [
"0 0",
@@ -296,22 +311,20 @@ issue_3273_local_strict: {
arguments: true,
}
input: {
function f(a) {
(function(a) {
"use strict";
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect: {
function f(a) {
(function(a) {
"use strict";
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect_stdout: [
"0 0",
@@ -325,22 +338,20 @@ issue_3273_local_strict_reduce_vars: {
reduce_vars: true,
}
input: {
function f(a) {
(function(a) {
"use strict";
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect: {
function f(a) {
(function(a) {
"use strict";
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect_stdout: [
"0 0",
@@ -354,21 +365,19 @@ issue_3273_global_strict: {
}
input: {
"use strict";
function f(a) {
(function(a) {
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect: {
"use strict";
function f(a) {
(function(a) {
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect_stdout: [
"0 0",
@@ -383,21 +392,19 @@ issue_3273_global_strict_reduce_vars: {
}
input: {
"use strict";
function f(a) {
(function(a) {
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect: {
"use strict";
function f(a) {
(function(a) {
console.log(arguments[0], a);
arguments[0]++;
console.log(arguments[0], a);
}
f(0);
})(0);
}
expect_stdout: [
"0 0",
@@ -776,3 +783,27 @@ issue_3420_7: {
}
expect_stdout: "true"
}
issue_4200: {
options = {
arguments: true,
keep_fargs: false,
}
input: {
var o = {
get p() {
return arguments[0];
},
};
console.log(o.p);
}
expect: {
var o = {
get p() {
return arguments[0];
},
};
console.log(o.p);
}
expect_stdout: "undefined"
}

View File

@@ -76,9 +76,8 @@ asm_mixed: {
start = start | 0;
end = end | 0;
var sum = 0.0, p = 0, q = 0;
for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0) {
for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0)
sum = sum + +log(values[p >> 3]);
}
return +sum;
}
function geometricMean(start, end) {
@@ -91,11 +90,12 @@ asm_mixed: {
function no_asm_GeometricMean(stdlib, foreign, buffer) {
function logSum(start, end) {
start |= 0, end |= 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 (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]);
return +sum;
}
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);
return { geometricMean: geometricMean };

View File

@@ -407,3 +407,57 @@ issue_3429_2: {
}
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"
}

View File

@@ -62,25 +62,26 @@ collapse_vars_side_effects_1: {
expect: {
function f1() {
var s = "abcdef", i = 2;
console.log.bind(console)(s.charAt(i++), s.charAt(i++), s.charAt(i++), 7);
console.log.bind(console)(s.charAt(i++), s.charAt(+i), s.charAt(4), 7);
}
function f2() {
var s = "abcdef", i = 2;
console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(i++), 7);
console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(+i), 7);
}
function f3() {
var s = "abcdef",
i = 2,
log = console.log.bind(console),
x = s.charAt(i++),
y = s.charAt(i++);
log(x, s.charAt(i++), y, 7);
y = s.charAt(+i);
log(x, s.charAt(4), y, 7);
}
function f4() {
var i = 10,
x = i += 2,
y = i += 3;
console.log.bind(console)(x, i += 4, y, 19);
var i = 10;
i += 2,
i += 3,
i += 4;
console.log.bind(console)(12, 19, 15, 19);
}
f1(), f2(), f3(), f4();
}
@@ -345,9 +346,8 @@ collapse_vars_if: {
return "x" != "Bar" + x / 4 ? g9 : g5;
}
function f3(x) {
if (x) {
if (x)
return 1;
}
return 2;
}
}
@@ -916,7 +916,7 @@ collapse_vars_lvalues_drop_assign: {
}
}
collapse_vars_misc1: {
collapse_vars_misc: {
options = {
booleans: true,
collapse_vars: true,
@@ -970,8 +970,8 @@ collapse_vars_misc1: {
function f7() { var b = window.a * window.z; return b + b }
function f8() { var b = window.a * window.z; return b + (5 + b) }
function f9() { var b = window.a * window.z; return bar() || b }
function f10(x) { var a = 5; return a += 3; }
function f11(x) { var a = 5; return a += 2; }
function f10(x) { return 8; }
function f11(x) { return 7; }
}
}
@@ -1598,7 +1598,7 @@ collapse_vars_constants: {
}
}
collapse_vars_arguments: {
collapse_vars_arguments_1: {
options = {
booleans: true,
collapse_vars: true,
@@ -1635,6 +1635,78 @@ collapse_vars_arguments: {
expect_stdout: true
}
collapse_vars_arguments_2: {
options = {
collapse_vars: true,
}
input: {
function log(a, b) {
console.log(b);
}
function f(c) {
var d = arguments[0];
c = "FAIL";
log(c, d);
}
f();
f("PASS");
}
expect: {
function log(a, b) {
console.log(b);
}
function f(c) {
var d = arguments[0];
log(c = "FAIL", d);
}
f();
f("PASS");
}
expect_stdout: [
"undefined",
"PASS",
]
}
collapse_vars_arguments_3: {
options = {
collapse_vars: true,
}
input: {
function log(a, b) {
console.log(b);
}
function f(c) {
var args = arguments;
console.log(c);
var d = args[0];
c = "FAIL";
log(c, d);
}
f();
f("PASS");
}
expect: {
function log(a, b) {
console.log(b);
}
function f(c) {
var args = arguments;
console.log(c);
var d = args[0];
log(c = "FAIL", d);
}
f();
f("PASS");
}
expect_stdout: [
"undefined",
"undefined",
"PASS",
"PASS",
]
}
collapse_vars_short_circuit: {
options = {
booleans: true,
@@ -3000,8 +3072,6 @@ issue_2298: {
expect: {
!function() {
(function() {
var a = undefined;
var undefined = a++;
try {
!function(b) {
(void 0)[1] = "foo";
@@ -3835,20 +3905,19 @@ issue_2436_3: {
}(o));
}
expect: {
var o = {
a: 1,
b: 2,
};
console.log(function(c) {
o = {
({
a: 3,
b: 4,
};
});
return {
x: c.a,
y: c.b,
};
}(o));
}({
a: 1,
b: 2,
}));
}
expect_stdout: true
}
@@ -4070,16 +4139,15 @@ issue_2436_10: {
}(o).join(" "));
}
expect: {
var o = {
a: 1,
b: 2,
};
function f(n) {
o = { b: 3 };
({ b: 3 });
return n;
}
console.log([
(c = o).a,
(c = {
a: 1,
b: 2,
}).a,
f(c.b),
c.b,
].join(" "));
@@ -4123,9 +4191,8 @@ issue_2436_11: {
if (isCollection(arg1)) {
var size = arg1, max = arg2, min = 0, res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt);
return size && true === size.isMatrix ? matrix(res) : res;
} else {
} else
return _randomInt(min = arg1, max = arg2);
}
}
}
}
@@ -4241,9 +4308,8 @@ issue_2497: {
function sample() {
if (true)
for (var i = 0; i < 1; ++i)
for (var k = 0; k < 1; ++k) {
for (var k = 0; k < 1; ++k)
value = (value = 1) ? value + 1 : 0;
}
else
for (i = 0; i < 1; ++i)
for (k = 0; k < 1; ++k)
@@ -6890,6 +6956,30 @@ sequence_in_iife_2: {
}
expect: {
var a = "foo", b = 42;
b = a;
console.log(a, b);
}
expect_stdout: "foo foo"
}
sequence_in_iife_3: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "foo", b = 42;
(function() {
var c = (b = a, b);
})();
console.log(a, b);
}
expect: {
var a = "foo";
console.log(a, a);
}
expect_stdout: "foo foo"
@@ -7955,7 +8045,7 @@ mangleable_var: {
expect_stdout: "PASS"
}
issue_3884: {
issue_3884_1: {
options = {
collapse_vars: true,
evaluate: true,
@@ -7973,9 +8063,33 @@ issue_3884: {
console.log(a, b);
}
expect: {
var a = 100;
++a;
console.log(a, 32);
var a = 100, b = 1;
b <<= ++a;
console.log(a, b);
}
expect_stdout: "101 32"
}
issue_3884_2: {
options = {
collapse_vars: true,
evaluate: true,
passes: 3,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 100, b = 1;
{
a++ + a || a;
b <<= a;
}
console.log(a, b);
}
expect: {
console.log(101, 32);
}
expect_stdout: "101 32"
}
@@ -8108,3 +8222,337 @@ issue_3908: {
}
expect_stdout: "PASS"
}
issue_3927: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
console.log(function(b) {
try {
try {
if (a + (b = "PASS", true)) return;
b.p;
} finally {
return b;
}
} catch (e) {
}
}());
}
expect: {
var a = 0;
console.log(function(b) {
try {
try {
if (a + (b = "PASS", true)) return;
b.p;
} finally {
return b;
}
} catch (e) {}
}());
}
expect_stdout: "PASS"
}
operator_in: {
options = {
collapse_vars: true,
}
input: {
function log(msg) {
console.log(msg);
}
var a = "FAIL";
try {
a = "PASS";
0 in null;
log("FAIL", a);
} catch (e) {}
log(a);
}
expect: {
function log(msg) {
console.log(msg);
}
var a = "FAIL";
try {
a = "PASS";
0 in null;
log("FAIL", a);
} catch (e) {}
log(a);
}
expect_stdout: "PASS"
}
issue_3971: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 0 == typeof f, b = 0;
{
var a = void (a++ + (b |= a));
}
console.log(b);
}
expect: {
var a = 0 == typeof f, b = 0;
var a = void (b |= ++a);
console.log(b);
}
expect_stdout: "1"
}
issue_3976: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
console.log("FAIL");
}
(function(a) {
function g() {
if ((a = 0) || f(0)) {
f();
} else {
f();
}
if (h(a = 0));
}
function h() {
g();
}
})();
console.log("PASS");
}
expect: {
function f() {
console.log("FAIL");
}
void 0;
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4012: {
options = {
collapse_vars: true,
dead_code: true,
evaluate: true,
}
input: {
(function(a) {
try {
throw 2;
} catch (b) {
a = "PASS";
if (--b)
return;
if (3);
} finally {
console.log(a);
}
})();
}
expect: {
(function(a) {
try {
throw 2;
} catch (b) {
a = "PASS";
if (--b)
return;
if (3);
} finally {
console.log(a);
}
})();
}
expect_stdout: "PASS"
}
global_assign: {
options = {
collapse_vars: true,
}
input: {
this.A = "FAIL";
A = "PASS";
B = "FAIL";
console.log(A);
}
expect: {
this.A = "FAIL";
A = "PASS";
B = "FAIL";
console.log(A);
}
expect_stdout: "PASS"
}
global_read: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
a = this.A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect: {
var a = 0;
a = this.A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4038: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
a = this;
a = a.A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect: {
var a = 0;
a = (a = this).A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4040: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var a = console.log("PASS") && a.p;
delete NaN;
}
expect: {
var a = console.log("PASS") && a.p;
delete NaN;
}
expect_stdout: "PASS"
}
issue_4047_1: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
console.log(+function(a) {
b = a;
(a >>= 0) && console.log("PASS");
}(--b + (0 !== typeof A)));
}
expect: {
var b = 1;
var a;
console.log((a = --b + ((a = 0) !== typeof A), +void ((a >>= 0) && console.log("PASS"))));
}
expect_stdout: [
"PASS",
"NaN",
]
}
issue_4047_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
console.log(+function(a) {
b = a;
(a >>= 0) && console.log("PASS");
}(--b + (0 !== typeof A)));
}
expect: {
var a;
console.log((a = +(0 !== typeof A), +void ((a >>= 0) && console.log("PASS"))));
}
expect_stdout: [
"PASS",
"NaN",
]
}
issue_4051: {
options = {
collapse_vars: true,
}
input: {
try {
var a = (b = b.p, "FAIL"), b = b;
} catch (e) {}
console.log(a);
}
expect: {
try {
var a = (b = b.p, "FAIL"), b = b;
} catch (e) {}
console.log(a);
}
expect_stdout: "undefined"
}
issue_4070: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
console.log(function f() {
function g() {}
g.p++;
return f.p = g.p;
}());
}
expect: {
console.log(function f() {
function g() {}
return f.p = ++g.p;
}());
}
expect_stdout: "NaN"
}

View File

@@ -123,6 +123,29 @@ self_comparison_3: {
]
}
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: {
options = {
comparisons: true,

View File

@@ -238,6 +238,41 @@ concat_8: {
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: {
options = {
strings: true,

View File

@@ -55,14 +55,15 @@ ifs_3_should_warn: {
}
input: {
var x, y;
if (x && !(x + "1") && y) { // 1
// 1
if (x && !(x + "1") && y) {
var qq;
foo();
} else {
bar();
}
if (x || !!(x + "1") || y) { // 2
// 2
if (x || !!(x + "1") || y) {
foo();
} else {
var jj;
@@ -71,9 +72,27 @@ ifs_3_should_warn: {
}
expect: {
var x, y;
var qq; bar(); // 1
var jj; foo(); // 2
// 1
var qq; bar();
// 2
foo(); var jj;
}
expect_warnings: [
"WARN: + in boolean context always true [test/compress/conditionals.js:3,18]",
"WARN: Boolean && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition left of && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition always false [test/compress/conditionals.js:3,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]",
"WARN: Declarations in unreachable code! [test/compress/conditionals.js:4,12]",
"WARN: + in boolean context always true [test/compress/conditionals.js:10,19]",
"WARN: Boolean || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition left of || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition always true [test/compress/conditionals.js:10,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]",
"WARN: Declarations in unreachable code! [test/compress/conditionals.js:13,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]",
]
}
ifs_4: {
@@ -783,6 +802,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: {
options = {
booleans: true,
@@ -1137,7 +1178,7 @@ issue_1645_2: {
expect_stdout: true
}
condition_symbol_matches_consequent: {
condition_matches_consequent: {
options = {
conditionals: true,
}
@@ -1166,6 +1207,35 @@ condition_symbol_matches_consequent: {
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: {
options = {
booleans: true,

1153
test/compress/const.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -59,6 +59,11 @@ dead_code_2_should_warn: {
f();
}
expect_stdout: true
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/dead-code.js:8,12]",
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:10,16]",
"WARN: Dropping unreachable code [test/compress/dead-code.js:10,16]",
]
node_version: "<=4"
}
@@ -89,11 +94,23 @@ dead_code_constant_boolean_should_warn_more: {
function bar() {}
// nothing for the while
// as for the for, it should keep:
var moo;
var x = 10, y;
var moo;
bar();
}
expect_stdout: true
expect_warnings: [
"WARN: + in boolean context always true [test/compress/dead-code.js:1,33]",
"WARN: Boolean || always true [test/compress/dead-code.js:1,16]",
"WARN: Dropping unreachable code [test/compress/dead-code.js:1,45]",
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:3,12]",
"WARN: Boolean expression always true [test/compress/dead-code.js:6,47]",
"WARN: Boolean && always false [test/compress/dead-code.js:6,28]",
"WARN: Dropping unreachable code [test/compress/dead-code.js:6,63]",
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:9,12]",
"WARN: Dropping side-effect-free statement [test/compress/dead-code.js:1,15]",
"WARN: Dropping side-effect-free statement [test/compress/dead-code.js:6,28]",
]
node_version: "<=4"
}
@@ -236,7 +253,7 @@ collapse_vars_lvalues_drop_assign: {
}
}
collapse_vars_misc1: {
collapse_vars_misc: {
options = {
collapse_vars: true,
dead_code: true,
@@ -1169,27 +1186,196 @@ redundant_assignments: {
expect_stdout: "PASS PASS"
}
self_assignments: {
self_assignments_1: {
options = {
dead_code: true,
}
input: {
var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" };
var a = "PASS";
a = a;
l[0] = l[0];
l[b] = l[b];
l[b++] = l[b++];
o.p = o.p;
console.log(a, b, l[0], o.p);
console.log(a);
}
expect: {
var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" };
var a = "PASS";
a;
l[0];
l[b];
l[b++] = l[b++];
o.p;
console.log(a, b, l[0], o.p);
console.log(a);
}
expect_stdout: "PASS 2 PASS PASS"
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

@@ -1730,7 +1730,7 @@ chained_3: {
expect: {
console.log(function(a, b) {
var c = b;
b++;
+b;
return c;
}(0, 2));
}
@@ -1997,7 +1997,7 @@ issue_3146_4: {
expect_stdout: "PASS"
}
issue_3192: {
issue_3192_1: {
options = {
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: {
options = {
pure_getters: "strict",
@@ -2161,8 +2181,7 @@ issue_3515_1: {
expect: {
var c = 0;
(function() {
this[c++] = 0;
for (var key20 in !0);
for (var key20 in !(this[c++] = 0));
})();
console.log(c);
}
@@ -2285,7 +2304,7 @@ issue_3598: {
try {
(function() {
a = "PASS";
var c = (void (c.p = 0))[!1];
(void ((void 0).p = 0))[!1];
})();
} catch (e) {}
console.log(a);
@@ -2557,11 +2576,512 @@ issue_3899: {
console.log(typeof a);
}
expect: {
++a;
var a = function() {
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,
}
input: {
console.log(
[1, , 3][1],
[1, 2, 3, a] + 1,
[1, 2, 3, 4] + 1,
[1, 2, 3, a][0] + 1,
[1, 2, 3, 4][0] + 1,
[1, 2, 3, 4][6 - 5] + 1,
[1, , 3, 4][6 - 5] + 1,
[[1, 2], [3, 4]][0] + 1,
[[1, 2], [3, 4]][6 - 5][1] + 1,
[[1, 2], , [3, 4]][6 - 5][1] + 1
);
var a = "PASS";
Array.prototype[1] = a;
console.log([, ].length);
console.log("" + [, , ]);
console.log([1, , 3][1]);
console.log([1, 2, 3, a] + 1);
console.log([1, 2, 3, 4] + 1);
console.log([1, 2, 3, a][0] + 1);
console.log([1, 2, 3, 4][0] + 1);
console.log([1, 2, 3, 4][6 - 5] + 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: {
console.log(
void 0,
[1, 2, 3, a] + 1,
"1,2,3,41",
[1, 2, 3, a][0] + 1,
2,
3,
NaN,
"1,21",
5,
(void 0)[1] + 1
);
var a = "PASS";
Array.prototype[1] = a;
console.log([, ].length);
console.log("" + [, , ]);
console.log([1, , 3][1]);
console.log([1, 2, 3, a] + 1);
console.log("1,2,3,41");
console.log([1, 2, 3, a][0] + 1);
console.log(2);
console.log(3);
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: {
@@ -1579,9 +1596,9 @@ issue_2968_1: {
expect: {
var c = "FAIL";
(function() {
b = -(a = 42),
void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0));
var a, b;
a = 42,
void ((a <<= 0) && (a[(c = "PASS", 0)] = 0));
var a;
})();
console.log(c);
}
@@ -2341,10 +2358,7 @@ issue_3878_1: {
console.log(b ? "PASS" : "FAIL");
}
expect: {
var b = function(a) {
return (a = 0) == (a && this > (a += 0));
}();
console.log(b ? "PASS" : "FAIL");
console.log(true ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
@@ -2412,9 +2426,9 @@ issue_3887: {
expect: {
(function(b) {
try {
b-- && console.log("PASS");
1, console.log("PASS");
} catch (a_2) {}
})(1);
})();
}
expect_stdout: "PASS"
}
@@ -2435,12 +2449,11 @@ issue_3903: {
console.log(d);
}
expect: {
var a = "PASS";
function f(b, c) {
return console, c;
}
var d = f(f(), a = a);
console.log(d);
f(f(), "PASS");
console.log("PASS");
}
expect_stdout: "PASS"
}
@@ -2478,10 +2491,559 @@ issue_3920: {
console.log(a);
}
expect: {
var a = function(b) {
return (b[b = 0] = 0) >= (b ? 0 : 1);
}("foo");
console.log(a);
(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"
}
issue_4214: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
return function() {
try {
return a;
} finally {
var b = 0;
}
}(a++ && this());
}
var c = f();
console.log(c);
}
expect: {
var c = function(a) {
return function() {
try {
return a;
} finally {}
}(a++ && this());
}();
console.log(c);
}
expect_stdout: "NaN"
}

View File

@@ -1483,8 +1483,7 @@ issue_2663_2: {
}
expect: {
(function() {
var i;
for (i in { a: 1, b: 2, c: 3 })
for (var i in { a: 1, b: 2, c: 3 })
j = i, console.log(j);
var j;
})();
@@ -2336,6 +2335,7 @@ issue_3274: {
inline: true,
join_vars: true,
loops: true,
passes: 2,
reduce_vars: true,
unused: true,
}
@@ -2676,7 +2676,7 @@ cross_references_3: {
};
return Math.square(n) + Math.cube(n);
};
}(Math)(2));
}()(2));
console.log(Math.square(3), Math.cube(3));
}
expect_stdout: [
@@ -4705,3 +4705,374 @@ issue_3911: {
}
expect_stdout: "PASS"
}
issue_3929: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var abc = function f() {
(function() {
switch (f) {
default:
var abc = 0;
case 0:
abc.p;
}
console.log(typeof f);
})();
};
typeof abc && abc();
})();
}
expect: {
(function() {
var abc = function f() {
(function() {
switch (f) {
default:
var abc = 0;
case 0:
abc.p;
}
console.log(typeof f);
})();
};
typeof abc && abc();
})();
}
expect_stdout: "function"
}
issue_4006: {
options = {
dead_code: true,
evaluate: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var a = 0;
(function() {
(function(b, c) {
for (var k in console.log(c), 0)
return b += 0;
})(0, --a);
return a ? 0 : --a;
})();
}
expect: {
var a = 0;
(function(c) {
for (var k in console.log(c), 0)
return;
})(--a), a || --a;
}
expect_stdout: "-1"
}
issue_4155: {
options = {
functions: true,
inline: true,
merge_vars: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a;
(function() {
console.log(a);
})(a);
var b = function() {};
b && console.log(typeof b);
})();
}
expect: {
(function() {
void console.log(b);
var b = function() {};
b && console.log(typeof b);
})();
}
expect_stdout: [
"undefined",
"function",
]
}
issue_4159: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 42, c = function(b) {
(b = a) && console.log(a++, b);
}(c = a);
}
expect: {
var a = 42;
(b = a) && console.log(a++, b);
var b;
}
expect_stdout: "42 42"
}
direct_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f(a, b) {
function g(c) {
return c >> 1;
}
return g(a) + g(b);
}
console.log(f(13, 31));
}
expect: {
function f(a, b) {
return (a >> 1) + (b >> 1);
}
console.log(f(13, 31));
}
expect_stdout: "21"
}
direct_inline_catch_redefined: {
options = {
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_4171_1: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
try {
while (a)
var e = function() {};
} catch (e) {
return function() {
return e;
};
}
}(!console));
}
expect: {
console.log(function(a) {
try {
while (a)
var e = function() {};
} catch (e) {
return function() {
return e;
};
}
}(!console));
}
expect_stdout: "undefined"
}
issue_4171_2: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
try {
while (a);
} catch (e) {
return function() {
return e;
};
} finally {
var e = function() {};
}
}(!console));
}
expect: {
console.log(function(a) {
try {
while (a);
} catch (e) {
return function() {
return e;
};
} finally {
function e() {}
}
}(!console));
}
expect_stdout: "undefined"
}
catch_defun: {
mangle = {
toplevel: true,
}
input: {
try {
throw 42;
} catch (a) {
function f() {
return typeof a;
}
}
console.log(f());
}
expect: {
try {
throw 42;
} catch (o) {
function t() {
return typeof o;
}
}
console.log(t());
}
expect_stdout: true
}
catch_no_argname: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f() {
return a;
}
try {
throw a;
} catch {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, f(), g());
}
expect: {
var a = "PASS";
try {
throw a;
} catch {
console.log(a, a, a);
}
console.log(a, a, a);
}
expect_stdout: [
"PASS PASS PASS",
"PASS PASS PASS",
]
node_version: ">=10"
}
issue_4186: {
options = {
conditionals: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
console.log(typeof function() {
return function() {
function f() {
if (1)
g();
else
(function() {
return f;
});
}
return f;
function g() {
if (1) {
if (0)
h;
else
h();
var key = 0;
}
}
function h() {
return factory;
}
};
}()());
}
expect: {
console.log(typeof function() {
return function f() {
1 ? void (1 && (0 ? h : h(), 0)) : function() {
return f;
};
};
function h() {
return factory;
}
}());
}
expect_stdout: "function"
}

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: {
options = {
global_defs: {

View File

@@ -972,3 +972,72 @@ issue_3871: {
}
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: {
var a = 0, b, c = "FAIL";
(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 {
throw 0;
} catch (e) {
(function() {
(function n() {
(function e() {
c = "PASS";
})();
})();
@@ -2522,3 +2522,340 @@ issue_3918: {
}
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

@@ -594,3 +594,157 @@ iife_if_return_simple: {
}
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: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
"INFO: pass 0: last_count: Infinity, count: 36",
"INFO: pass 0: last_count: Infinity, count: 35",
"WARN: Dropping 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]",
"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: pass 1: last_count: 36, count: 18",
"INFO: pass 1: last_count: 35, 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: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
"INFO: pass 0: last_count: Infinity, count: 47",
"INFO: pass 0: last_count: Infinity, count: 46",
"WARN: Dropping 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]",
"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: pass 1: last_count: 47, count: 29",
"INFO: pass 1: last_count: 46, count: 29",
]
}

View File

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

View File

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

View File

@@ -277,8 +277,8 @@ join_object_assignments_forin: {
}
expect: {
console.log(function() {
var o = { a: "PASS" };
for (var a in o)
var o = { a: "PASS" }, a;
for (a in o)
return o[a];
}());
}
@@ -808,9 +808,9 @@ issue_3795: {
}
expect: {
var a = "FAIL", d = function() {
if (a = 42, d) return -1;
if (void 0) return -1;
a = "PASS";
}();
}(a = 42);
console.log(a, d);
}
expect_stdout: "PASS undefined"

View File

@@ -306,8 +306,6 @@ issue_2298: {
expect: {
!function() {
(function() {
var a = undefined;
var undefined = a++;
try {
!function() {
(void 0)[1] = "foo";

View File

@@ -201,7 +201,7 @@ evaluate: {
}
}
issue_1532: {
issue_1532_1: {
options = {
evaluate: true,
loops: true,
@@ -210,18 +210,56 @@ issue_1532: {
function f(x, y) {
do {
if (x) break;
foo();
console.log(y);
} 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: {
function f(x, y) {
do {
if (x) break;
foo();
} while (false);
if (x) {
console.log(x);
break;
}
} while (console.log(y), false);
}
f(null, "PASS");
f(42, "FAIL");
}
expect_stdout: [
"PASS",
"42",
]
}
issue_186: {
@@ -509,8 +547,8 @@ dead_code_condition: {
console.log(a);
}
expect: {
var c;
var a = 0, b = 5;
var c;
a += 1, 0,
console.log(a);
}
@@ -756,7 +794,37 @@ empty_for_in_side_effects: {
expect_warnings: [
"WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
"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]",
]
}
@@ -933,3 +1001,224 @@ issue_3634_2: {
}
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"
}
do_continue: {
options = {
loops: true,
}
input: {
try {
do {
continue;
} while ([ A ]);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
do {
continue;
} while ([ A ]);
} catch (e) {
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: {
console.log(
x + 1 + 2,
2 * x,
2 * +x,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
@@ -130,7 +130,7 @@ evaluate_1_unsafe_math: {
expect: {
console.log(
x + 1 + 2,
2 * x,
2 * +x,
+x + 3,
1 + x + 2 + 3,
3 | x,
@@ -148,45 +148,52 @@ evaluate_1_unsafe_math: {
evaluate_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: false,
}
input: {
var x = "42", y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y = null;
[
x + 1 + 2,
2 * x,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
1 + (2 + x + 3),
2 + ~x + 3 + 1,
2 + ~x + 3 - y,
0 & x,
2 + (x |= 0) + 3 + 1,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + "12",
2 * x,
+x + 1 + 2,
1 + x + "23",
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
2 + x + 3 + 1,
2 + ~x + 3 + 1,
2 + ~x + 3,
0 & x,
2 + (x |= 0) + 3 + 1,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"string 4212",
@@ -207,45 +214,52 @@ evaluate_2: {
evaluate_2_unsafe_math: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: true,
}
input: {
var x = "42", y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y = null;
[
x + 1 + 2,
2 * x,
+x + 3,
1 + x + 2 + 3,
3 | x,
6 + x--,
x*y + 6,
1 + (2 + x + 3),
6 + ~x,
5 + ~x - y,
0 & x,
6 + (x |= 0),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + "12",
2 * x,
+x + 3,
1 + x + "23",
3 | x,
6 + x--,
x*y + 6,
6 + x,
6 + ~x,
5 + ~x,
0 & x,
6 + (x |= 0),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"string 4212",
@@ -310,45 +324,52 @@ evaluate_4: {
evaluate_5: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: false,
}
input: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
a - 2 + 3,
a - 2 - 3,
+a + 2 + 3,
+a + 2 - 3,
2 - a + 3,
2 - a - 3,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 2 + 3,
+a + 2 - 3,
a - 2 + 3,
a - 2 - 3,
+a + 2 + 3,
+a + 2 - 3,
2 - a + 3,
2 - a - 3,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect_stdout: [
"number 6",
@@ -369,45 +390,52 @@ evaluate_5: {
evaluate_5_unsafe_math: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: true,
}
input: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect: {
var a = "1";
[
+a + 5,
+a + -1,
a - -1,
a - 5,
+a + 5,
+a + -1,
5 - a,
-1 - a,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 5,
+a + -1,
a - -1,
a - 5,
+a + 5,
+a + -1,
5 - a,
-1 - a,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect_stdout: [
"number 6",
@@ -546,37 +574,44 @@ evaluate_6_unsafe_math: {
evaluate_7: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: false,
}
input: {
var x = "42", 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) {
console.log(typeof n, n);
});
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),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", 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) {
console.log(typeof n, n);
});
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),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"number 48",
@@ -593,37 +628,44 @@ evaluate_7: {
evaluate_7_unsafe_math: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: true,
}
input: {
var x = "42", 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) {
console.log(typeof n, n);
});
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),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y;
[
+x + 5 + !y,
+x + 5 - !y,
+x + -1 - !y,
+x + -1 + !y,
x - -1 + !y,
x - -1 - !y,
x - 5 - !y,
x - 5 + !y,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num, y) {
var x = "" + num;
[
+x + 5 + !y,
+x + 5 - !y,
+x + -1 - !y,
+x + -1 + !y,
x - -1 + !y,
x - -1 - !y,
x - 5 - !y,
x - 5 + !y,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"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: {
options = {
evaluate: true,
@@ -1251,3 +1309,29 @@ issue_3695: {
}
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 = {
properties: {
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: {
options = {
evaluate: true,
@@ -1046,16 +1093,22 @@ array_hole: {
side_effects: true,
}
input: {
console.log(
[ 1, 2, , 3][1],
[ 1, 2, , 3][2],
[ 1, 2, , 3][3]
);
Array.prototype[2] = "PASS";
console.log([ 1, 2, , 3 ][1]);
console.log([ 1, 2, , 3 ][2]);
console.log([ 1, 2, , 3 ][3]);
}
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: {
@@ -1070,11 +1123,7 @@ new_this: {
}
}.f(42);
}
expect: {
new function(a) {
this.a = a;
}(42);
}
expect: {}
}
issue_2513: {

View File

@@ -136,7 +136,7 @@ relational: {
side_effects :true,
}
input: {
foo() in foo();
foo() in new foo();
foo() instanceof bar();
foo() < "bar";
bar() > foo();

View File

@@ -848,9 +848,8 @@ collapse_vars_1_true: {
}
expect: {
function f(a, b) {
for (;;) {
for (;;)
if (a.g() || b.p) break;
}
}
}
}

View File

@@ -120,7 +120,7 @@ modified: {
expect: {
function f0() {
var b = 2;
b++;
+b;
console.log(2);
console.log(4);
}
@@ -905,7 +905,7 @@ use_before_var: {
}
expect: {
function f(){
console.log(t);
console.log(void 0);
var t = 1;
}
}
@@ -981,12 +981,12 @@ inner_var_for_1: {
expect: {
function f() {
var a = 1;
x(1, b, d);
for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) {
x(1, void 0, d);
for (var b = 2, c = 3; x(1, 2, 3, d); x(1, 2, 3, d)) {
var d = 4, e = 5;
x(1, b, 3, d, e);
x(1, 2, 3, d, e);
}
x(1, b, 3, d, e);
x(1, 2, 3, d, e);
}
}
}
@@ -1520,10 +1520,7 @@ func_inline: {
}
expect: {
function f() {
console.log(1 + h());
var h = function() {
return 2;
};
console.log(1 + (void 0)());
}
}
}
@@ -1627,7 +1624,7 @@ defun_label: {
expect_stdout: true
}
double_reference: {
double_reference_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
@@ -1641,6 +1638,32 @@ double_reference: {
g();
}
}
expect: {
function f() {
var g = function g() {
g();
};
g();
}
}
}
double_reference_2: {
options = {
functions: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
var g = function g() {
g();
};
g();
}
}
expect: {
function f() {
(function g() {
@@ -1650,6 +1673,60 @@ double_reference: {
}
}
double_reference_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var x = function f() {
return f;
};
function g() {
return x();
}
console.log(g() === g());
}
expect: {
var x = function f() {
return f;
};
function g() {
return x();
}
console.log(g() === g());
}
expect_stdout: "true"
}
double_reference_4: {
options = {
comparisons: true,
functions: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var x = function f() {
return f;
};
function g() {
return x();
}
console.log(g() === g());
}
expect: {
console.log(true);
}
expect_stdout: "true"
}
iife_arguments_1: {
options = {
reduce_funcs: true,
@@ -1689,8 +1766,35 @@ iife_arguments_2: {
}
expect: {
(function() {
console.log(function f() {
var x = function f() {
return f;
};
console.log(x() === arguments[0]);
})();
}
expect_stdout: true
}
iife_arguments_3: {
options = {
functions: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var x = function f() {
return f;
};
console.log(x() === arguments[0]);
})();
}
expect: {
(function() {
console.log(function x() {
return x;
}() === arguments[0]);
})();
}
@@ -2034,6 +2138,7 @@ issue_1670_4: {
issue_1670_5: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
keep_fargs: false,
@@ -2065,11 +2170,13 @@ issue_1670_5: {
issue_1670_6: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
switches: true,
unused: true,
@@ -2087,10 +2194,9 @@ issue_1670_6: {
})(1);
}
expect: {
(function(a) {
a = 1;
console.log(a);
})(1);
(function() {
console.log(1);
})();
}
expect_stdout: "1"
}
@@ -2309,7 +2415,7 @@ redefine_farg_2: {
console.log(typeof [], "number",function(a, b) {
a = b;
return typeof a;
}([]));
}());
}
expect_stdout: "object number undefined"
}
@@ -2372,8 +2478,7 @@ delay_def: {
return;
}
function g() {
return a;
var a = 1;
return;
}
console.log(f(), g());
}
@@ -2395,7 +2500,7 @@ delay_def_lhs: {
expect: {
console.log(function() {
long_name++;
return long_name;
return NaN;
var long_name;
}());
}
@@ -2651,11 +2756,9 @@ var_assign_5: {
}
expect: {
!function() {
var a;
!function(b) {
a = 2,
console.log(a, b);
}(a);
console.log(2, void 0);
}();
}();
}
expect_stdout: "2 undefined"
@@ -2676,8 +2779,8 @@ var_assign_6: {
}
expect: {
!function() {
var a = function(){}(a = 1);
console.log(a);
(function(){}());
console.log(void 0);
}();
}
expect_stdout: "undefined"
@@ -2724,8 +2827,8 @@ issue_1814_1: {
expect: {
!function() {
!function(a) {
console.log(a++, 42);
}(0);
console.log(0, 42);
}();
}();
}
expect_stdout: "0 42"
@@ -2751,8 +2854,8 @@ issue_1814_2: {
expect: {
!function() {
!function(a) {
console.log("321", a++);
}(0);
console.log("321", 0);
}();
}();
}
expect_stdout: "321 0"
@@ -4725,7 +4828,7 @@ escape_conditional: {
function bar() {}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("FAIL");
else
console.log("PASS");
@@ -4763,7 +4866,7 @@ escape_sequence: {
function bar() {}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("FAIL");
else
console.log("PASS");
@@ -4808,7 +4911,7 @@ escape_throw: {
function foo() {}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("FAIL");
else
console.log("PASS");
@@ -4845,7 +4948,7 @@ escape_local_conditional: {
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("PASS");
else
console.log("FAIL");
@@ -4882,7 +4985,7 @@ escape_local_sequence: {
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("PASS");
else
console.log("FAIL");
@@ -4926,7 +5029,7 @@ escape_local_throw: {
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("PASS");
else
console.log("FAIL");
@@ -4980,11 +5083,7 @@ inverted_var: {
console.log(1, 2, 3, 4, 5, function c() {
c = 6;
return c;
}(), 7, function() {
c = 8;
return c;
var c = "foo";
}());
}(), 7, 8);
}
expect_stdout: true
}
@@ -5180,9 +5279,7 @@ defun_var_3: {
var a = 42, b;
}
expect: {
function a() {}
console.log(typeof a, "function");
var a = 42;
console.log("function", "function");
}
expect_stdout: "function function"
}
@@ -5277,11 +5374,11 @@ defun_catch_4: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect_stdout: "42"
node_version: "<=4"
expect_stdout: true
}
defun_catch_5: {
@@ -5303,10 +5400,10 @@ defun_catch_5: {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect_stdout: "42"
node_version: "<=4"
expect_stdout: true
}
defun_catch_6: {
@@ -5493,7 +5590,7 @@ lvalues_def_1: {
}
expect: {
var b = 1;
var a = b++, b = NaN;
var a = +b, b = NaN;
console.log(a, b);
}
expect_stdout: "1 NaN"
@@ -7122,3 +7219,385 @@ issue_3922: {
}
expect_stdout: "PASS"
}
issue_3949_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
(function f(a) {
var a = void (a = 0, g);
function g() {
console.log(typeof a);
}
g();
})();
}
expect: {
(function f(a) {
var a = void (a = 0, g);
function g() {
console.log(typeof a);
}
g();
})();
}
expect_stdout: "undefined"
}
issue_3949_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
var a = void (a = 0, g);
function g() {
console.log(typeof a);
}
g();
})();
}
expect: {
(function(a) {
a = void (a = 0, g);
function g() {
console.log(typeof a);
}
g();
})();
}
expect_stdout: "undefined"
}
issue_3949_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
function f() {}
for (var a, i = 3; 0 <= --i; ) {
a = f;
console.log(a === b);
var b = a;
}
}
expect: {
function f() {}
for (var a, i = 3; 0 <= --i; ) {
a = f;
console.log(a === b);
var b = a;
}
}
expect_stdout: [
"false",
"true",
"true",
]
}
issue_3949_4: {
options = {
reduce_vars: true,
unused: true,
toplevel: true,
}
input: {
function f() {}
for (var a, i = 3; 0 <= --i; ) {
a = f;
console.log(a === b);
var b = a;
}
}
expect: {
function f() {}
for (var a, i = 3; 0 <= --i; ) {
a = f;
console.log(a === b);
var b = a;
}
}
expect_stdout: [
"false",
"true",
"true",
]
}
local_assignment_lambda: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL";
function f() {
a = "PASS";
console.log(a);
}
f();
f();
}
expect: {
function f() {
console.log("PASS");
}
f(),
f();
}
expect_stdout: [
"PASS",
"PASS",
]
}
local_assignment_loop: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL";
do {
a = "PASS";
console.log(a);
} while (!console);
}
expect: {
do {
console.log("PASS");
} while (!console);
}
expect_stdout: "PASS"
}
issue_3957_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
function f(a) {
while (a += console.log(a = 0))
a = 0;
}
f("FAIL");
}
expect: {
function f(a) {
while (a += console.log(a = 0))
a = 0;
}
f("FAIL");
}
expect_stdout: [
"0",
"0",
]
}
issue_3957_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function f(a) {
while (a += console.log(a = 0))
a = 0;
}
f("FAIL");
}
expect: {
function f(a) {
while (a += console.log(a = 0))
a = 0;
}
f("FAIL");
}
expect_stdout: [
"0",
"0",
]
}
issue_3958: {
options = {
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a;
(function(b) {
(function(c) {
console.log(c[0] = 1);
})(a = b);
--a;
})([]);
console.log(a);
}
expect: {
var a;
(function(b) {
(function(c) {
console.log(c[0] = 1);
})(a = []);
--a;
})();
console.log(a);
}
expect_stdout: [
"1",
"0",
]
}
issue_3974: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
var a = 0 in 0;
0 && a;
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
var a = 0 in 0;
0 && a;
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_4030: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
{
delete (a = "PASS");
A = "PASS";
}
console.log(A);
}
expect: {
A = "PASS";
console.log(A);
}
expect_stdout: "PASS"
}
global_assign: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
A = "FAIL";
this.A = "PASS";
console.log(A);
}
expect: {
A = "FAIL";
this.A = "PASS";
console.log(A);
}
expect_stdout: "PASS"
}
issue_4188_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
try {
while (A)
var a = function() {}, b = a;
} catch (a) {
console.log(function() {
return typeof a;
}(), typeof b);
}
})();
}
expect: {
(function() {
try {
while (A)
var a = function() {}, b = a;
} catch (a) {
console.log(function() {
return typeof a;
}(), typeof b);
}
})();
}
expect_stdout: "object undefined"
}
issue_4188_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
try {
throw 42;
} catch (a) {
console.log(function() {
return typeof a;
}(), typeof b);
}
while (!console)
var a = function() {}, b = a;
})();
}
expect: {
(function() {
try {
throw 42;
} catch (a) {
console.log(function() {
return typeof a;
}(), typeof b);
}
while (!console)
var a = function() {}, b = a;
})();
}
expect_stdout: "number undefined"
}

View File

@@ -80,3 +80,21 @@ log_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"
}
forin: {
forin_1: {
options = {
sequences: true,
}
@@ -895,6 +895,49 @@ forin: {
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: {
options = {
sequences: true,
@@ -1112,3 +1155,25 @@ issue_3703: {
}
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;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
(void 0).isNaN;
}
}
}
@@ -246,6 +245,31 @@ unsafe_builtin_2: {
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: {
options = {
side_effects: true,
@@ -275,3 +299,137 @@ drop_value: {
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: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -19,6 +20,7 @@ constant_switch_1: {
constant_switch_2: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -39,6 +41,7 @@ constant_switch_2: {
constant_switch_3: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -60,6 +63,7 @@ constant_switch_3: {
constant_switch_4: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -86,6 +90,7 @@ constant_switch_4: {
constant_switch_5: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -120,6 +125,7 @@ constant_switch_5: {
constant_switch_6: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -154,6 +160,7 @@ constant_switch_6: {
constant_switch_7: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -197,6 +204,7 @@ constant_switch_7: {
constant_switch_8: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -226,6 +234,7 @@ constant_switch_8: {
constant_switch_9: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -315,6 +324,7 @@ keep_default: {
issue_1663: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -551,6 +561,7 @@ issue_441_2: {
issue_1674: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
@@ -876,6 +887,7 @@ beautify: {
issue_1758: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
@@ -898,15 +910,16 @@ issue_1758: {
issue_2535: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch(w(), 42) {
case 13: x();
case 42: y();
default: z();
case 13: x();
case 42: y();
default: z();
}
}
expect: {
@@ -919,6 +932,7 @@ issue_2535: {
issue_1750: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
switches: true,
@@ -963,6 +977,7 @@ drop_switch_1: {
drop_switch_2: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
@@ -1007,6 +1022,7 @@ drop_switch_3: {
drop_switch_4: {
options = {
conditionals: true,
dead_code: true,
switches: true,
}
@@ -1028,3 +1044,140 @@ drop_switch_4: {
}
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

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

View File

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

View File

@@ -1,2 +1,2 @@
new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSJ9
console.log(3);
//# 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;
for (var k in o) L17060: {
a++;
UNUSED: {
console.log(0 - .1 - .1 - .1);
}
var a;
console.log(k);

View File

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

View File

@@ -249,7 +249,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
@@ -257,7 +257,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n");
@@ -330,7 +330,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
var lines = stderr.split(/\n/);
@@ -342,7 +342,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
var lines = stderr.split(/\n/);
@@ -354,7 +354,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
var lines = stderr.split(/\n/);
@@ -366,7 +366,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
var lines = stderr.split(/\n/);
@@ -378,7 +378,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -392,7 +392,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -406,7 +406,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -420,7 +420,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -434,7 +434,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -448,7 +448,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -462,7 +462,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -476,7 +476,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -490,7 +490,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -504,7 +504,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -518,7 +518,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -532,7 +532,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -546,7 +546,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -560,7 +560,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -574,7 +574,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -588,7 +588,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -602,7 +602,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -616,7 +616,7 @@ describe("bin/uglifyjs", function() {
});
});
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) {
assert.ok(err);
assert.strictEqual(stdout, "");
@@ -629,6 +629,18 @@ describe("bin/uglifyjs", function() {
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) {
var command = [
uglifyjscmd,
@@ -717,7 +729,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/issue-3315/input.js --config-file test/input/issue-3315/config.json";
exec(command, function(err, stdout) {
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();
});
});

View File

@@ -28,4 +28,65 @@ describe("Number literals", function() {
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] + ";");
});
});
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() {
var result = reduce_test(read("test/input/reduce/label.js"), {
compress: {
unsafe_math: true,
},
mangle: false,
}, {
verbose: false,
@@ -296,4 +299,18 @@ describe("test/reduce.js", function() {
"// }",
]).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

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

View File

@@ -112,19 +112,18 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// no structural AST changes before this point.
if (node.start._permute >= REPLACEMENTS.length) return;
if (parent instanceof U.AST_Assign
&& parent.left === node
|| parent instanceof U.AST_Unary
&& parent.expression === node
&& ["++", "--", "delete"].indexOf(parent.operator) >= 0) {
// ignore lvalues
// ignore lvalues
if (parent instanceof U.AST_Assign && parent.left === node) return;
if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
case "++":
case "--":
case "delete":
return;
}
if ((parent instanceof U.AST_For || parent instanceof U.AST_ForIn)
&& parent.init === node && node instanceof U.AST_Var) {
// preserve for (var ...)
return node;
}
// preserve for (var xxx; ...)
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
// preserve for (xxx in ...)
if (parent instanceof U.AST_ForIn && parent.init === node) return node;
// node specific permutations with no parent logic
@@ -146,7 +145,9 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return permute < 2 ? expr : wrap_with_console_log(expr);
}
else if (node instanceof U.AST_BlockStatement) {
if (in_list) {
if (in_list && node.body.filter(function(node) {
return node instanceof U.AST_Const;
}).length == 0) {
node.start._permute++;
CHANGED = true;
return List.splice(node.body);
@@ -411,7 +412,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
start: {},
});
}
else if (node instanceof U.AST_Var) {
else if (node instanceof U.AST_Definitions) {
// remove empty var statement
if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({
start: {},
@@ -428,7 +429,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
return to_sequence(node.args);
}
if (node instanceof U.AST_Catch) {
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)),
@@ -438,7 +439,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
}));
var code = testcase_ast.print_to_string();
if (diff = test_for_diff(code, minify_options, result_cache, max_timeout)) {
var diff = test_for_diff(code, minify_options, result_cache, max_timeout);
if (diff && !diff.timed_out && !diff.error) {
testcase = code;
differs = diff;
} else {
@@ -451,6 +453,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
node.start = JSON.parse(JSON.stringify(node.start));
node.start._permute = 0;
}));
var before_iterations = testcase;
for (var c = 0; c < max_iterations; ++c) {
if (verbose && pass == 1 && c % 25 == 0) {
log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
@@ -493,12 +496,31 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
}
}
if (c == 0) break;
if (before_iterations === testcase) break;
if (verbose) {
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
}
}
testcase = try_beautify(testcase, minify_options, differs.unminified_result, result_cache, max_timeout);
var beautified = U.minify(testcase, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
comments: true,
},
});
testcase = {
code: testcase,
};
if (!beautified.error) {
diff = test_for_diff(beautified.code, minify_options, result_cache, max_timeout);
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)));
@@ -537,34 +559,6 @@ function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, "");
}
function try_beautify(testcase, minify_options, expected, result_cache, timeout) {
var result = U.minify(testcase, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
comments: true,
},
});
if (result.error) return {
code: testcase,
};
var toplevel = sandbox.has_toplevel(minify_options);
if (isNaN(timeout)) {
if (!U.minify(result.code, minify_options).error) return {
code: testcase,
};
} else {
var actual = run_code(result.code, toplevel, result_cache, timeout);
if (!sandbox.same_stdout(expected, actual)) return {
code: testcase,
};
}
result.code = "// (beautified)\n" + result.code;
return result;
}
function has_exit(fn) {
var found = false;
var tw = new U.TreeWalker(function(node) {
@@ -649,7 +643,15 @@ function wrap_with_console_log(node) {
function run_code(code, toplevel, result_cache, timeout) {
var key = crypto.createHash("sha1").update(code).digest("base64");
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout));
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) {
@@ -657,21 +659,19 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
if (minified.error) return minified;
var toplevel = sandbox.has_toplevel(minify_options);
var elapsed = Date.now();
var unminified_result = run_code(code, toplevel, result_cache, max_timeout);
elapsed = Date.now() - elapsed;
var timeout = Math.min(100 * elapsed, max_timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout);
var unminified = run_code(code, toplevel, result_cache, max_timeout);
var timeout = Math.min(100 * unminified.elapsed, max_timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
if (sandbox.same_stdout(unminified_result, minified_result)) {
return is_timed_out(unminified_result) && is_timed_out(minified_result) && {
if (sandbox.same_stdout(unminified.result, minified_result)) {
return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
timed_out: true,
};
}
return {
unminified_result: unminified_result,
unminified_result: unminified.result,
minified_result: minified_result,
elapsed: elapsed,
elapsed: unminified.elapsed,
};
}

View File

@@ -40,7 +40,7 @@ function createContext() {
arg.constructor.toString();
if (level--) for (var key in arg) {
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;

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",
];
var BINARY_OPS_NO_COMMA = [
var BINARY_OPS = [
" + ", // 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 = [
"=",
@@ -269,6 +275,7 @@ var CANNOT_RETURN = true;
var NO_DEFUN = false;
var DEFUN_OK = true;
var DONT_STORE = true;
var NO_CONST = true;
var VAR_NAMES = [
"a",
@@ -306,6 +313,7 @@ var TYPEOF_OUTCOMES = [
"crap",
];
var block_vars = [];
var unique_vars = [];
var loops = 0;
var funcs = 0;
@@ -323,6 +331,7 @@ function strictMode() {
function createTopLevelCode() {
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
block_vars.length = 0;
unique_vars.length = 0;
loops = 0;
funcs = 0;
@@ -368,33 +377,66 @@ function filterDirective(s) {
return s;
}
function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
var block_len = block_vars.length;
var var_len = VAR_NAMES.length;
var consts = [];
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
while (!rng(block_vars.length > block_len ? 10 : 100)) {
var name = createVarName(MANDATORY, DONT_STORE);
consts.push(name);
block_vars.push(name);
}
unique_vars.length -= 6;
fn(function() {
var s = [];
if (consts.length) {
var save = VAR_NAMES;
VAR_NAMES = VAR_NAMES.filter(function(name) {
return consts.indexOf(name) < 0;
});
var len = VAR_NAMES.length;
s.push("const " + consts.map(function(name) {
var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
VAR_NAMES.push(name);
return name + " = " + value;
}).join(", ") + ";");
VAR_NAMES = save.concat(VAR_NAMES.slice(len));
}
return s.join("\n");
});
block_vars.length = block_len;
if (consts.length) VAR_NAMES.splice(var_len, consts.length);
}
function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
if (--recurmax < 0) { return ";"; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var namesLenBefore = VAR_NAMES.length;
var s = [];
var name;
if (allowDefun || rng(5) > 0) {
name = "f" + funcs++;
} else {
unique_vars.push("a", "b", "c");
name = createVarName(MANDATORY, !allowDefun);
unique_vars.length -= 3;
}
var s = [
"function " + name + "(" + createParams() + "){",
strictMode()
];
if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess.
s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), DEFUN_OK, canThrow, stmtDepth));
} else {
// functions with statements
s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
}
s.push("}", "");
s = filterDirective(s).join("\n");
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var namesLenBefore = VAR_NAMES.length;
if (allowDefun || rng(5) > 0) {
name = "f" + funcs++;
} else {
unique_vars.push("a", "b", "c");
name = createVarName(MANDATORY, !allowDefun);
unique_vars.length -= 3;
}
s.push("function " + name + "(" + createParams() + "){", strictMode());
s.push(defns());
if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess.
s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), DEFUN_OK, canThrow, stmtDepth));
} else {
// functions with statements
s.push(_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
}
s.push("}", "");
s = filterDirective(s).join("\n");
VAR_NAMES.length = namesLenBefore;
VAR_NAMES.length = namesLenBefore;
});
if (!allowDefun) {
// avoid "function statements" (decl inside statements)
@@ -408,7 +450,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
return s + ";";
}
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
function _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
if (--recurmax < 0) { return ";"; }
var s = "";
while (--n > 0) {
@@ -417,6 +459,15 @@ function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotRe
return s;
}
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
var s = "";
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
s += defns() + "\n";
s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
});
return s;
}
function enableLoopControl(flag, defaultValue) {
return Array.isArray(flag) && flag.indexOf("") < 0 ? flag.concat("") : flag || defaultValue;
}
@@ -490,11 +541,16 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var label = createLabel(canBreak, canContinue);
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
var optElementVar = "";
if (rng(5) > 1) {
optElementVar = "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[key" + loop + "]; ";
}
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) + "}}";
var key = rng(10) ? "key" + loop : getVarName(NO_CONST);
return [
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
label.target + " for (",
/^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:
return use_strict && rng(20) === 0 ? '"use strict";' : ";";
case STMT_EXPR:
@@ -560,13 +616,18 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
// the catch var should only be accessible in the catch clause...
// we have to do go through some trouble here to prevent leaking it
var nameLenBefore = VAR_NAMES.length;
var catchName = createVarName(MANDATORY);
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
if (!catch_redef) unique_vars.push(catchName);
s += " catch (" + catchName + ") { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
// remove catch name
if (!catch_redef) unique_vars.pop();
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var catchName = createVarName(MANDATORY);
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
if (!catch_redef) unique_vars.push(catchName);
s += " catch (" + catchName + ") { ";
s += defns() + "\n";
s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
s += " }";
// remove catch name
if (!catch_redef) unique_vars.pop();
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
});
}
if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
return s;
@@ -586,7 +647,7 @@ function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotR
if (hadDefault || rng(5) > 0) {
s.push(
"case " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ":",
createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
_createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
rng(10) > 0 ? " break;" : "/* fall-through */",
""
);
@@ -594,7 +655,7 @@ function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotR
hadDefault = true;
s.push(
"default:",
createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
_createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
""
);
}
@@ -642,7 +703,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
return getVarName();
case p++:
return getVarName() + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
return getVarName(NO_CONST) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
@@ -688,19 +749,22 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
);
break;
default:
var instantiate = rng(4) ? "new " : "";
s.push(
instantiate + "function " + name + "(){",
strictMode()
);
if (instantiate) for (var i = rng(4); --i >= 0;) {
if (rng(2)) s.push("this." + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ";");
else s.push("this[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]" + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ";");
}
s.push(
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
rng(2) == 0 ? "}" : "}()"
);
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var instantiate = rng(4) ? "new " : "";
s.push(
instantiate + "function " + name + "(){",
strictMode(),
defns()
);
if (instantiate) for (var i = rng(4); --i >= 0;) {
if (rng(2)) s.push("this." + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ";");
else s.push("this[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]" + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ";");
}
s.push(
_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
rng(2) == 0 ? "}" : "}()"
);
});
break;
}
VAR_NAMES.length = nameLenBefore;
@@ -762,6 +826,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
case p++:
return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
case p++:
return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow);
case p++:
return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++:
var name = getVarName();
var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
@@ -832,28 +900,32 @@ function getDotKey(assign) {
function createAccessor(recurmax, stmtDepth, canThrow) {
var namesLenBefore = VAR_NAMES.length;
var s;
var prop1 = getDotKey();
if (rng(2) == 0) {
s = [
"get " + prop1 + "(){",
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
"},"
];
} else {
var prop2;
do {
prop2 = getDotKey();
} while (prop1 == prop2);
s = [
"set " + prop1 + "(" + createVarName(MANDATORY) + "){",
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
"},"
];
}
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var prop1 = getDotKey();
if (rng(2) == 0) {
s = [
"get " + prop1 + "(){",
strictMode(),
defns(),
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
"},"
];
} else {
var prop2;
do {
prop2 = getDotKey();
} while (prop1 == prop2);
s = [
"set " + prop1 + "(" + createVarName(MANDATORY) + "){",
strictMode(),
defns(),
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
"},"
];
}
});
VAR_NAMES.length = namesLenBefore;
return filterDirective(s).join("\n");
}
@@ -879,7 +951,7 @@ function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
}
function _createBinaryExpr(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) {
// intentionally generate more hardcore ops
@@ -891,7 +963,7 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
case 1:
return "(" + createUnarySafePrefix() + "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + "))";
case 2:
assignee = getVarName();
assignee = getVarName(NO_CONST);
return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
case 3:
assignee = getVarName();
@@ -929,9 +1001,12 @@ function createValue() {
return VALUES[rng(VALUES.length)];
}
function createBinaryOp(noComma) {
if (noComma) return BINARY_OPS_NO_COMMA[rng(BINARY_OPS_NO_COMMA.length)];
return BINARY_OPS[rng(BINARY_OPS.length)];
function createBinaryOp(noComma, canThrow) {
var op;
do {
op = BINARY_OPS[rng(BINARY_OPS.length)];
} while (noComma && op == "," || !canThrow && op == " in ");
return op;
}
function createAssignment() {
@@ -950,9 +1025,10 @@ function createUnaryPostfix() {
return UNARY_POSTFIX[rng(UNARY_POSTFIX.length)];
}
function getVarName() {
function getVarName(noConst) {
// try to get a generated name reachable from current scope. default to just `a`
return VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || "a";
var name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
return !name || noConst && block_vars.indexOf(name) >= 0 ? "a" : name;
}
function createVarName(maybe, dontStore) {
@@ -962,7 +1038,7 @@ function createVarName(maybe, dontStore) {
do {
name = VAR_NAMES[rng(VAR_NAMES.length)];
if (suffix) name += "_" + suffix;
} while (unique_vars.indexOf(name) >= 0);
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0);
if (suffix && !dontStore) VAR_NAMES.push(name);
return name;
}
@@ -1124,18 +1200,6 @@ function log(options) {
errorln(original_result);
errorln("uglified result:");
errorln(uglify_result);
errorln("//-------------------------------------------------------------");
var reduced = reduce_test(original_code, JSON.parse(options), {
verbose: false,
}).code;
if (reduced) {
errorln();
errorln("// reduced test case (output will differ)");
errorln();
errorln(reduced);
errorln();
errorln("//-------------------------------------------------------------");
}
} else {
errorln("// !!! uglify failed !!!");
errorln(uglify_code);
@@ -1146,6 +1210,20 @@ function log(options) {
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(JSON.stringify(JSON.parse(options), null, 2));
errorln();
@@ -1159,6 +1237,11 @@ function log(options) {
}
}
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) {
uglified = uglified.split(" ");
var i = uglified.length;
@@ -1173,60 +1256,86 @@ function fuzzy_match(original, uglified) {
return true;
}
function skip_infinite_recursion(orig, toplevel) {
var code = orig;
var tries = [];
var offset = 0;
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;
var match;
while (match = re.exec(code)) {
if (/}\s*finally\s*$/.test(match[0])) {
tries.shift();
continue;
}
var index = match.index + match[0].length + 1;
if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset });
continue;
}
while (tries.length && tries[0].catch) tries.shift();
tries[0].catch = index;
var insert = "throw " + match[1] + ".ufuzz_skip || (" + match[1] + ".ufuzz_skip = " + tries[0].try + "), " + match[1] + ";";
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") {
offset += insert.length;
code = new_code;
} else if (result.name == "RangeError" && result.message == "Maximum call stack size exceeded") {
index = result.ufuzz_skip;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
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({
compress: false,
mangle: false
}) ];
var minify_options = require("./options.json").map(JSON.stringify);
var sort_globals = [
"Object.keys(this).sort().forEach(function(name) {",
" var value = this[name];",
" delete this[name];",
" this[name] = value;",
"});",
"",
].join("\n");
var original_code, original_result, errored;
var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
original_code = createTopLevelCode();
var orig_result = [ sandbox.run_code(original_code) ];
var orig_result = [ sandbox.run_code(original_code), sandbox.run_code(original_code, true) ];
errored = typeof orig_result[0] != "string";
if (!errored) orig_result.push(sandbox.run_code(original_code, true));
(errored ? fallback_options : minify_options).forEach(function(options) {
if (errored) {
println("//=============================================================");
println("// original code");
try_beautify(original_code, false, orig_result[0], println);
println();
println();
println("original result:");
println(orig_result[0]);
println();
}
minify_options.forEach(function(options) {
var o = JSON.parse(options);
var toplevel = sandbox.has_toplevel(o);
o.validate = true;
@@ -1238,20 +1347,23 @@ for (var round = 1; round <= num_iterations; round++) {
ok = sandbox.same_stdout(original_result, uglify_result);
// 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));
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) {
if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == "string" && typeof uglify_result == "string") {
ok = fuzzy_match(original_result, uglify_result);
if (!ok) {
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel);
ok = sandbox.same_stdout(fuzzy_result, uglify_result);
}
}
// ignore difference in error message caused by Temporal Dead Zone
if (!ok && errored) ok = uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError";
// ignore difference in error message caused by `in`
// ignore difference in depth of termination caused by infinite recursion
if (!ok) {
var orig_skipped = skip_infinite_recursion(original_code, toplevel);
var uglify_skipped = skip_infinite_recursion(uglify_code, toplevel);
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));
}
@@ -1261,16 +1373,6 @@ for (var round = 1; round <= num_iterations; round++) {
ok = errored && uglify_code.name == original_result.name;
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (errored) {
println("//=============================================================");
println("// original code");
try_beautify(original_code, toplevel, original_result, println);
println();
println();
println("original result:");
println(original_result);
println();
}
if (!ok && isFinite(num_iterations)) {
println();
process.exit(1);

View File

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

@@ -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>