Compare commits

...

115 Commits

Author SHA1 Message Date
Alex Lam S.L
e33c727e8b v3.10.3 2020-08-30 13:09:12 +01:00
Alex Lam S.L
f886b3fb2b fix corner case in loops & unused (#4083)
fixes #4082
2020-08-29 02:42:17 +08:00
Alex Lam S.L
b1cc15e85b fix corner case in sequences (#4080)
fixes #4079
2020-08-26 20:41:11 +08:00
Alex Lam S.L
3aa765e429 fix corner case in evaluate (#4078)
fixes #4077
2020-08-26 19:45:38 +08:00
Alex Lam S.L
93d084a1d1 fix corner case in loops & unused (#4076)
fixes #4075
2020-08-26 17:32:20 +08:00
Alex Lam S.L
c7a3e09407 enhance loops & unused (#4074)
- extend `ufuzz` generation of for-in loops
2020-08-26 09:32:55 +08:00
Alex Lam S.L
09525c7530 fix corner case in sequences (#4073) 2020-08-26 01:26:49 +08:00
Alex Lam S.L
a7e15fe73c streamline parenthesis logic (#4072) 2020-08-25 19:45:37 +08:00
Alex Lam S.L
a31c27c7cf fix corner case in collapse_vars (#4071)
fixes #4070
2020-08-25 17:23:36 +08:00
Alex Lam S.L
1caf7c7bd2 minor clean up (#4069) 2020-08-25 10:10:56 +08:00
Alex Lam S.L
0eb0c9b388 fix corner case in evaluate (#4068)
fixes #4067
2020-08-24 14:57:26 +08:00
Alex Lam S.L
7dc61cdc89 tidy up various interfaces (#4066) 2020-08-24 04:39:38 +08:00
Alex Lam S.L
af1b2f30c9 v3.10.2 2020-08-23 23:09:12 +08:00
Alex Lam S.L
37b4fc7e31 update domprops (#4065) 2020-08-23 23:06:15 +08:00
Alex Lam S.L
da85d102e3 enhance mangle.properties (#4064) 2020-08-23 08:45:39 +08:00
Alex Lam S.L
35fe1092d3 simplify traversal logic (#4063) 2020-08-23 05:45:35 +08:00
Alex Lam S.L
f2d486e771 enhance comparisons (#4062) 2020-08-23 01:03:48 +08:00
Alex Lam S.L
fee677786e fix corner case in collapse_vars (#4061) 2020-08-21 10:35:34 +08:00
Alex Lam S.L
aa83ecdb3b fix corner case in switches (#4060)
fixes #4059
2020-08-21 08:05:10 +08:00
Alex Lam S.L
a153176469 enhance conditionals & switches (#4058) 2020-08-21 00:35:39 +08:00
Alex Lam S.L
1c6384b6a5 improve ufuzz duty cycle heuristic (#4057) 2020-08-19 23:29:01 +08:00
Alex Lam S.L
e8db526f51 avoid setters during console.log() in sandbox (#4055)
fixes #4054
2020-08-19 06:14:41 +08:00
Alex Lam S.L
fa13ed4391 reject multiple defaults in switch (#4053)
fixes #4050
2020-08-17 10:09:12 +08:00
Alex Lam S.L
23f0dca992 fix corner cases in collapse_vars & dead_code (#4052)
fixes #4051
2020-08-17 05:54:27 +08:00
Alex Lam S.L
45ab3b51d8 clarify toplevel & global variable aliasing (#4046) 2020-08-10 06:39:28 +08:00
Alex Lam S.L
49670d216b fix corner case in collapse_vars (#4048)
fixes #4047
2020-08-10 05:48:56 +08:00
Alex Lam S.L
e2237d8cd2 improve ufuzz duty cycle heuristic (#4045) 2020-08-09 03:10:19 +08:00
Alex Lam S.L
91f078fe35 workaround incorrect workflow status (#4044) 2020-08-08 05:16:54 +08:00
Alex Lam S.L
a546cb881d improve ufuzz duty cycle on GitHub Actions (#4043) 2020-08-07 18:42:36 +08:00
Alex Lam S.L
84d5dffd9f tweak GitHub Actions (#4042) 2020-08-07 02:15:51 +08:00
Alex Lam S.L
a8e286f7e1 fix corner case in collapse_vars (#4041)
fixes #4040
2020-08-06 20:30:28 +08:00
Alex Lam S.L
9b05494ebc fix corner cases in aliasing of global variables (#4039)
fixes #4038
2020-08-06 09:39:50 +01:00
Alex Lam S.L
30ef20a208 tweak GitHub Actions (#4037) 2020-08-05 22:09:02 +08:00
Alex Lam S.L
a4002ef467 fix corner case in evaluate (#4036)
fixes #4035
2020-08-04 20:05:10 +08:00
Alex Lam S.L
9d758a216b v3.10.1 2020-08-02 21:08:48 +08:00
Alex Lam S.L
af13f8dd2c improve diagnostics upon AST validation failure (#4033) 2020-07-31 22:50:16 +08:00
Alex Lam S.L
88423f2574 validate against multiple parents on AST_Node (#4032)
- fix related issues in `global_defs`, `ie8` & `reduce_vars`
2020-07-31 08:09:19 +08:00
Alex Lam S.L
ee632a5519 fix corner case in reduce_vars (#4031)
fixes #4030
2020-07-31 08:05:09 +08:00
Alex Lam S.L
dfe47bcc42 fix corner case in ie8 & reduce_vars (#4029)
fixes #4028
2020-07-29 03:11:02 +08:00
Alex Lam S.L
6d3dcaa59e fix corner case in unused (#4026)
fixes #4025
2020-07-26 09:27:54 +08:00
Alex Lam S.L
1bc0df1569 fix corner case in hoist_props (#4024)
fixes #4023
2020-07-26 09:27:34 +08:00
Alex Lam S.L
a98ba994bd reduce ufuzz test cases that fail to minify() (#4021) 2020-07-21 17:22:18 +08:00
Alex Lam S.L
cd671221c5 fix corner case in ie8 & reduce_vars (#4020)
fixes #4019
2020-07-21 17:22:18 +08:00
Alex Lam S.L
bce3919748 fix corner case in unused (#4018)
fixes #4017
2020-07-21 17:21:58 +08:00
Alex Lam S.L
61b66e83f1 fix corner case in ie8 (#4016)
fixes #4015
2020-07-21 02:32:20 +08:00
Alex Lam S.L
a5db8cd14c fix corner case in collapse_vars (#4013)
fixes #4012
2020-07-20 23:28:13 +08:00
Alex Lam S.L
2021c2fa3e fix corner case in false positive detection (#4011) 2020-07-20 21:57:22 +08:00
Alex Lam S.L
484d3fd8c7 fix corner case in side_effects (#4009)
fixes #4008
2020-07-01 11:33:48 +08:00
Alex Lam S.L
3bf8699f95 fix corner case in inline (#4007)
fixes #4006
2020-06-29 09:06:23 +08:00
Alex Lam S.L
58c24f8007 v3.10.0 2020-06-21 11:30:24 +01:00
Alex Lam S.L
e61bc34eb1 fix corner case in collapse_vars (#4002)
fixes #4001
2020-06-20 02:19:37 +08:00
Alex Lam S.L
8b2cfd45fa fix corner case in rename (#4000)
fixes #3999
2020-06-15 01:29:01 +08:00
Alex Lam S.L
ae9f56be10 fix corner case in evaluate (#3998)
fixes #3997
2020-06-15 01:28:44 +08:00
Alex Lam S.L
9aed0e3a73 speed up false positive detection in ufuzz (#3996) 2020-06-14 03:42:42 +08:00
Alex Lam S.L
88850a6e05 enhance evaluate (#3995) 2020-06-14 02:50:26 +08:00
Alex Lam S.L
9e881407bd fix corner cases related to AST_Hole (#3994) 2020-06-13 15:24:57 +01:00
Alex Lam S.L
3188db7b90 remove AppVeyor (#3992) 2020-06-12 08:43:42 +08:00
Alex Lam S.L
a82ca62b66 fix corner case in dead_code (#3991) 2020-06-12 08:00:19 +08:00
Alex Lam S.L
e9465717ab enhance dead_code (#3990) 2020-06-12 02:16:13 +08:00
Alex Lam S.L
e89031f1af fix corner case in unsafe evaluate (#3989)
fixes #3988
2020-06-11 07:37:39 +08:00
Alex Lam S.L
596fad182e fix corner case in unused (#3987)
fixes #3986
2020-06-11 02:01:23 +08:00
Alex Lam S.L
ed69adedcd fix corner case in --reduce-test (#3985) 2020-06-10 15:51:00 +01:00
Alex Lam S.L
1dbf7d4a3a fix corner case in side_effects (#3984)
fixes #3983
2020-06-10 19:30:37 +08:00
Alex Lam S.L
2a9d0fc6fb improve false positive detection in ufuzz (#3982) 2020-06-10 07:28:56 +08:00
Alex Lam S.L
45db96679e perform ufuzz on more stable Node.js version (#3981) 2020-06-10 00:02:05 +08:00
Alex Lam S.L
1d15f51238 improve fix for #3976 (#3980) 2020-06-10 00:00:57 +08:00
Alex Lam S.L
ed7c82fa5e fix corner case in collapse_vars (#3978)
fixes #3976
2020-06-09 19:07:20 +08:00
Alex Lam S.L
3b273cecac improve false positive detection in ufuzz (#3977) 2020-06-09 19:07:02 +08:00
Alex Lam S.L
d764b6cc3b fix corner case in reduce_vars (#3975)
fixes #3974
2020-06-09 10:33:47 +08:00
Alex Lam S.L
08c4729eb4 improve false positive detection in ufuzz (#3973) 2020-06-09 01:47:50 +08:00
Alex Lam S.L
5561d3e7f3 fix corner case in collapse_vars (#3972)
fixes #3971
2020-06-09 00:09:21 +08:00
Alex Lam S.L
491d6ce1d5 improve false positive detection in ufuzz (#3968) 2020-06-08 14:21:45 +08:00
Alex Lam S.L
cd55eeb77c fix corner case in dead_code (#3969)
fixes #3967
2020-06-08 13:42:09 +08:00
Alex Lam S.L
3230952d57 improve handling of invalid CLI options (#3966) 2020-06-08 11:16:03 +08:00
Alex Lam S.L
df3bb8028a fix corner cases related to in (#3964) 2020-06-08 05:23:23 +08:00
Alex Lam S.L
28b7b15da1 parse command line internally (#3961) 2020-06-07 13:48:51 +08:00
Alex Lam S.L
aa37b19698 fix corner case in unused (#3963)
fixes #3962
2020-06-07 13:48:29 +08:00
Alex Lam S.L
02e889e449 improve fix for #3958 (#3960) 2020-06-06 15:07:32 +01:00
Alex Lam S.L
486ce00b8e fix corner case in reduce_vars (#3959)
fixes #3958
2020-06-06 10:04:37 +08:00
Alex Lam S.L
eb481cee8c fix corner cases in reduce_vars & unused (#3955)
fixes #3953
fixes #3956
fixes #3957
2020-06-05 18:51:21 +08:00
Alex Lam S.L
fbc9d8009b fix corner case in collapse_vars (#3954) 2020-06-05 14:28:08 +08:00
Alex Lam S.L
04fd3d90f8 fix corner cases in assignments, reduce_vars & unused (#3950)
fixes #3949
fixes #3951
2020-06-05 04:06:43 +08:00
Alex Lam S.L
a489f8cb5e add test case for #3945 (#3948) 2020-06-03 03:34:38 +08:00
Alex Lam S.L
e2e4b7fb37 fix corner case in hoist_props (#3947)
fixes #3945
2020-06-02 23:51:06 +08:00
Alex Lam S.L
c97ad98f92 fix corner case in evaluate (#3946)
fixes #3944
2020-06-02 23:50:40 +08:00
Alex Lam S.L
b24eb22c6b enhance reduce_vars (#3942) 2020-06-01 20:55:23 +08:00
Alex Lam S.L
06ba4e2ce8 fix corner case in arguments (#3939) 2020-05-31 12:18:27 +08:00
Alex Lam S.L
0eb4577a82 fix corner case in evaluate (#3938)
fixes #3937
2020-05-30 18:22:40 +08:00
Alex Lam S.L
43498769f0 fix corner case in evaluate (#3936)
fixes #3935
2020-05-29 22:10:36 +08:00
Alex Lam S.L
60c0bc1e6b fix corner case in evaluate (#3934)
fixes #3933
2020-05-29 17:48:26 +08:00
Alex Lam S.L
6a5c63e1e3 enhance evaluate, functions & inline (#3931) 2020-05-29 07:05:47 +08:00
Alex Lam S.L
d47ea77811 fix corner case in functions (#3930)
fixes #3929
2020-05-28 20:07:36 +08:00
Alex Lam S.L
7840746bd9 fix corner case in collapse_vars (#3928)
fixes #3927
2020-05-27 21:02:48 +08:00
Alex Lam S.L
49ea629f3f v3.9.4 2020-05-27 07:50:18 +01:00
Alex Lam S.L
13c72a986c fix corner case in infinite recursion detection (#3926) 2020-05-27 02:03:38 +08:00
Alex Lam S.L
08af3eae44 perform ufuzz on Pull Requests (#3925) 2020-05-25 22:55:14 +08:00
Alex Lam S.L
27bdcbbd83 fix corner cases in infinite recursion detection (#3924) 2020-05-25 22:54:57 +08:00
Alex Lam S.L
2c4d7d66ef fix corner case in reduce_vars (#3923)
fixes #3922
2020-05-24 07:38:40 +08:00
Alex Lam S.L
d1cc5270a3 fix corner case in evaluate (#3921)
fixes #3920
2020-05-22 11:38:09 +08:00
Alex Lam S.L
75c5b6029b fix corner case in ie8 & reduce_vars (#3919)
fixes #3918
2020-05-22 09:56:35 +08:00
Alex Lam S.L
fa14a9cfcd fix corner case in join_vars (#3917)
fixes #3916
2020-05-22 05:26:46 +08:00
Alex Lam S.L
aeb9ea5ac2 fix corner case in inline (#3915)
fixes #3911
2020-05-21 22:05:31 +08:00
Alex Lam S.L
798841be82 improve job resilience (#3913) 2020-05-21 04:50:42 +08:00
Alex Lam S.L
cc6eb4b15f improve ufuzz (#3912)
- preserve test case if `beautify` suppresses bug
- determine suspicious options even if `minify()` fails
2020-05-21 04:00:38 +08:00
Alex Lam S.L
14eee81dc6 update header comment for --reduce-test (#3910) 2020-05-19 11:35:33 +08:00
Alex Lam S.L
55ebb27878 fix corner case in collapse_vars (#3909)
fixes #3908
2020-05-19 11:34:50 +08:00
Alex Lam S.L
87046410ef enhance dead_code (#3907) 2020-05-19 03:53:08 +08:00
Alex Lam S.L
f9b3198714 fix corner case in evaluate (#3906)
fixes #3905
2020-05-18 08:41:10 +08:00
Alex Lam S.L
48b62393a4 fix corner case in evaluate (#3904)
fixes #3903
2020-05-17 22:25:13 +08:00
Alex Lam S.L
a00f8dade7 fix suspicious toplevel detection (#3902) 2020-05-17 21:35:17 +08:00
Alex Lam S.L
9daa2fb6f5 benchmark without validation by default (#3901) 2020-05-15 23:57:50 +08:00
Alex Lam S.L
8d81d264f4 fix corner case in functions (#3900)
fixes #3899
2020-05-15 18:03:56 +08:00
Alex Lam S.L
5ef7060098 fix corner case in collapse_vars (#3898)
fixes #3897
2020-05-15 01:09:54 +08:00
Alex Lam S.L
938368ba21 enhance collapse_vars (#3896) 2020-05-14 07:52:42 +08:00
Alex Lam S.L
fe2f1965d6 fix corner case in reduce_vars (#3895)
fixes #3894
2020-05-13 23:44:54 +08:00
51 changed files with 6808 additions and 1949 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 }}
@@ -29,7 +32,7 @@ jobs:
git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add $NODE && nvs use $NODE'; do
cd ~/.nvs
git clean -xdf
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd -
done
. ~/.nvs/nvs.sh --version

View File

@@ -1,7 +1,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 }}
TOKEN: ${{ github.token }}
jobs:
ufuzz:
strategy:
@@ -16,13 +21,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
git clean -xdf
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
@@ -31,4 +36,12 @@ jobs:
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
node test/ufuzz/job 3600000
PERIOD=1800000
if [[ $CAUSE == "schedule" ]]; then
PERIOD=`node test/ufuzz/actions $BASE_URL $TOKEN`
fi
if (( $PERIOD == 0 )); then
echo "too many jobs in queue - skipping..."
else
node test/ufuzz/job $PERIOD
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)
@@ -1157,3 +1156,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,10 +341,8 @@ function run() {
print_error(format_object(ex.defs));
}
fatal(ex);
} else if (program.output == "ast") {
if (!options.compress && !options.mangle) {
result.ast.figure_out_scope({});
}
} else if (output == "ast") {
if (!options.compress && !options.mangle) result.ast.figure_out_scope({});
print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) {
case "thedef":
@@ -304,26 +368,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 +439,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) {
@@ -274,10 +288,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,16 +412,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", "cname enclosed uses_eval uses_with parent_scope functions variables make_def", {
$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)",
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
parent_scope: "[AST_Scope?/S] link to the parent scope",
functions: "[Object/S] like `variables`, but only lists function declarations",
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
},
clone: function(deep) {
var node = this._clone(deep);

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();
});
@@ -863,21 +839,21 @@ function OutputStream(options) {
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 +870,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,7 +889,8 @@ 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() {
@@ -921,7 +899,8 @@ function OutputStream(options) {
output.space();
self._do_print_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() {
@@ -950,7 +929,8 @@ function OutputStream(options) {
output.space();
self._do_print_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() {
@@ -963,7 +943,8 @@ function OutputStream(options) {
output.space();
self._do_print_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() {
@@ -974,7 +955,7 @@ function OutputStream(options) {
});
/* -----[ functions ]----- */
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) {
DEFPRINT(AST_Lambda, function(output, nokeyword) {
var self = this;
if (!nokeyword) {
output.print("function");
@@ -992,32 +973,23 @@ 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) {
@@ -1046,7 +1018,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() {
@@ -1068,7 +1041,8 @@ function OutputStream(options) {
});
/* -----[ 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 +1060,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,7 +1096,8 @@ 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() {
@@ -1129,13 +1106,14 @@ function OutputStream(options) {
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) {
DEFPRINT(AST_Var, function(output) {
var self = this;
output.print("var");
output.space();
self.definitions.forEach(function(def, i) {
@@ -1160,7 +1138,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 +1152,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 +1162,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 +1186,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 +1208,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 +1251,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 +1264,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 +1280,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 +1299,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,7 +1374,7 @@ 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(" ");
});

View File

@@ -1092,7 +1092,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 +1107,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());

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

@@ -96,8 +96,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var 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;
@@ -149,7 +150,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
scope.def_variable(node).defun = defun;
}
});
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
@@ -230,6 +233,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
} else {
new_def = scope.def_variable(node);
}
old_def.defun = new_def.scope;
old_def.orig.concat(old_def.references).forEach(function(node) {
node.thedef = new_def;
node.reference(options);
@@ -239,12 +243,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,23 +256,28 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
}
});
function init_scope_vars(scope, parent) {
scope.cname = -1; // the current index for mangling functions/variables
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
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
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_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
init_scope_vars(this, parent_scope);
});
AST_Lambda.DEFMETHOD("init_scope_vars", function() {
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
AST_Lambda.DEFMETHOD("init_scope_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,
}));
});
@@ -386,18 +389,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,
@@ -543,11 +538,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.orig.concat(def.references).forEach(function(sym) {
if (sym.definition() === def) sym.name = name;
});
}
});
@@ -560,22 +552,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

@@ -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.3",
"version": "3.10.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

@@ -10,7 +10,7 @@ var zlib = require("zlib");
var args = process.argv.slice(2);
if (!args.length) args.push("-mc");
args.unshift("bin/uglifyjs");
args.push("--validate", "--timings");
args.push("--timings");
var urls = [
"https://code.jquery.com/jquery-3.4.1.js",
"https://code.angularjs.org/1.7.8/angular.js",

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",

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,7 +62,7 @@ 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;
@@ -74,13 +74,14 @@ collapse_vars_side_effects_1: {
log = console.log.bind(console),
x = s.charAt(i++),
y = s.charAt(i++);
log(x, s.charAt(i++), y, 7);
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();
}
@@ -916,7 +917,7 @@ collapse_vars_lvalues_drop_assign: {
}
}
collapse_vars_misc1: {
collapse_vars_misc: {
options = {
booleans: true,
collapse_vars: true,
@@ -970,8 +971,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 +1599,7 @@ collapse_vars_constants: {
}
}
collapse_vars_arguments: {
collapse_vars_arguments_1: {
options = {
booleans: true,
collapse_vars: true,
@@ -1635,6 +1636,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 +3073,7 @@ issue_2298: {
expect: {
!function() {
(function() {
var a = undefined;
var undefined = a++;
0;
try {
!function(b) {
(void 0)[1] = "foo";
@@ -3835,20 +3907,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 +4141,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(" "));
@@ -6890,6 +6960,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 +8049,7 @@ mangleable_var: {
expect_stdout: "PASS"
}
issue_3884: {
issue_3884_1: {
options = {
collapse_vars: true,
evaluate: true,
@@ -7973,9 +8067,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"
}
@@ -8015,3 +8133,430 @@ issue_3891: {
}
expect_stdout: "function"
}
issue_3894: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a, c;
(function(b) {
a = c = 0,
log(b);
})(-0);
log(a);
log(c);
}
expect: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a, c;
void log(-(a = c = 0));
log(a);
log(c);
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
}
issue_3897: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
(function() {
function f(b) {
b = a = 1 + a;
a = 1 + a;
console.log(b);
}
f();
})();
console.log(a);
}
expect: {
var a = 0;
(function() {
function f(b) {
b = a = 1 + a;
a = 1 + a;
console.log(b);
}
f();
})();
console.log(a);
}
expect_stdout: [
"1",
"2",
]
}
issue_3908: {
options = {
collapse_vars: true,
conditionals: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
if (console) {
var o = {
p: !1
}, a = o;
}
console.log("PASS");
}
expect: {
console && 0;
console.log("PASS");
}
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

@@ -236,7 +236,7 @@ collapse_vars_lvalues_drop_assign: {
}
}
collapse_vars_misc1: {
collapse_vars_misc: {
options = {
collapse_vars: true,
dead_code: true,
@@ -1168,3 +1168,197 @@ redundant_assignments: {
}
expect_stdout: "PASS PASS"
}
self_assignments_1: {
options = {
dead_code: true,
}
input: {
var a = "PASS";
a = a;
console.log(a);
}
expect: {
var a = "PASS";
a;
console.log(a);
}
expect_stdout: "PASS"
}
self_assignments_2: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "q", o = {
p: "PASS",
};
o.p = o.p;
o[a] = o[a];
console.log(o.p, o[a]);
}
expect: {
var a = "q", o = {
p: "PASS",
};
console.log(o.p, o[a]);
}
expect_stdout: "PASS undefined"
}
self_assignments_3: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "q", o = {
p: "FAIL",
get q() {
return "PASS";
},
set q(v) {
this.p = v;
},
};
o.p = o.p;
o[a] = o[a];
console.log(o.p, o[a]);
}
expect: {
var a = "q", o = {
p: "FAIL",
get q() {
return "PASS";
},
set q(v) {
this.p = v;
},
};
o.p = o.p;
o[a] = o[a];
console.log(o.p, o[a]);
}
expect_stdout: "PASS PASS"
}
self_assignments_4: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var i = 0, l = [ "PASS" ];
l[0] = l[0];
l[i] = l[i];
console.log(l[0], i);
}
expect: {
var i = 0, l = [ "PASS" ];
console.log(l[0], i);
}
expect_stdout: "PASS 0"
}
self_assignments_5: {
options = {
dead_code: true,
evaluate: true,
passes: 3,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var i = 0, l = [ "FAIL", "PASS" ];
l[0] = l[0];
l[i] = l[i];
l[i++] = l[i++];
console.log(l[0], i);
}
expect: {
var i = 0, l = [ "FAIL", "PASS" ];
l[0];
l[0];
l[0] = l[1];
console.log(l[0], 2);
}
expect_stdout: "PASS 2"
}
self_assignments_6: {
options = {
dead_code: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var o = {
p: "PASS",
};
console.log(o.p = o.p);
}
expect: {
var o = {
p: "PASS",
};
console.log(o.p);
}
expect_stdout: "PASS"
}
issue_3967: {
options = {
dead_code: true,
}
input: {
var a = "FAIL";
try {
a = 0 in (a = "PASS");
} catch (e) {}
console.log(a);
}
expect: {
var a = "FAIL";
try {
a = 0 in (a = "PASS");
} catch (e) {}
console.log(a);
}
expect_stdout: "PASS"
}
issue_4051: {
options = {
dead_code: true,
}
input: {
try {
delete (A = A);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
delete (A = A);
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -2285,7 +2285,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);
@@ -2527,8 +2527,324 @@ issue_3802_2: {
}
expect: {
0;
function a() {};
var a = function() {};
console.log(typeof a);
}
expect_stdout: "function"
}
issue_3899: {
options = {
assignments: true,
evaluate: true,
functions: true,
inline: true,
join_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
a = a + 1;
var a = function f(b) {
return function() {
return b;
};
}(2);
console.log(typeof a);
}
expect: {
function a() {
return 2;
}
console.log(typeof a);
}
expect_stdout: "function"
}
cross_scope_assign_chain: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a, b = 0;
(function() {
a = b;
a++;
while (b++);
})();
console.log(a ? "PASS" : "FAIL");
}
expect: {
var a, b = 0;
(function() {
a = b;
a++;
while (b++);
})();
console.log(a ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
assign_if_assign_read: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
var b;
do {
b = "FAIL";
if (Array.isArray(a)) {
b = a[0];
console.log(b);
}
} while (!console);
})([ "PASS" ]);
}
expect: {
(function(a) {
var b;
do {
"FAIL";
if (Array.isArray(a)) {
b = a[0];
console.log(b);
}
} while (!console);
})([ "PASS" ]);
}
expect_stdout: "PASS"
}
issue_3951: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = console.log("PASS");
console.log(a);
a = "0";
console.log(a.p = 0);
a && a;
}
expect: {
var a = console.log("PASS");
console.log(a);
a = "0";
console.log(a.p = 0);
}
expect_stdout: [
"PASS",
"undefined",
"0",
]
}
issue_3956: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(function(a) {
function f(b) {
console.log(b);
a = 1;
}
var c = f(c += 0);
(function(d) {
console.log(d);
})(console.log(a) ^ 1, c);
})();
}
expect: {
var c, d;
c += 0,
console.log(NaN),
d = 1 ^ console.log(1),
console.log(d);
}
expect_stdout: [
"NaN",
"1",
"1",
]
}
issue_3962_1: {
options = {
evaluate: true,
keep_fargs: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
function f(b, c) {
do {
var d = console + e, e = 0..toString() === b;
} while (0);
if (c) console.log("PASS");
}
var a = f(a--, 1);
a;
}
expect: {
var a = 0;
a = (function(c) {
do {
console;
0..toString();
} while (0);
if (c) console.log("PASS");
}((a--, 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");
}((a--, 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 d, c = 0;
try {
console.log(c);
} finally {
d = c + 1;
c = 0;
}
console.log(1, 1, d);
}
expect_stdout: [
"0",
"1 1 1",
]
}

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,485 @@ issue_3887: {
expect: {
(function(b) {
try {
b-- && console.log("PASS");
1, console.log("PASS");
} catch (a_2) {}
})(1);
})();
}
expect_stdout: "PASS"
}
issue_3903: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f(b, c) {
return console, c;
}
var d = f(f(), a = a);
console.log(d);
}
expect: {
function f(b, c) {
return console, c;
}
f(f(), "PASS");
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_3905: {
options = {
evaluate: true,
passes: 2,
unused: true,
}
input: {
(function(a, a) {
return console.log(a = 0), a && console.log("FAIL");
})("foo", 42);
}
expect: {
(function(a, a) {
return console.log(a = 0), a && console.log("FAIL");
})("foo", 42);
}
expect_stdout: "0"
}
issue_3920: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function(b) {
return (b[b = 0] = 0) >= (b ? 0 : 1);
}("foo");
console.log(a);
}
expect: {
(function(b) {
"foo"[0] = 0;
})();
console.log(false);
}
expect_stdout: "false"
}
inlined_increment_prefix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0;
(function() {
++a;
})();
console.log(a += 0);
}
expect: {
var a = 0;
void ++a;
console.log(1);
}
expect_stdout: "1"
}
inlined_increment_postfix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0;
(function() {
a++;
})();
console.log(a += 0);
}
expect: {
var a = 0;
void a++;
console.log(1);
}
expect_stdout: "1"
}
compound_assignment_to_property: {
options = {
evaluate: true,
unsafe: true,
}
input: {
1 + (0..p >>= 0) && console.log("PASS");
}
expect: {
1 + (0..p >>= 0),
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_2208_assign: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect_stdout: "42"
}
issue_2208_postfix: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
a = 41;
a++;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 41;
a++;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect_stdout: "42"
}
issue_2208_prefix: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
a = 43;
--a;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 43;
--a;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect_stdout: "42"
}
issue_3933: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a, b) {
a && (b ^= 1) && console.log("PASS");
})(1);
}
expect: {
(function(a, b) {
1, 1, console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_3935: {
options = {
evaluate: true,
}
input: {
console.log(function f(a) {
return a++;
}());
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}
issue_3937: {
options = {
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a = 123;
(a++ + (b = a))[b] ? 0 ? a : b : 0 ? a : b;
console.log(a, b);
}
expect: {
var a = 123;
(a++ + (b = a))[b], 0, b;
console.log(a, b);
}
expect_stdout: "124 124"
}
issue_3944: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
function f() {
while (function() {
var a = 0 == (b && b.p), b = console.log(a);
}());
f;
}
f();
})();
}
expect: {
void function f() {
while (a = 0 == (a = void 0), console.log(a), void 0);
var a;
f;
}();
}
expect_stdout: "false"
}
issue_3953: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
function f(a) {
(a += 0 * (a = 0)) && console.log("PASS");
}
f(1);
}
expect: {
function f(a) {
(a += 0 * (a = 0)) && console.log("PASS");
}
f(1);
}
expect_stdout: "PASS"
}
issue_3988: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function f(b) {
return ("" + (b &= 0))[b && this];
}
var a = f();
console.log(a);
}
expect: {
var a = function(b) {
return ("" + (b &= 0))[b && this];
}();
console.log(a);
}
expect_stdout: "0"
}
operator_in: {
options = {
evaluate: true,
unsafe: true,
}
input: {
Object.prototype.PASS = 0;
console.log(0 in [ 1 ]);
console.log(0 in [ , ]);
console.log(0 / 0 in { NaN: 2 });
console.log("PASS" in { });
console.log("FAIL" in { });
console.log("toString" in { });
console.log("toString" in { toString: 3 });
}
expect: {
Object.prototype.PASS = 0;
console.log(true);
console.log(0 in [ , ]);
console.log(true);
console.log("PASS" in { });
console.log("FAIL" in { });
console.log("toString" in { });
console.log(true);
}
expect_stdout: [
"true",
"false",
"true",
"true",
"false",
"true",
"true",
]
}
issue_3997: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = function f(b) {
return b[b += this] = b;
}(0);
console.log(typeof a);
}
expect: {
var a = function f(b) {
return b[b += this] = b;
}(0);
console.log(typeof a);
}
expect_stdout: "string"
}
issue_4035: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var a = 0;
(function() {
var b = --a;
console.log(delete (0 + b));
console.log(delete (1 * b));
console.log(delete (b + 0));
console.log(delete (b - 0));
console.log(delete (b / 1));
})();
}
expect: {
var a = 0;
(function() {
var b = --a;
console.log((0 + b, true));
console.log((1 * b, true));
console.log((0 + b, true));
console.log((b - 0, true));
console.log((b / 1, true));
})();
}
expect_stdout: [
"true",
"true",
"true",
"true",
"true",
]
}
issue_4067: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function(a) {
(function(b) {
b[0] += 0;
console.log(+a);
})(a);
})([]);
}
expect: {
(function(a) {
(function(b) {
b[0] += 0;
console.log(+a);
})(a);
})([]);
}
expect_stdout: "NaN"
}
issue_4077: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log((a = []) - (a[0]++, 1) || "PASS");
}
expect: {
console.log((a = []) - (a[0]++, 1) || "PASS");
}
expect_stdout: "PASS"
}

View File

@@ -2336,6 +2336,7 @@ issue_3274: {
inline: true,
join_vars: true,
loops: true,
passes: 2,
reduce_vars: true,
unused: true,
}
@@ -4680,3 +4681,100 @@ issue_3852: {
}
expect_stdout: "PASS"
}
issue_3911: {
options = {
collapse_vars: true,
conditionals: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return function() {
if (a) (a++, b += a);
f();
};
}
var a = f, b;
console.log("PASS");
}
expect: {
console.log("PASS");
}
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"
}

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";
})();
})();
@@ -2489,3 +2489,228 @@ issue_3889: {
}
expect_stdout: "undefined"
}
issue_3918: {
options = {
conditionals: true,
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
}
input: {
if (console.log("PASS")) {
var a = function f() {
f.p;
try {
console.log("FAIL");
} catch (e) {}
}, b = a;
}
}
expect: {
var a;
console.log("PASS") && (a = function f() {
f.p;
try {
console.log("FAIL");
} catch (o) {}
}, a);
}
expect_stdout: "PASS"
}
issue_3999: {
rename = true
mangle = {
ie8: true,
}
input: {
(function() {
(function f() {
for (var i = 0; i < 2; i++)
try {
f[0];
} catch (f) {
var f = 0;
console.log(i);
}
})();
})(typeof f);
}
expect: {
(function() {
(function f() {
for (var o = 0; o < 2; o++)
try {
f[0];
} catch (f) {
var f = 0;
console.log(o);
}
})();
})(typeof f);
}
expect_stdout: [
"0",
"1",
]
}
issue_4001: {
options = {
collapse_vars: true,
ie8: true,
inline: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
console.log(function(a) {
function f() {
return a;
var b;
}
var c = f();
(function g() {
c[42];
f;
})();
(function a() {});
}(42));
}
expect: {
function f() {
return a;
}
var a;
console.log((a = 42, void f()[42], void function a() {}));
}
expect_stdout: "undefined"
}
issue_4015: {
rename = true
mangle = {
ie8: true,
toplevel: true,
}
input: {
var n, a = 0, b;
function f() {
try {
throw 0;
} catch (b) {
(function g() {
(function b() {
a++;
})();
})();
}
}
f();
console.log(a);
}
expect: {
var n, o = 0, c;
function t() {
try {
throw 0;
} catch (c) {
(function n() {
(function c() {
o++;
})();
})();
}
}
t();
console.log(o);
}
expect_stdout: "1"
}
issue_4019: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = function() {
try {
console.log("FAIL");
} catch (b) {}
}, a = (console.log(a.length), ++a);
}
expect: {
var o = function() {
try {
console.log("FAIL");
} catch (o) {}
}, o = (console.log(o.length), ++o);
}
expect_stdout: "0"
}
issue_4028: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
ie8: true,
}
input: {
function a() {
try {
A;
} catch (e) {}
}
var b = a += a;
console.log(typeof b);
}
expect: {
function a() {
try {
A;
} catch (a) {}
}
var b = a += a;
console.log(typeof b);
}
expect_stdout: "string"
}
issue_2737: {
options = {
ie8: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
a();
})(function f() {
console.log(typeof f);
});
}
expect: {
(function(a) {
a();
})(function f() {
console.log(typeof f);
});
}
expect_stdout: "function"
}

View File

@@ -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"
@@ -1023,3 +1023,35 @@ issue_3856: {
}
expect_stdout: "undefined"
}
issue_3916: {
options = {
join_vars: true,
}
input: {
var o = {};
o.p = "PASS";
o.__proto__ = 42;
o.q = "FAIL";
o.__proto__ = {
p: "FAIL",
q: "PASS",
};
o.__proto__ = "foo";
console.log(typeof o.__proto__, o.p, delete o.q, o.q);
}
expect: {
var o = {
p: "PASS",
__proto__: 42,
q: "FAIL",
};
o.__proto__ = {
p: "FAIL",
q: "PASS",
};
o.__proto__ = "foo";
console.log(typeof o.__proto__, o.p, delete o.q, o.q);
}
expect_stdout: "object PASS true PASS"
}

View File

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

View File

@@ -756,7 +756,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 +963,49 @@ 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"
}

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: {

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

@@ -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)());
}
}
}
@@ -2034,6 +2031,7 @@ issue_1670_4: {
issue_1670_5: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
keep_fargs: false,
@@ -2065,6 +2063,7 @@ issue_1670_5: {
issue_1670_6: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
keep_fargs: false,
@@ -2372,8 +2371,7 @@ delay_def: {
return;
}
function g() {
return a;
var a = 1;
return;
}
console.log(f(), g());
}
@@ -2395,7 +2393,7 @@ delay_def_lhs: {
expect: {
console.log(function() {
long_name++;
return long_name;
return NaN;
var long_name;
}());
}
@@ -2651,11 +2649,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 +2672,8 @@ var_assign_6: {
}
expect: {
!function() {
var a = function(){}(a = 1);
console.log(a);
(function(){}());
console.log(void 0);
}();
}
expect_stdout: "undefined"
@@ -2724,8 +2720,8 @@ issue_1814_1: {
expect: {
!function() {
!function(a) {
console.log(a++, 42);
}(0);
console.log(0, 42);
}();
}();
}
expect_stdout: "0 42"
@@ -2751,8 +2747,8 @@ issue_1814_2: {
expect: {
!function() {
!function(a) {
console.log("321", a++);
}(0);
console.log("321", 0);
}();
}();
}
expect_stdout: "321 0"
@@ -4725,7 +4721,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 +4759,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 +4804,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 +4841,7 @@ escape_local_conditional: {
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("PASS");
else
console.log("FAIL");
@@ -4882,7 +4878,7 @@ escape_local_sequence: {
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("PASS");
else
console.log("FAIL");
@@ -4926,7 +4922,7 @@ escape_local_throw: {
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
if (thing !== baz())
console.log("PASS");
else
console.log("FAIL");
@@ -4980,11 +4976,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 +5172,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"
}
@@ -7068,3 +7058,373 @@ issue_3880: {
}
expect_stdout: "PASS"
}
issue_3894: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a;
(function(b) {
a = 0,
log(b);
})(-0);
}
expect: {
function log(msg) {
console.log(msg ? "FAIL" : "PASS");
}
var a;
void log(-(a = 0));
}
expect_stdout: "PASS"
}
issue_3922: {
options = {
evaluate: true,
keep_fargs: "strict",
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function(a) {
var b;
b && b[c];
a |= this;
console.log("PASS");
var c = a.undefined;
})();
}
expect: {
(function() {
0;
console.log("PASS");
})();
}
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"
}

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;
}
}
}
@@ -275,3 +274,120 @@ 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",
]
}

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

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

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

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

@@ -296,4 +296,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

@@ -4,19 +4,17 @@ var List = U.List;
var os = require("os");
var sandbox = require("./sandbox");
// Reduce a ufuzz-style `console.log` based test case by iteratively replacing
// AST nodes with various permutations. Each AST_Statement in the tree is also
// speculatively dropped to determine whether it is needed. If the altered
// tree and the last known good tree produce the same non-nil error-free output
// after being run, then the permutation survives to the next generation and
// is the basis for subsequent iterations. The test case is reduced as a
// consequence of complex expressions being replaced with simpler ones.
// This function assumes that the testcase will not result in a parse or
// runtime Error. Note that a reduced test case will have different runtime
// output - it is not functionally equivalent to the original. The only criteria
// is that once the generated reduced test case is run without minification, it
// will produce different output from the code minified with `minify_options`.
// Returns a `minify` result object with an additonal boolean property `reduced`.
// Reduce a test case by iteratively replacing AST nodes with various
// permutations. Each AST_Statement in the tree is also speculatively dropped
// to determine whether it is needed. If the altered tree and the last known
// good tree produce the same output after being run, then the permutation
// survives to the next generation and is the basis for subsequent iterations.
// The test case is reduced as a consequence of complex expressions being
// replaced with simpler ones. Note that a reduced test case will have
// different runtime output - it is not functionally equivalent to the
// original. The only criteria is that once the generated reduced test case is
// run without minification, it will produce different output from the code
// minified with `minify_options`. Returns a `minify` result object.
Error.stackTraceLimit = Infinity;
module.exports = function reduce_test(testcase, minify_options, reduce_options) {
@@ -440,7 +438,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 {

View File

@@ -9,6 +9,6 @@ require("./run")([
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
args.unshift("test/benchmark.js", "--validate");
return args;
}));

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;

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

@@ -0,0 +1,49 @@
require("../../tools/exit");
var get = require("https").get;
var parse = require("url").parse;
var base = process.argv[2];
var token = process.argv[3];
function read(url, callback) {
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() {
callback(JSON.parse(chunks.join("")));
});
});
}
var queued = 0, total = 0, earliest, now = Date.now();
process.on("beforeExit", function() {
if (queued > 3) {
process.stdout.write("0");
} else if (now - earliest > 0 && total > 1) {
process.stdout.write(Math.min(20 * (now - earliest) / (total - 1), 6300000).toFixed(0));
} else {
process.stdout.write("3600000");
}
});
read(base + "/actions/workflows/ufuzz.yml/runs?event=schedule", function(reply) {
reply.workflow_runs.filter(function(workflow) {
return /^(in_progress|queued|)$/.test(workflow.status);
}).forEach(function(workflow) {
read(workflow.jobs_url, function(reply) {
reply.jobs.forEach(function(job) {
if (job.status == "queued") queued++;
total++;
if (!job.started_at) return;
var start = new Date(job.started_at);
if (!(earliest < start)) earliest = start;
});
});
});
});

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 = [
"=",
@@ -490,11 +496,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();
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:
@@ -762,6 +773,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) + "]";
@@ -879,7 +894,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
@@ -929,9 +944,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() {
@@ -1018,6 +1036,7 @@ function log_suspects(minify_options, component) {
if (!options) return;
if (typeof options != "object") options = {};
var defs = default_options[component];
var toplevel = sandbox.has_toplevel(minify_options);
var suspects = Object.keys(defs).filter(function(name) {
var flip = name == "keep_fargs";
if (flip !== (name in options ? options : defs)[name]) {
@@ -1025,12 +1044,15 @@ function log_suspects(minify_options, component) {
var o = JSON.parse(JSON.stringify(options));
o[name] = flip;
m[component] = o;
m.validate = true;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
if (typeof uglify_code != "string") {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component + "." + name);
errorln(result.error);
} else {
var r = sandbox.run_code(result.code, sandbox.has_toplevel(m));
var r = sandbox.run_code(result.code, toplevel);
return !sandbox.same_stdout(uglify_result, r);
}
}
@@ -1044,18 +1066,21 @@ function log_suspects(minify_options, component) {
}
}
function log_suspects_global(options) {
function log_suspects_global(options, toplevel) {
var suspects = Object.keys(default_options).filter(function(component) {
return typeof default_options[component] != "object";
}).filter(function(component) {
var m = JSON.parse(options);
m[component] = false;
m.validate = true;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
if (typeof uglify_code != "string") {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component);
errorln(result.error);
} else {
var r = sandbox.run_code(result.code, sandbox.has_toplevel(m));
var r = sandbox.run_code(result.code, toplevel);
return !sandbox.same_stdout(uglify_result, r);
}
});
@@ -1074,7 +1099,37 @@ function log(options) {
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
try_beautify(original_code, toplevel, original_result, errorln);
var beautified = UglifyJS.minify(original_code, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
},
});
if (beautified.error) {
errorln("// !!! beautify failed !!!");
errorln(beautified.error);
errorln("//");
errorln(original_code);
} else {
var uglified = UglifyJS.minify(beautified.code, JSON.parse(options));
var expected, actual;
if (typeof uglify_code != "string" || uglified.error) {
expected = uglify_code;
actual = uglified.error;
} else {
expected = uglify_result;
actual = sandbox.run_code(uglified.code, toplevel);
}
if (sandbox.same_stdout(expected, actual)) {
errorln("// (beautified)");
errorln(beautified.code);
} else {
errorln("//");
errorln(original_code);
}
}
errorln();
errorln();
errorln("//-------------------------------------------------------------");
@@ -1087,18 +1142,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);
@@ -1109,19 +1152,38 @@ 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();
if (!ok && typeof uglify_code == "string") {
if (!ok) {
Object.keys(default_options).filter(function(component) {
var defs = default_options[component];
return defs && typeof defs == "object";
}).forEach(log_suspects.bind(null, JSON.parse(options)));
log_suspects_global(options);
log_suspects_global(options, toplevel);
errorln("!!!!!! Failed... round " + round);
}
}
function sort_globals(code) {
var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
return globals.length ? "var " + globals.join(",") + ";" + code : code;
}
function fuzzy_match(original, uglified) {
uglified = uglified.split(" ");
var i = uglified.length;
@@ -1136,34 +1198,63 @@ function fuzzy_match(original, uglified) {
return true;
}
function skip_infinite_recursion(orig, toplevel) {
var code = orig;
var tries = [];
var offset = 0;
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;
}
if (tries.length && tries[0].catch) tries.shift();
var index = match.index + match[0].length + 1;
if (/(?:^|[\s{};])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset });
continue;
}
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);
function patch_try_catch(orig, toplevel) {
var stack = [ {
code: orig,
index: 0,
offset: 0,
tries: [],
} ];
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g;
while (stack.length) {
var code = stack[0].code;
var offset = stack[0].offset;
var tries = stack[0].tries;
var match;
re.lastIndex = stack.shift().index;
while (match = re.exec(code)) {
var index = match.index + match[0].length + 1;
if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset });
continue;
}
var insert;
if (/}\s*finally\s*$/.test(match[0])) {
tries.shift();
insert = 'if (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;';
} else {
while (tries.length && tries[0].catch) tries.shift();
tries[0].catch = index - offset;
insert = [
"if (!" + match[1] + ".ufuzz_var) {",
match[1] + '.ufuzz_var = "' + match[1] + '";',
match[1] + ".ufuzz_try = " + tries[0].try + ";",
match[1] + ".ufuzz_catch = " + tries[0].catch + ";",
"UFUZZ_ERROR = " + match[1] + ";",
"}",
"throw " + match[1] + ";",
].join("\n");
}
var new_code = code.slice(0, index) + insert + code.slice(index);
var result = sandbox.run_code(new_code, toplevel);
if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") {
if (!stack.filled && match[1]) stack.push({
code: code,
index: index && index - 1,
offset: offset,
tries: JSON.parse(JSON.stringify(tries)),
});
offset += insert.length;
code = new_code;
} else if (result.name == "TypeError" && /'in'/.test(result.message)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("invalid `in`");' + orig.slice(index);
} else if (result.name == "RangeError" && result.message == "Maximum call stack size exceeded") {
index = result.ufuzz_try;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
}
}
stack.filled = true;
}
}
@@ -1172,14 +1263,6 @@ var fallback_options = [ JSON.stringify({
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++) {
@@ -1201,7 +1284,7 @@ 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) {
@@ -1211,10 +1294,11 @@ for (var round = 1; round <= num_iterations; round++) {
ok = sandbox.same_stdout(fuzzy_result, uglify_result);
}
}
// ignore difference in error message caused by `in`
// ignore difference in depth of termination caused by infinite recursion
if (!ok) {
var orig_skipped = 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));
}

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>