Compare commits

...

93 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
49 changed files with 6347 additions and 1914 deletions

View File

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

View File

@@ -2,7 +2,11 @@ name: Fuzzing
on: on:
pull_request: pull_request:
schedule: schedule:
- cron: "*/8 * * * *" - cron: "*/5 * * * *"
env:
BASE_URL: https://api.github.com/repos/${{ github.repository }}
CAUSE: ${{ github.event_name }}
TOKEN: ${{ github.token }}
jobs: jobs:
ufuzz: ufuzz:
strategy: strategy:
@@ -17,13 +21,13 @@ jobs:
shell: bash shell: bash
run: | run: |
git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs git clone --branch v1.5.4 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add 10 && nvs use 10'; do while ! timeout 60 bash -c '. ~/.nvs/nvs.sh add 8 && nvs use 8'; do
cd ~/.nvs cd ~/.nvs
while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done while !(git clean -xdf); do echo "'git clean' failed - retrying..."; done
cd - cd -
done done
. ~/.nvs/nvs.sh --version . ~/.nvs/nvs.sh --version
nvs use 10 nvs use 8
node --version node --version
npm config set audit false npm config set audit false
npm config set optional false npm config set optional false
@@ -32,4 +36,12 @@ jobs:
npm config set update-notifier false npm config set update-notifier false
npm --version npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done 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,13 +212,12 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported: (comma-separated) options are supported:
- `toplevel` (default `false`) -- mangle names declared in the top level scope. - `eval` (default `false`) -- mangle names visible in scopes where `eval` or
`with` are used.
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used. - `reserved` (default: `[]`) -- when mangling is enabled but you want to
prevent certain names from being mangled, you can declare those names with
When mangling is enabled but you want to prevent certain names from being `--mangle reserved` — pass a comma-separated list of names. For example:
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']
@@ -657,8 +656,8 @@ to be `false` and all symbol names will be omitted.
- `hoist_props` (default: `true`) -- hoist properties from constant object and - `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example: array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props` `var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher, works best with `toplevel` and `mangle` enabled, alongside with `compress` option
and the `compress` option `toplevel` enabled. `passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false` - `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general) by default because it seems to increase the size of the output in general)
@@ -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 properties can be added, removed and modified (not prevented with
`Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`, `Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`Object.preventExtensions()` or `Object.seal()`). `Object.preventExtensions()` or `Object.seal()`).
- When `toplevel` is enabled, UglifyJS effectively assumes input code is wrapped
within `function(){ ... }`, thus forbids aliasing of declared global variables:
```js
A = "FAIL";
var B = "FAIL";
// can be `global`, `self`, `window` etc.
var top = function() {
return this;
}();
// "PASS"
top.A = "PASS";
console.log(A);
// "FAIL" after compress and/or mangle
top.B = "PASS";
console.log(B);
```

View File

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

View File

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

View File

@@ -120,6 +120,20 @@ var AST_Node = DEFNODE("Node", "start end", {
ctor.prototype._validate.call(this); ctor.prototype._validate.call(this);
} while (ctor = ctor.BASE); } 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); }, null);
(AST_Node.log_function = function(fn, verbose) { (AST_Node.log_function = function(fn, verbose) {
@@ -274,10 +288,13 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
var label = node.label; var label = node.label;
var def = this.label; var def = this.label;
node.walk(new TreeWalker(function(node) { 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; node.label.thedef = label;
label.references.push(node); label.references.push(node);
return true;
} }
if (node instanceof AST_Scope) return true;
})); }));
} }
return node; return node;
@@ -395,16 +412,16 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */ /* -----[ 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", $documentation: "Base class for all statements introducing a lexical scope",
$propdoc: { $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)", 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) { clone: function(deep) {
var node = this._clone(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); toplevel = toplevel[action](option);
files[toplevel.start.file] = toplevel.print_to_string().replace(orig, ""); files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
}); });
if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now(); if (timings) timings.rename = Date.now();
if (options.rename) { if (options.rename) {
toplevel.figure_out_scope(options.mangle); toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle); toplevel.expand_names(options.mangle);
} }
if (timings) timings.compress = Date.now(); if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); if (options.compress) {
toplevel = new Compressor(options.compress).compress(toplevel);
if (options.validate) toplevel.validate_ast();
}
if (timings) timings.scope = Date.now(); if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle); if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now(); if (timings) timings.mangle = Date.now();
@@ -193,9 +197,7 @@ function minify(files, options) {
toplevel.mangle_names(options.mangle); toplevel.mangle_names(options.mangle);
} }
if (timings) timings.properties = Date.now(); if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) { if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now(); if (timings) timings.output = Date.now();
var result = {}; var result = {};
if (options.output.ast) { if (options.output.ast) {

View File

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

View File

@@ -1092,7 +1092,7 @@ function parse($TEXT, options) {
function switch_body_() { function switch_body_() {
expect("{"); expect("{");
var a = [], cur = null, branch = null, tmp; var a = [], branch, cur, default_branch, tmp;
while (!is("punc", "}")) { while (!is("punc", "}")) {
if (is("eof")) expect_token("punc", "}"); if (is("eof")) expect_token("punc", "}");
if (is("keyword", "case")) { if (is("keyword", "case")) {
@@ -1107,12 +1107,14 @@ function parse($TEXT, options) {
expect(":"); expect(":");
} else if (is("keyword", "default")) { } else if (is("keyword", "default")) {
if (branch) branch.end = prev(); if (branch) branch.end = prev();
if (default_branch) croak("More than one default clause in switch statement");
cur = []; cur = [];
branch = new AST_Default({ branch = new AST_Default({
start : (tmp = S.token, next(), expect(":"), tmp), start : (tmp = S.token, next(), expect(":"), tmp),
body : cur body : cur
}); });
a.push(branch); a.push(branch);
default_branch = branch;
} else { } else {
if (!cur) unexpected(); if (!cur) unexpected();
cur.push(statement()); cur.push(statement());

View File

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

View File

@@ -96,8 +96,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
var self = this; var self = this;
var scope = self.parent_scope = null;
var defun = null; var defun = null;
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Catch) { if (node instanceof AST_Catch) {
var save_scope = scope; var save_scope = scope;
@@ -149,7 +150,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
scope.def_variable(node).defun = defun; 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); self.walk(tw);
// pass 2: find back references and eval // pass 2: find back references and eval
@@ -230,6 +233,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
} else { } else {
new_def = scope.def_variable(node); new_def = scope.def_variable(node);
} }
old_def.defun = new_def.scope;
old_def.orig.concat(old_def.references).forEach(function(node) { old_def.orig.concat(old_def.references).forEach(function(node) {
node.thedef = new_def; node.thedef = new_def;
node.reference(options); 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) { AST_Toplevel.DEFMETHOD("def_global", function(node) {
var globals = this.globals, name = node.name; var globals = this.globals, name = node.name;
if (globals.has(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) { 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) init_scope_vars(this, parent_scope);
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
this.uses_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
}); });
AST_Lambda.DEFMETHOD("init_scope_vars", function() { AST_Lambda.DEFMETHOD("init_scope_vars", function(parent_scope) {
AST_Scope.prototype.init_scope_vars.apply(this, arguments); init_scope_vars(this, parent_scope);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({ this.def_variable(new AST_SymbolFunarg({
name: "arguments", name: "arguments",
start: this.start, start: this.start,
end: this.end end: this.end,
})); }));
}); });
@@ -386,18 +389,10 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options) {
// labels are always mangleable // labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", return_false); AST_Label.DEFMETHOD("unmangleable", return_false);
AST_Symbol.DEFMETHOD("unreferenced", function() {
return !this.definition().references.length && !this.scope.pinned();
});
AST_Symbol.DEFMETHOD("definition", function() { AST_Symbol.DEFMETHOD("definition", function() {
return this.thedef; return this.thedef;
}); });
AST_Symbol.DEFMETHOD("global", function() {
return this.definition().global;
});
function _default_mangler_options(options) { function _default_mangler_options(options) {
options = defaults(options, { options = defaults(options, {
eval : false, eval : false,
@@ -543,11 +538,8 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var redef = def.redefined(); var redef = def.redefined();
var name = redef ? redef.rename || redef.name : next_name(); var name = redef ? redef.rename || redef.name : next_name();
def.rename = name; def.rename = name;
def.orig.forEach(function(sym) { def.orig.concat(def.references).forEach(function(sym) {
sym.name = name; if (sym.definition() === def) sym.name = name;
});
def.references.forEach(function(sym) {
sym.name = name;
}); });
} }
}); });
@@ -560,22 +552,24 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
options = _default_mangler_options(options); options = _default_mangler_options(options);
base54.reset(); base54.reset();
var fn = AST_Symbol.prototype.add_source_map;
try { try {
AST_Node.prototype.print = function(stream, force_parens) { AST_Symbol.prototype.add_source_map = function() {
this._print(stream, force_parens); if (!this.unmangleable(options)) base54.consider(this.name, -1);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
}; };
if (options.properties) {
AST_Dot.prototype.add_source_map = function() {
base54.consider(this.property, -1);
};
AST_Sub.prototype.add_source_map = function() {
skip_string(this.property);
};
}
base54.consider(this.print_to_string(), 1); base54.consider(this.print_to_string(), 1);
} finally { } finally {
AST_Node.prototype.print = AST_Node.prototype._print; AST_Symbol.prototype.add_source_map = fn;
delete AST_Dot.prototype.add_source_map;
delete AST_Sub.prototype.add_source_map;
} }
base54.sort(); base54.sort();

View File

@@ -112,51 +112,29 @@ function return_this() { return this; }
function return_null() { return null; } function return_null() { return null; }
var List = (function() { var List = (function() {
function List(a, f, backwards) { function List(a, f) {
var ret = [], top = [], i; var ret = [];
function doit() { for (var i = 0; i < a.length; i++) {
var val = f(a[i], i); var val = f(a[i], i);
var is_last = val instanceof Last; if (val === skip) continue;
if (is_last) val = val.v;
if (val instanceof AtTop) {
val = val.v;
if (val instanceof Splice) { if (val instanceof Splice) {
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); ret.push.apply(ret, 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 { } else {
ret.push(val); ret.push(val);
} }
} }
return is_last; return ret;
}
if (Array.isArray(a)) {
if (backwards) {
for (i = a.length; --i >= 0;) if (doit()) break;
ret.reverse();
top.reverse();
} else {
for (i = 0; i < a.length; ++i) if (doit()) break;
}
} else {
for (i in a) if (HOP(a, i)) if (doit()) break;
}
return top.concat(ret);
} }
List.is_op = function(val) { 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 = {}; var skip = List.skip = {};
function AtTop(val) { this.v = val; } function Splice(val) {
function Splice(val) { this.v = val; } this.v = val;
function Last(val) { this.v = val; } }
return List; return List;
})(); })();

View File

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

View File

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

View File

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

View File

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

View File

@@ -62,7 +62,7 @@ collapse_vars_side_effects_1: {
expect: { expect: {
function f1() { function f1() {
var s = "abcdef", i = 2; 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() { function f2() {
var s = "abcdef", i = 2; var s = "abcdef", i = 2;
@@ -74,13 +74,14 @@ collapse_vars_side_effects_1: {
log = console.log.bind(console), log = console.log.bind(console),
x = s.charAt(i++), x = s.charAt(i++),
y = s.charAt(i++); y = s.charAt(i++);
log(x, s.charAt(i++), y, 7); log(x, s.charAt(4), y, 7);
} }
function f4() { function f4() {
var i = 10, var i = 10;
x = i += 2, i += 2,
y = i += 3; i += 3,
console.log.bind(console)(x, i += 4, y, 19); i += 4;
console.log.bind(console)(12, 19, 15, 19);
} }
f1(), f2(), f3(), f4(); f1(), f2(), f3(), f4();
} }
@@ -916,7 +917,7 @@ collapse_vars_lvalues_drop_assign: {
} }
} }
collapse_vars_misc1: { collapse_vars_misc: {
options = { options = {
booleans: true, booleans: true,
collapse_vars: true, collapse_vars: true,
@@ -970,8 +971,8 @@ collapse_vars_misc1: {
function f7() { var b = window.a * window.z; return b + b } function f7() { var b = window.a * window.z; return b + b }
function f8() { var b = window.a * window.z; return b + (5 + 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 f9() { var b = window.a * window.z; return bar() || b }
function f10(x) { var a = 5; return a += 3; } function f10(x) { return 8; }
function f11(x) { var a = 5; return a += 2; } function f11(x) { return 7; }
} }
} }
@@ -1598,7 +1599,7 @@ collapse_vars_constants: {
} }
} }
collapse_vars_arguments: { collapse_vars_arguments_1: {
options = { options = {
booleans: true, booleans: true,
collapse_vars: true, collapse_vars: true,
@@ -1635,6 +1636,78 @@ collapse_vars_arguments: {
expect_stdout: true 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: { collapse_vars_short_circuit: {
options = { options = {
booleans: true, booleans: true,
@@ -3000,8 +3073,7 @@ issue_2298: {
expect: { expect: {
!function() { !function() {
(function() { (function() {
var a = undefined; 0;
var undefined = a++;
try { try {
!function(b) { !function(b) {
(void 0)[1] = "foo"; (void 0)[1] = "foo";
@@ -3835,20 +3907,19 @@ issue_2436_3: {
}(o)); }(o));
} }
expect: { expect: {
var o = {
a: 1,
b: 2,
};
console.log(function(c) { console.log(function(c) {
o = { ({
a: 3, a: 3,
b: 4, b: 4,
}; });
return { return {
x: c.a, x: c.a,
y: c.b, y: c.b,
}; };
}(o)); }({
a: 1,
b: 2,
}));
} }
expect_stdout: true expect_stdout: true
} }
@@ -4070,16 +4141,15 @@ issue_2436_10: {
}(o).join(" ")); }(o).join(" "));
} }
expect: { expect: {
var o = {
a: 1,
b: 2,
};
function f(n) { function f(n) {
o = { b: 3 }; ({ b: 3 });
return n; return n;
} }
console.log([ console.log([
(c = o).a, (c = {
a: 1,
b: 2,
}).a,
f(c.b), f(c.b),
c.b, c.b,
].join(" ")); ].join(" "));
@@ -6890,6 +6960,30 @@ sequence_in_iife_2: {
} }
expect: { expect: {
var a = "foo", b = 42; 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); console.log(a, a);
} }
expect_stdout: "foo foo" expect_stdout: "foo foo"
@@ -7955,7 +8049,7 @@ mangleable_var: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3884: { issue_3884_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true, evaluate: true,
@@ -7973,9 +8067,33 @@ issue_3884: {
console.log(a, b); console.log(a, b);
} }
expect: { expect: {
var a = 100; var a = 100, b = 1;
++a; b <<= ++a;
console.log(a, 32); 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" expect_stdout: "101 32"
} }
@@ -8108,3 +8226,337 @@ issue_3908: {
} }
expect_stdout: "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: { issue_2857_1: {
options = { options = {
comparisons: true, comparisons: true,

View File

@@ -236,7 +236,7 @@ collapse_vars_lvalues_drop_assign: {
} }
} }
collapse_vars_misc1: { collapse_vars_misc: {
options = { options = {
collapse_vars: true, collapse_vars: true,
dead_code: true, dead_code: true,
@@ -1169,27 +1169,196 @@ redundant_assignments: {
expect_stdout: "PASS PASS" expect_stdout: "PASS PASS"
} }
self_assignments: { self_assignments_1: {
options = { options = {
dead_code: true, dead_code: true,
} }
input: { input: {
var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" }; var a = "PASS";
a = a; a = a;
l[0] = l[0]; console.log(a);
l[b] = l[b];
l[b++] = l[b++];
o.p = o.p;
console.log(a, b, l[0], o.p);
} }
expect: { expect: {
var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" }; var a = "PASS";
a; 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[b]; l[0];
l[b++] = l[b++]; l[0] = l[1];
o.p; console.log(l[0], 2);
console.log(a, b, l[0], o.p);
} }
expect_stdout: "PASS 2 PASS PASS" 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 { try {
(function() { (function() {
a = "PASS"; a = "PASS";
var c = (void (c.p = 0))[!1]; (void ((void 0).p = 0))[!1];
})(); })();
} catch (e) {} } catch (e) {}
console.log(a); console.log(a);
@@ -2557,11 +2557,294 @@ issue_3899: {
console.log(typeof a); console.log(typeof a);
} }
expect: { expect: {
++a; function a() {
var a = function() {
return 2; return 2;
}; }
console.log(typeof a); console.log(typeof a);
} }
expect_stdout: "function" 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, unsafe: true,
} }
input: { input: {
console.log( var a = "PASS";
[1, , 3][1], Array.prototype[1] = a;
[1, 2, 3, a] + 1, console.log([, ].length);
[1, 2, 3, 4] + 1, console.log("" + [, , ]);
[1, 2, 3, a][0] + 1, console.log([1, , 3][1]);
[1, 2, 3, 4][0] + 1, console.log([1, 2, 3, a] + 1);
[1, 2, 3, 4][6 - 5] + 1, console.log([1, 2, 3, 4] + 1);
[1, , 3, 4][6 - 5] + 1, console.log([1, 2, 3, a][0] + 1);
[[1, 2], [3, 4]][0] + 1, console.log([1, 2, 3, 4][0] + 1);
[[1, 2], [3, 4]][6 - 5][1] + 1, console.log([1, 2, 3, 4][6 - 5] + 1);
[[1, 2], , [3, 4]][6 - 5][1] + 1 console.log([1, , 3, 4][6 - 5] + 1);
); console.log([[1, 2], [3, 4]][0] + 1);
console.log([[1, 2], [3, 4]][6 - 5][1] + 1);
console.log([[1, 2], , [3, 4]][6 - 5][1] + 1);
} }
expect: { expect: {
console.log( var a = "PASS";
void 0, Array.prototype[1] = a;
[1, 2, 3, a] + 1, console.log([, ].length);
"1,2,3,41", console.log("" + [, , ]);
[1, 2, 3, a][0] + 1, console.log([1, , 3][1]);
2, console.log([1, 2, 3, a] + 1);
3, console.log("1,2,3,41");
NaN, console.log([1, 2, 3, a][0] + 1);
"1,21", console.log(2);
5, console.log(3);
(void 0)[1] + 1 console.log([1, , 3, 4][1] + 1);
); console.log("1,21");
console.log(5);
console.log([[1, 2], , [3, 4]][1][1] + 1);
} }
expect_stdout: true expect_stdout: [
"1",
",PASS",
"PASS",
"1,2,3,PASS1",
"1,2,3,41",
"2",
"2",
"3",
"PASS1",
"1,21",
"5",
"A1",
]
} }
unsafe_string: { unsafe_string: {
@@ -1579,9 +1596,9 @@ issue_2968_1: {
expect: { expect: {
var c = "FAIL"; var c = "FAIL";
(function() { (function() {
b = -(a = 42), a = 42,
void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0)); void ((a <<= 0) && (a[(c = "PASS", 0)] = 0));
var a, b; var a;
})(); })();
console.log(c); console.log(c);
} }
@@ -2341,10 +2358,7 @@ issue_3878_1: {
console.log(b ? "PASS" : "FAIL"); console.log(b ? "PASS" : "FAIL");
} }
expect: { expect: {
var b = function(a) { console.log(true ? "PASS" : "FAIL");
return (a = 0) == (a && this > (a += 0));
}();
console.log(b ? "PASS" : "FAIL");
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
@@ -2412,9 +2426,9 @@ issue_3887: {
expect: { expect: {
(function(b) { (function(b) {
try { try {
b-- && console.log("PASS"); 1, console.log("PASS");
} catch (a_2) {} } catch (a_2) {}
})(1); })();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
@@ -2435,12 +2449,11 @@ issue_3903: {
console.log(d); console.log(d);
} }
expect: { expect: {
var a = "PASS";
function f(b, c) { function f(b, c) {
return console, c; return console, c;
} }
var d = f(f(), a = a); f(f(), "PASS");
console.log(d); console.log("PASS");
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
@@ -2478,10 +2491,420 @@ issue_3920: {
console.log(a); console.log(a);
} }
expect: { expect: {
var a = function(b) { (function(b) {
return (b[b = 0] = 0) >= (b ? 0 : 1); "foo"[0] = 0;
}("foo"); })();
console.log(a); console.log(false);
} }
expect_stdout: "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, inline: true,
join_vars: true, join_vars: true,
loops: true, loops: true,
passes: 2,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -4705,3 +4706,75 @@ issue_3911: {
} }
expect_stdout: "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: { keyword: {
options = { options = {
global_defs: { global_defs: {

View File

@@ -972,3 +972,72 @@ issue_3871: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3945_1: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
function f() {
o.p;
var o = {
q: 0,
};
}
}
expect: {
function f() {
o.p;
var o = {
q: 0,
};
}
}
}
issue_3945_2: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
console.log(typeof o);
var o = {
p: 0,
};
}
expect: {
console.log(typeof o);
var o = {
p: 0,
};
}
expect_stdout: "undefined"
}
issue_4023: {
options = {
comparisons: true,
hoist_props: true,
inline: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function f() {
var a = function() {
return { p: 0 };
}();
return console.log("undefined" != typeof a);
}
f();
}
expect: {
console.log(void 0 !== {});
}
expect_stdout: "true"
}

View File

@@ -2223,13 +2223,13 @@ issue_3523_rename_ie8: {
expect: { expect: {
var a = 0, b, c = "FAIL"; var a = 0, b, c = "FAIL";
(function() { (function() {
var c, n, t, o, a, r, f, i, u, e, h, l, v, y; var c, n, t, o, a, r, e, f, i, u, h, l, v, y;
})(); })();
try { try {
throw 0; throw 0;
} catch (e) { } catch (e) {
(function() { (function() {
(function n() { (function e() {
c = "PASS"; c = "PASS";
})(); })();
})(); })();
@@ -2522,3 +2522,195 @@ issue_3918: {
} }
expect_stdout: "PASS" 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: { expect: {
var a = "FAIL", d = function() { var a = "FAIL", d = function() {
if (a = 42, d) return -1; if (void 0) return -1;
a = "PASS"; a = "PASS";
}(); }(a = 42);
console.log(a, d); console.log(a, d);
} }
expect_stdout: "PASS undefined" expect_stdout: "PASS undefined"

View File

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

View File

@@ -756,7 +756,37 @@ empty_for_in_side_effects: {
expect_warnings: [ expect_warnings: [
"WARN: Dropping unused variable b [test/compress/loops.js:4,16]", "WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
"INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]", "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
"WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]", "WARN: Side effects in object of for-in loop [test/compress/loops.js:2,17]",
]
}
empty_for_in_prop_init: {
options = {
loops: true,
pure_getters: "strict",
unused: true,
}
input: {
console.log(function f() {
var a = "bar";
for ((a, f)[a] in console.log("foo"));
return a;
}());
}
expect: {
console.log(function() {
var a = "bar";
console.log("foo");
return a;
}());
}
expect_stdout: [
"foo",
"bar",
]
expect_warnings: [
"INFO: Dropping unused loop variable f [test/compress/loops.js:3,21]",
"WARN: Side effects in object of for-in loop [test/compress/loops.js:3,30]",
] ]
} }
@@ -933,3 +963,49 @@ issue_3634_2: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_4075: {
options = {
loops: true,
unused: true,
}
input: {
var a = "FAIL";
(function() {
for (a in { PASS: 0 });
})()
console.log(a);
}
expect: {
var a = "FAIL";
(function() {
for (a in { PASS: 0 });
})()
console.log(a);
}
expect_stdout: "PASS"
}
issue_4082: {
options = {
keep_fargs: "strict",
loops: true,
unused: true,
}
input: {
var a = "PASS";
(function(a) {
for (a in "foo")
var b;
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(a) {
for (a in "foo");
})();
console.log(a);
}
expect_stdout: "PASS"
}

View File

@@ -130,7 +130,7 @@ evaluate_string_length: {
} }
} }
mangle_properties: { mangle_properties_1: {
mangle = { mangle = {
properties: { properties: {
keep_quoted: false, keep_quoted: false,
@@ -152,6 +152,53 @@ mangle_properties: {
} }
} }
mangle_properties_2: {
mangle = {
properties: {
reserved: [
"value",
]
},
}
input: {
var o = {
prop1: 1,
};
Object.defineProperty(o, "prop2", {
value: 2,
});
Object.defineProperties(o, {
prop3: {
value: 3,
},
});
console.log("prop1", o.prop1, "prop1" in o);
console.log("prop2", o.prop2, o.hasOwnProperty("prop2"));
console.log("prop3", o.prop3, Object.getOwnPropertyDescriptor(o, "prop3").value);
}
expect: {
var o = {
o: 1,
};
Object.defineProperty(o, "p", {
value: 2,
});
Object.defineProperties(o, {
r: {
value: 3,
},
});
console.log("prop1", o.o, "o" in o);
console.log("prop2", o.p, o.hasOwnProperty("p"));
console.log("prop3", o.r, Object.getOwnPropertyDescriptor(o, "r").value);
}
expect_stdout: [
"prop1 1 true",
"prop2 2 true",
"prop3 3 3",
]
}
mangle_unquoted_properties: { mangle_unquoted_properties: {
options = { options = {
evaluate: true, evaluate: true,
@@ -1046,16 +1093,22 @@ array_hole: {
side_effects: true, side_effects: true,
} }
input: { input: {
console.log( Array.prototype[2] = "PASS";
[ 1, 2, , 3][1], console.log([ 1, 2, , 3 ][1]);
[ 1, 2, , 3][2], console.log([ 1, 2, , 3 ][2]);
[ 1, 2, , 3][3] console.log([ 1, 2, , 3 ][3]);
);
} }
expect: { expect: {
console.log(2, void 0, 3); Array.prototype[2] = "PASS";
console.log(2);
console.log([ , , , ][2]);
console.log(3);
} }
expect_stdout: "2 undefined 3" expect_stdout: [
"2",
"PASS",
"3",
]
} }
new_this: { new_this: {

View File

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

View File

@@ -905,7 +905,7 @@ use_before_var: {
} }
expect: { expect: {
function f(){ function f(){
console.log(t); console.log(void 0);
var t = 1; var t = 1;
} }
} }
@@ -981,12 +981,12 @@ inner_var_for_1: {
expect: { expect: {
function f() { function f() {
var a = 1; var a = 1;
x(1, b, d); x(1, void 0, d);
for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) { for (var b = 2, c = 3; x(1, 2, 3, d); x(1, 2, 3, d)) {
var d = 4, e = 5; 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: { expect: {
function f() { function f() {
console.log(1 + h()); console.log(1 + (void 0)());
var h = function() {
return 2;
};
} }
} }
} }
@@ -2034,6 +2031,7 @@ issue_1670_4: {
issue_1670_5: { issue_1670_5: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
keep_fargs: false, keep_fargs: false,
@@ -2065,6 +2063,7 @@ issue_1670_5: {
issue_1670_6: { issue_1670_6: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
keep_fargs: false, keep_fargs: false,
@@ -2372,8 +2371,7 @@ delay_def: {
return; return;
} }
function g() { function g() {
return a; return;
var a = 1;
} }
console.log(f(), g()); console.log(f(), g());
} }
@@ -2395,7 +2393,7 @@ delay_def_lhs: {
expect: { expect: {
console.log(function() { console.log(function() {
long_name++; long_name++;
return long_name; return NaN;
var long_name; var long_name;
}()); }());
} }
@@ -2651,11 +2649,9 @@ var_assign_5: {
} }
expect: { expect: {
!function() { !function() {
var a;
!function(b) { !function(b) {
a = 2, console.log(2, void 0);
console.log(a, b); }();
}(a);
}(); }();
} }
expect_stdout: "2 undefined" expect_stdout: "2 undefined"
@@ -2676,8 +2672,8 @@ var_assign_6: {
} }
expect: { expect: {
!function() { !function() {
var a = function(){}(a = 1); (function(){}());
console.log(a); console.log(void 0);
}(); }();
} }
expect_stdout: "undefined" expect_stdout: "undefined"
@@ -2724,8 +2720,8 @@ issue_1814_1: {
expect: { expect: {
!function() { !function() {
!function(a) { !function(a) {
console.log(a++, 42); console.log(0, 42);
}(0); }();
}(); }();
} }
expect_stdout: "0 42" expect_stdout: "0 42"
@@ -2751,8 +2747,8 @@ issue_1814_2: {
expect: { expect: {
!function() { !function() {
!function(a) { !function(a) {
console.log("321", a++); console.log("321", 0);
}(0); }();
}(); }();
} }
expect_stdout: "321 0" expect_stdout: "321 0"
@@ -4725,7 +4721,7 @@ escape_conditional: {
function bar() {} function bar() {}
(function() { (function() {
var thing = baz(); var thing = baz();
if (thing !== (thing = baz())) if (thing !== baz())
console.log("FAIL"); console.log("FAIL");
else else
console.log("PASS"); console.log("PASS");
@@ -4763,7 +4759,7 @@ escape_sequence: {
function bar() {} function bar() {}
(function() { (function() {
var thing = baz(); var thing = baz();
if (thing !== (thing = baz())) if (thing !== baz())
console.log("FAIL"); console.log("FAIL");
else else
console.log("PASS"); console.log("PASS");
@@ -4808,7 +4804,7 @@ escape_throw: {
function foo() {} function foo() {}
(function() { (function() {
var thing = baz(); var thing = baz();
if (thing !== (thing = baz())) if (thing !== baz())
console.log("FAIL"); console.log("FAIL");
else else
console.log("PASS"); console.log("PASS");
@@ -4845,7 +4841,7 @@ escape_local_conditional: {
} }
(function() { (function() {
var thing = baz(); var thing = baz();
if (thing !== (thing = baz())) if (thing !== baz())
console.log("PASS"); console.log("PASS");
else else
console.log("FAIL"); console.log("FAIL");
@@ -4882,7 +4878,7 @@ escape_local_sequence: {
} }
(function() { (function() {
var thing = baz(); var thing = baz();
if (thing !== (thing = baz())) if (thing !== baz())
console.log("PASS"); console.log("PASS");
else else
console.log("FAIL"); console.log("FAIL");
@@ -4926,7 +4922,7 @@ escape_local_throw: {
} }
(function() { (function() {
var thing = baz(); var thing = baz();
if (thing !== (thing = baz())) if (thing !== baz())
console.log("PASS"); console.log("PASS");
else else
console.log("FAIL"); console.log("FAIL");
@@ -4980,11 +4976,7 @@ inverted_var: {
console.log(1, 2, 3, 4, 5, function c() { console.log(1, 2, 3, 4, 5, function c() {
c = 6; c = 6;
return c; return c;
}(), 7, function() { }(), 7, 8);
c = 8;
return c;
var c = "foo";
}());
} }
expect_stdout: true expect_stdout: true
} }
@@ -5180,9 +5172,7 @@ defun_var_3: {
var a = 42, b; var a = 42, b;
} }
expect: { expect: {
function a() {} console.log("function", "function");
console.log(typeof a, "function");
var a = 42;
} }
expect_stdout: "function function" expect_stdout: "function function"
} }
@@ -7122,3 +7112,319 @@ issue_3922: {
} }
expect_stdout: "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]" expect_stdout: "[object global]"
} }
issue_4054: {
input: {
console.log({
set p(v) {
throw "FAIL";
},
});
}
expect: {
console.log({
set p(v) {
throw "FAIL";
},
});
}
expect_stdout: "{ p: [Setter] }"
}

View File

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

View File

@@ -97,9 +97,8 @@ issue_2233_2: {
var RegExp; var RegExp;
UndeclaredGlobal; UndeclaredGlobal;
function foo() { function foo() {
var Number;
AnotherUndeclaredGlobal; AnotherUndeclaredGlobal;
Number.isNaN; (void 0).isNaN;
} }
} }
} }
@@ -275,3 +274,120 @@ drop_value: {
foo(), bar(); foo(), bar();
} }
} }
operator_in: {
options = {
side_effects: true,
}
input: {
try {
"foo" in true;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
0 in true;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_3983_1: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f() {
g && g();
}
f();
function g() {
0 ? a : 0;
}
var b = a;
console.log(a);
}
expect: {
var a = "PASS";
g();
function g() {}
console.log(a);
}
expect_stdout: "PASS"
}
issue_3983_2: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f() {
g && g();
}
f();
function g() {
0 ? a : 0;
}
var b = a;
console.log(a);
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4008: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
function f(b, b) {
console.log(b);
}
f && f(a && a[a]);
console.log(a);
}
expect: {
var a = "PASS";
function f(b, b) {
console.log(b);
}
f(a[a]);
console.log(a);
}
expect_stdout: [
"undefined",
"PASS",
]
}

View File

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

View File

@@ -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) { it("Should work with `--output-opts`", function(done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -O'; var command = uglifyjscmd + ' test/input/issue-1482/input.js -O ascii_only';
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js")); assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
@@ -257,7 +257,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail when both --beautify & --output-opts are specified", function(done) { it("Should fail when both --beautify & --output-opts are specified", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js -bO"; var command = uglifyjscmd + " test/input/issue-520/input.js -bO ascii_only";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n"); assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n");
@@ -330,7 +330,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with invalid syntax", function(done) { it("Should fail with invalid syntax", function(done) {
var command = uglifyjscmd + ' test/input/invalid/simple.js'; var command = uglifyjscmd + " test/input/invalid/simple.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -342,7 +342,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with correct marking of tabs", function(done) { it("Should fail with correct marking of tabs", function(done) {
var command = uglifyjscmd + ' test/input/invalid/tab.js'; var command = uglifyjscmd + " test/input/invalid/tab.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -354,7 +354,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with correct marking at start of line", function(done) { it("Should fail with correct marking at start of line", function(done) {
var command = uglifyjscmd + ' test/input/invalid/eof.js'; var command = uglifyjscmd + " test/input/invalid/eof.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -366,7 +366,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with a missing loop body", function(done) { it("Should fail with a missing loop body", function(done) {
var command = uglifyjscmd + ' test/input/invalid/loop-no-body.js'; var command = uglifyjscmd + " test/input/invalid/loop-no-body.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -378,7 +378,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (5--)", function(done) { it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js'; var command = uglifyjscmd + " test/input/invalid/assign_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -392,7 +392,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (Math.random() /= 2)", function(done) { it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js'; var command = uglifyjscmd + " test/input/invalid/assign_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -406,7 +406,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (++this)", function(done) { it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js'; var command = uglifyjscmd + " test/input/invalid/assign_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -420,7 +420,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (++null)", function(done) { it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js'; var command = uglifyjscmd + " test/input/invalid/assign_4.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -434,7 +434,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (a.=)", function(done) { it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js'; var command = uglifyjscmd + " test/input/invalid/dot_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -448,7 +448,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (%.a)", function(done) { it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js'; var command = uglifyjscmd + " test/input/invalid/dot_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -462,7 +462,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (a./();)", function(done) { it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js'; var command = uglifyjscmd + " test/input/invalid/dot_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -476,7 +476,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error ({%: 1})", function(done) { it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js'; var command = uglifyjscmd + " test/input/invalid/object.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -490,7 +490,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (delete x)", function(done) { it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js'; var command = uglifyjscmd + " test/input/invalid/delete.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -504,7 +504,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (function g(arguments))", function(done) { it("Should throw syntax error (function g(arguments))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_1.js'; var command = uglifyjscmd + " test/input/invalid/function_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -518,7 +518,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (function eval())", function(done) { it("Should throw syntax error (function eval())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_2.js'; var command = uglifyjscmd + " test/input/invalid/function_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -532,7 +532,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (iife arguments())", function(done) { it("Should throw syntax error (iife arguments())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_3.js'; var command = uglifyjscmd + " test/input/invalid/function_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -546,7 +546,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (catch (eval))", function(done) { it("Should throw syntax error (catch (eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js'; var command = uglifyjscmd + " test/input/invalid/try.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -560,7 +560,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (var eval)", function(done) { it("Should throw syntax error (var eval)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/var.js'; var command = uglifyjscmd + " test/input/invalid/var.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -574,7 +574,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (else)", function(done) { it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js'; var command = uglifyjscmd + " test/input/invalid/else.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -588,7 +588,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (return)", function(done) { it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js'; var command = uglifyjscmd + " test/input/invalid/return.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -602,7 +602,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (for-in init)", function(done) { it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js'; var command = uglifyjscmd + " test/input/invalid/for-in_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -616,7 +616,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (for-in var)", function(done) { it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js'; var command = uglifyjscmd + " test/input/invalid/for-in_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -629,6 +629,18 @@ describe("bin/uglifyjs", function() {
done(); done();
}); });
}); });
it("Should throw syntax error (switch defaults)", function(done) {
var command = uglifyjscmd + " test/input/invalid/switch.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/switch.js:3,2");
assert.strictEqual(lines[1], " default:");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: More than one default clause in switch statement");
done();
});
});
it("Should handle literal string as source map input", function(done) { it("Should handle literal string as source map input", function(done) {
var command = [ var command = [
uglifyjscmd, uglifyjscmd,
@@ -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"; var command = uglifyjscmd + " test/input/issue-3315/input.js --config-file test/input/issue-3315/config.json";
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, 'function f(){"aaaaaaaaaa";var a={prop:1,a:2};return a.prop+a.a}\n'); assert.strictEqual(stdout, 'function f(){"aaaaaaaaaa";var a={prop:1,t:2};return a.prop+a.t}\n');
done(); done();
}); });
}); });

View File

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

View File

@@ -296,4 +296,18 @@ describe("test/reduce.js", function() {
"// }", "// }",
]).join("\n")); ]).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

@@ -438,7 +438,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
})); }));
var code = testcase_ast.print_to_string(); 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; testcase = code;
differs = diff; differs = diff;
} else { } else {

View File

@@ -40,7 +40,7 @@ function createContext() {
arg.constructor.toString(); arg.constructor.toString();
if (level--) for (var key in arg) { if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key); var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level); if (!desc || !desc.get && !desc.set) arg[key] = safe_log(arg[key], level);
} }
} }
return arg; return arg;

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", "this",
]; ];
var BINARY_OPS_NO_COMMA = [ var BINARY_OPS = [
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
" - ", " - ",
"/", "/",
@@ -190,9 +190,15 @@ var BINARY_OPS_NO_COMMA = [
"%", "%",
"&&", "&&",
"||", "||",
"^" ]; "^",
",",
var BINARY_OPS = [","].concat(BINARY_OPS_NO_COMMA); ];
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in ");
var ASSIGNMENTS = [ var ASSIGNMENTS = [
"=", "=",
@@ -490,11 +496,16 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var label = createLabel(canBreak, canContinue); var label = createLabel(canBreak, canContinue);
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
var optElementVar = ""; var key = rng(10) ? "key" + loop : getVarName();
if (rng(5) > 1) { return [
optElementVar = "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[key" + loop + "]; "; "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
} label.target + " for (",
return "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; " + label.target + " for (var key" + loop + " in expr" + loop + ") {" + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}}"; /^key/.test(key) ? "var " : "",
key + " in expr" + loop + ") {",
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
"}}",
].join("");
case STMT_SEMI: case STMT_SEMI:
return use_strict && rng(20) === 0 ? '"use strict";' : ";"; return use_strict && rng(20) === 0 ? '"use strict";' : ";";
case STMT_EXPR: case STMT_EXPR:
@@ -762,6 +773,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
case p++: case p++:
return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); 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++: case p++:
var name = getVarName(); var name = getVarName();
var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; 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) { function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow)
+ createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + createBinaryOp(noComma, canThrow) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
} }
function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
// intentionally generate more hardcore ops // intentionally generate more hardcore ops
@@ -929,9 +944,12 @@ function createValue() {
return VALUES[rng(VALUES.length)]; return VALUES[rng(VALUES.length)];
} }
function createBinaryOp(noComma) { function createBinaryOp(noComma, canThrow) {
if (noComma) return BINARY_OPS_NO_COMMA[rng(BINARY_OPS_NO_COMMA.length)]; var op;
return BINARY_OPS[rng(BINARY_OPS.length)]; do {
op = BINARY_OPS[rng(BINARY_OPS.length)];
} while (noComma && op == "," || !canThrow && op == " in ");
return op;
} }
function createAssignment() { function createAssignment() {
@@ -1124,18 +1142,6 @@ function log(options) {
errorln(original_result); errorln(original_result);
errorln("uglified result:"); errorln("uglified result:");
errorln(uglify_result); errorln(uglify_result);
errorln("//-------------------------------------------------------------");
var reduced = reduce_test(original_code, JSON.parse(options), {
verbose: false,
}).code;
if (reduced) {
errorln();
errorln("// reduced test case (output will differ)");
errorln();
errorln(reduced);
errorln();
errorln("//-------------------------------------------------------------");
}
} else { } else {
errorln("// !!! uglify failed !!!"); errorln("// !!! uglify failed !!!");
errorln(uglify_code); errorln(uglify_code);
@@ -1146,6 +1152,20 @@ function log(options) {
errorln(original_result); errorln(original_result);
} }
} }
errorln("//-------------------------------------------------------------");
var reduce_options = JSON.parse(options);
reduce_options.validate = true;
var reduced = reduce_test(original_code, reduce_options, {
verbose: false,
}).code;
if (reduced) {
errorln();
errorln("// reduced test case (output will differ)");
errorln();
errorln(reduced);
errorln();
errorln("//-------------------------------------------------------------");
}
errorln("minify(options):"); errorln("minify(options):");
errorln(JSON.stringify(JSON.parse(options), null, 2)); errorln(JSON.stringify(JSON.parse(options), null, 2));
errorln(); errorln();
@@ -1159,6 +1179,11 @@ function log(options) {
} }
} }
function sort_globals(code) {
var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
return globals.length ? "var " + globals.join(",") + ";" + code : code;
}
function fuzzy_match(original, uglified) { function fuzzy_match(original, uglified) {
uglified = uglified.split(" "); uglified = uglified.split(" ");
var i = uglified.length; var i = uglified.length;
@@ -1173,35 +1198,64 @@ function fuzzy_match(original, uglified) {
return true; return true;
} }
function skip_infinite_recursion(orig, toplevel) { function patch_try_catch(orig, toplevel) {
var code = orig; var stack = [ {
var tries = []; code: orig,
var offset = 0; index: 0,
offset: 0,
tries: [],
} ];
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g; 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; var match;
re.lastIndex = stack.shift().index;
while (match = re.exec(code)) { while (match = re.exec(code)) {
if (/}\s*finally\s*$/.test(match[0])) {
tries.shift();
continue;
}
var index = match.index + match[0].length + 1; var index = match.index + match[0].length + 1;
if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) { if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) {
tries.unshift({ try: index - offset }); tries.unshift({ try: index - offset });
continue; 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(); while (tries.length && tries[0].catch) tries.shift();
tries[0].catch = index; tries[0].catch = index - offset;
var insert = "throw " + match[1] + ".ufuzz_skip || (" + match[1] + ".ufuzz_skip = " + tries[0].try + "), " + match[1] + ";"; 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 new_code = code.slice(0, index) + insert + code.slice(index);
var result = sandbox.run_code(new_code, toplevel); var result = sandbox.run_code(new_code, toplevel);
if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") { 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; offset += insert.length;
code = new_code; 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") { } else if (result.name == "RangeError" && result.message == "Maximum call stack size exceeded") {
index = result.ufuzz_skip; index = result.ufuzz_try;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index); return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
} }
} }
stack.filled = true;
}
} }
var fallback_options = [ JSON.stringify({ var fallback_options = [ JSON.stringify({
@@ -1209,14 +1263,6 @@ var fallback_options = [ JSON.stringify({
mangle: false mangle: false
}) ]; }) ];
var minify_options = require("./options.json").map(JSON.stringify); 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 original_code, original_result, errored;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
@@ -1238,7 +1284,7 @@ for (var round = 1; round <= num_iterations; round++) {
ok = sandbox.same_stdout(original_result, uglify_result); ok = sandbox.same_stdout(original_result, uglify_result);
// ignore declaration order of global variables // ignore declaration order of global variables
if (!ok && !toplevel) { 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` // ignore numerical imprecision caused by `unsafe_math`
if (!ok && typeof uglify_result == "string" && o.compress && o.compress.unsafe_math) { if (!ok && typeof uglify_result == "string" && o.compress && o.compress.unsafe_math) {
@@ -1248,10 +1294,11 @@ for (var round = 1; round <= num_iterations; round++) {
ok = sandbox.same_stdout(fuzzy_result, uglify_result); ok = sandbox.same_stdout(fuzzy_result, uglify_result);
} }
} }
// ignore difference in error message caused by `in`
// ignore difference in depth of termination caused by infinite recursion // ignore difference in depth of termination caused by infinite recursion
if (!ok) { if (!ok) {
var orig_skipped = skip_infinite_recursion(original_code, toplevel); var orig_skipped = patch_try_catch(original_code, toplevel);
var uglify_skipped = skip_infinite_recursion(uglify_code, toplevel); var uglify_skipped = patch_try_catch(uglify_code, toplevel);
if (orig_skipped && uglify_skipped) { if (orig_skipped && uglify_skipped) {
ok = sandbox.same_stdout(sandbox.run_code(orig_skipped, toplevel), sandbox.run_code(uglify_skipped, toplevel)); 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>