Compare commits

...

116 Commits

Author SHA1 Message Date
Alex Lam S.L
0cfbd79aa1 v3.3.10 2018-02-08 10:16:16 +00:00
Alex Lam S.L
d66d86f20b account for exceptions in AST_Assign.left (#2892)
fixes #2891
2018-02-08 03:31:51 +08:00
Alex Lam S.L
905325d3e2 update dependencies (#2889)
acorn 5.4.1
commander 2.14.1
semver 5.5.0
2018-02-07 18:13:18 +08:00
Alex Lam S.L
dea0cc0662 mention file encoding (#2887) 2018-02-06 16:48:49 +08:00
Alex Lam S.L
d69d8007d6 evaluate to{Low,Upp}erCase() under unsafe (#2886) 2018-02-06 15:57:15 +08:00
Dan
c0b8f2a16d add information on testing and code style (#2885)
fixes #2884
2018-02-06 14:19:28 +08:00
Dan
cb0257dbbf describe a few compiler assumptions (#2883) 2018-02-06 14:19:03 +08:00
Dan
9637f51b68 change undefined == x to null == x (#2882)
fixes #2871
2018-02-05 15:00:23 +08:00
Alex Lam S.L
3026bd8975 improve exceptional flow compression by collapse_vars (#2880) 2018-02-04 04:18:22 +08:00
Alex Lam S.L
78a44d5ab0 maintain order between side-effects and externally observable assignments (#2879)
fixes #2878
2018-02-04 03:58:49 +08:00
Alex Lam S.L
7e13c0db40 handle break & continue in collapse_vars (#2875)
fixes #2873
2018-02-03 07:58:43 +08:00
Alex Lam S.L
e6a2e9e4d0 allow collapse_vars across conditional branches (#2867) 2018-02-03 02:44:40 +08:00
Alex Lam S.L
e773f03927 fix assignment logic in reduce_vars (#2872)
fixes #2869
2018-02-03 01:33:09 +08:00
Alex Lam S.L
b16380d669 fix missing corner case in #2855 (#2868) 2018-02-02 18:08:56 +08:00
Ryan Gunn
334b07a3db Update License Copyright Year to 2018 (#2866) 2018-02-02 16:30:01 +08:00
Alex Lam S.L
3cc1527f00 always test for rename (#2865) 2018-02-01 21:42:55 +08:00
Alex Lam S.L
525a61fb55 better fix for #2858 (#2864) 2018-02-01 20:06:36 +08:00
Alex Lam S.L
c3a002ff97 account for side-effects in comparisons of null & undefined (#2863) 2018-02-01 19:15:17 +08:00
Alex Lam S.L
fad6766a90 simplify comparisons with undefined & null (#2862)
fixes #2857
2018-02-01 16:50:54 +08:00
Alex Lam S.L
aa664dea0a avoid evaluate of compound assignment after dead_code transform (#2861)
fixes #2860
2018-02-01 16:18:29 +08:00
Alex Lam S.L
102f994b9d account for declaration assignment in collapse_vars (#2859)
fixes #2858
2018-02-01 15:09:53 +08:00
Alex Lam S.L
2a4c68be4f relax collapse_vars on AST_Exit (#2855)
First introduced in #1862 to stop assignments to migrate beyond `return` or `throw`. Since then `collapse_vars` has been improved to handle various side-effect-related corner cases.
2018-01-31 23:49:59 +08:00
Alex Lam S.L
541e6011af improve symbol replacement heuristic (#2851) 2018-01-29 17:41:15 +08:00
Alex Lam S.L
6fa3fbeae8 compress chained compound assignments (#2850) 2018-01-29 15:13:25 +08:00
Alex Lam S.L
4eb4cb656c v3.3.9 2018-01-27 12:56:34 +00:00
Alex Lam S.L
193612ac67 fix accounting after conversion to assignment (#2847)
Missing reference to `AST_SymbolRef` created by `unused` causes `collapse_vars` to misbehave.

fixes #2846
2018-01-26 14:21:11 +08:00
Alex Lam S.L
95cfce68ea backport of #2835 (#2841) 2018-01-23 05:45:45 +08:00
Alex Lam S.L
ec4202590d drop assignments to constant expressions only (#2839)
fixes #2838
2018-01-23 02:49:54 +08:00
Alex Lam S.L
5e2cd07d6f handle duplicate function declarations correctly (#2837)
fixes #2836
2018-01-23 01:28:09 +08:00
Alex Lam S.L
06166df999 v3.3.8 2018-01-21 07:08:01 +00:00
Alex Lam S.L
e2dc9cf091 fix unsafe evaluate of AST_Array (#2825)
fixes #2822
2018-01-21 01:39:44 +08:00
Alex Lam S.L
069df27bf1 enable unsafe for test/ufuzz.js (#2819)
- introduce `unsafe_undefined`
- safer `.toString()` compression

Miscellaneous
- rename `unsafe_Function`
2018-01-19 23:47:42 +08:00
Alex Lam S.L
3e7873217c improve unused on built-in functions (#2817) 2018-01-19 20:41:57 +08:00
Alex Lam S.L
e21bab7ce6 avoid duplicate property names in object literals under "use strict" (#2818)
fixes #2816
2018-01-19 20:13:50 +08:00
Alex Lam S.L
ac9a168fba fix & improve test/ufuzz.js (#2815)
- use correct `options` when testing `rename`
- mask arbitrarily assigned function IDs to reduce rate of false positives
2018-01-19 16:51:59 +08:00
Alex Lam S.L
81b64549ce fix time-out for respawned test/ufuzz.js (#2814) 2018-01-19 06:11:19 +08:00
Alex Lam S.L
082e004b87 compress undefined property names (#2811)
- enforce property names as string
- handle `void 0` as `undefined` in `hoist_props` & `reduce_vars`
2018-01-19 00:36:30 +08:00
kzc
983e69128b fix join_vars property assignment for negative array index (#2810)
fixes #2790
2018-01-18 21:52:54 +08:00
Alex Lam S.L
b335912e86 enhance test/ufuzz.js (#2808)
- standalone test for `rename`
- handle `keep_fargs` & `rename` upon failure
2018-01-18 14:08:05 +08:00
Alex Lam S.L
cc07f3b806 faster output of comments (#2806) 2018-01-18 02:57:33 +08:00
Alex Lam S.L
07e4b64f3a fix AST_Scope.clone() (#2803)
fixes #2799
2018-01-17 21:33:13 +08:00
Alex Lam S.L
d3ce2bc9e7 suppress unsafe_proto for LHS expressions (#2804) 2018-01-17 20:41:51 +08:00
Alex Lam S.L
cff3bf4914 configure rename with CLI (#2802) 2018-01-17 15:12:22 +08:00
Alex Lam S.L
79cfac77bd extend join_vars & sequences (#2798) 2018-01-17 13:58:27 +08:00
Alex Lam S.L
224c14d49d improve mocha tests (#2797)
- workaround sporadic delays from Travis CI
2018-01-16 17:51:25 +08:00
Alex Lam S.L
7857354d85 improve test/travis-ufuzz.js (#2795)
- print usage
- support concurrent jobs
- improve instance utilisation
- resume after V8 self-destruct
2018-01-16 17:33:21 +08:00
Alex Lam S.L
b4aef753e7 general improvements around AST_ForIn (#2796)
- compress using `collapse_vars`
- remove unused `name`
- simplify `loop_body`
2018-01-16 17:03:12 +08:00
Joël Galeran
424173d311 fix typo in README (#2792) 2018-01-16 10:29:38 +08:00
Alex Lam S.L
ec7cd1dcf7 handle VM failure gracefully (#2791) 2018-01-15 23:41:39 +08:00
Alex Lam S.L
7def684730 improve test/travis-ufuzz.js (#2789)
- wait for instance to boot
- run on forked repositories
- workaround `request_limit_reached`
2018-01-15 19:18:21 +08:00
Alex Lam S.L
10f961c27b enhance collapse_vars (#2788) 2018-01-15 18:47:23 +08:00
Alex Lam S.L
b483678ca7 avoid suboptimal termination in passes (#2787) 2018-01-15 16:42:31 +08:00
Alex Lam S.L
cbbe6fad60 avoid double counting within single-use functions (#2785)
fixes #2783
2018-01-15 16:42:15 +08:00
Alex Lam S.L
f96929c031 improve test/travis-ufuzz.js (#2786)
- use more RAM
- show progress in console
- report failure as job status
2018-01-15 15:08:35 +08:00
Alex Lam S.L
2b6657e967 run test/ufuzz.js when Travis CI is idle (#2784) 2018-01-15 08:52:11 +08:00
Alex Lam S.L
7c0c92943f v3.3.7 2018-01-14 09:13:26 +00:00
Alex Lam S.L
62a66dfff4 fix & extend join_vars for object assigments (#2781) 2018-01-14 17:11:31 +08:00
kzc
2cab348341 improve SymbolDef info in --output ast (#2778)
* SymbolDef info (a.k.a. `thedef`) is now represented as a string containing `"ID name [mangled_name]"`. 
* Enhance display of `globals`, `variables`, `functions` and `enclosed`.
* `SymbolDef.next_id` starts at `1` and the `id` is adjusted for `-o ast` display.
2018-01-14 01:40:51 +08:00
Alex Lam S.L
460218a3f8 v3.3.6 2018-01-13 05:37:42 +00:00
Alex Lam S.L
e49416e4aa fix reduce_vars on AST_Accessor (#2776)
fixes #2774
2018-01-13 02:46:14 +08:00
kzc
d4d7d99b70 add SymbolDef IDs to --output ast (#2772) 2018-01-12 15:41:09 +08:00
Alex Lam S.L
6a696d0a7b fix output of imported AST (#2771) 2018-01-12 01:05:49 +08:00
Alex Lam S.L
1c9e13f47d update dependencies (#2770)
- acorn@5.3.0
- commander@2.13.0
2018-01-12 00:32:17 +08:00
Alex Lam S.L
b757450cd8 fix nested unused assignments (#2769)
fixes #2768
2018-01-11 23:13:44 +08:00
Alex Lam S.L
23ec484806 fix corner case in #2763 (#2766) 2018-01-11 21:18:08 +08:00
Alex Lam S.L
f1e1bb419a join object assignments (#2763) 2018-01-11 17:08:21 +08:00
Alex Lam S.L
6a0af85c8b skip only vars in if_return (#2759)
fixes #2747
2018-01-10 19:08:46 +08:00
Alex Lam S.L
09269be974 enhance conditionals (#2758)
`x ? y || z : z` --> `x && y || z`
2018-01-10 16:59:57 +08:00
Alex Lam S.L
bf832cde16 improve synergy between compress and rename (#2755) 2018-01-09 17:55:41 +08:00
Alex Lam S.L
2972d58dbb patch variable declaractions extracted within catch (#2753)
fixes #2749
2018-01-09 13:54:35 +08:00
Alex Lam S.L
2e22d38a02 improve rename reproducibility (#2754)
fixes #2752
2018-01-09 13:53:05 +08:00
Alex Lam S.L
ce27bcd69a compress loops with immediate break (#2746)
fixes #2740
2018-01-08 14:30:18 +08:00
Alex Lam S.L
9336cc8247 v3.3.5 2018-01-07 11:51:36 +00:00
Alex Lam S.L
9809567dfc improve process.exit() workaround (#2741)
- use public API
- fix issue with Node.js 0.10 on WIndows
2018-01-07 17:53:50 +08:00
Alex Lam S.L
1ee8be8d91 fix recursive function inline (#2738)
fixes #2737
2018-01-07 15:31:24 +08:00
Alex Lam S.L
8430c2f9f8 enable AppVeyor CI (#2739) 2018-01-07 14:14:58 +08:00
Alex Lam S.L
659c8a7632 handle trailing line comments correctly (#2736)
fixes #2734
2018-01-06 21:05:21 +08:00
Alex Lam S.L
3564b4f20d compress RegExp() in unsafe (#2735) 2018-01-06 21:04:47 +08:00
Alex Lam S.L
3505a3604a enhance unsafe_proto (#2733) 2018-01-06 06:16:51 +08:00
kzc
9b1bc6c014 ufuzz: add strings "a", "b", "c" to VALUES (#2732) 2018-01-06 05:54:53 +08:00
Alex Lam S.L
9f23185f2b fix corner case with arguments as function name (#2729)
fixes #2728
2018-01-05 22:21:18 +08:00
Alex Lam S.L
b82feb9302 improve if_return (#2727) 2018-01-05 20:24:30 +08:00
Alex Lam S.L
7f2a591c7e warn on deprecated features (#2726)
- `function.arguments`
- `function.callers`

fixes #2719
2018-01-05 19:36:02 +08:00
Alex Lam S.L
afbcebddf6 fix mangle name collision across files (#2722) 2018-01-05 05:08:09 +08:00
Alex Lam S.L
484e484571 fix corner case in inline (#2720) 2018-01-04 23:38:37 +08:00
Alex Lam S.L
6f3f21233f reminify tests upon expect_stdout (#2716) 2018-01-04 20:13:05 +08:00
Alex Lam S.L
a6873a3859 forbid block-scoped AST_Defun in strict mode (#2718) 2018-01-04 18:45:51 +08:00
Alex Lam S.L
7a6d452b54 preserve constant modification under strict mode (#2717) 2018-01-04 15:53:53 +08:00
Alex Lam S.L
9b58b54e2d extend inline (#2714)
- compress `function` with variables within loops
- restrict to `AST_Var` for better compatibility with ES6+
2018-01-04 12:58:40 +08:00
Alex Lam S.L
c598a12af9 apply collapse_vars to loop conditions (#2712) 2018-01-04 03:18:28 +08:00
Alex Lam S.L
cfe3a98ce5 drop unused assignment based on reduce_vars (#2709) 2018-01-04 01:03:33 +08:00
Alex Lam S.L
14778e049b fix reduce_vars on AST_Defun (#2708) 2018-01-03 17:18:38 +08:00
Alex Lam S.L
446fb0198b extend __PURE__ to AST_New (#2706)
fixes #2705
2018-01-03 04:48:07 +08:00
Alex Lam S.L
7d3cddf9d6 inline functions with AST_Var (#2688) 2018-01-03 01:54:44 +08:00
Alex Lam S.L
6dead95eb3 enhance collapse_vars (#2704) 2018-01-02 18:42:15 +08:00
Alex Lam S.L
cc931b3ad8 enhance if_return (#2703) 2018-01-02 15:09:51 +08:00
Alex Lam S.L
d838b4b52e reset argument value within loop after inline (#2699) 2018-01-02 01:24:53 +08:00
Alex Lam S.L
2f3bddbaca scan within IIFEs of assigned values (#2702)
fixes #2701
2018-01-02 01:24:23 +08:00
Alex Lam S.L
673b071637 enhance join_vars & sequences (#2697)
- nudge declarations without assignments
  - within `AST_BlockStatement`
  - across `AST_If`
2018-01-01 00:09:26 +08:00
Alex Lam S.L
da82fa59a7 fix inline on duplicate argument names (#2698) 2017-12-31 20:59:58 +08:00
Alex Lam S.L
333792352e reduce hoisting declarations (#2687) 2017-12-31 16:15:00 +08:00
Alex Lam S.L
e2ec270b04 v3.3.4 2017-12-31 00:01:14 +08:00
Alex Lam S.L
ed7a0a454e fix dead_code on escaped return assignment (#2693)
fixes #2692
2017-12-30 15:20:25 +08:00
Alex Lam S.L
d819559a01 minor clean-ups (#2686) 2017-12-29 14:04:52 +08:00
Alex Lam S.L
8ca49155a8 v3.3.3 2017-12-29 03:07:39 +08:00
Alex Lam S.L
b95e3338d9 fix pure_getters on AST_Binary (#2681)
fixes #2678
2017-12-28 17:01:01 +08:00
Alex Lam S.L
e40a0ee9c6 improve assignment variations (#2671) 2017-12-28 15:36:55 +08:00
Alex Lam S.L
cb62bd98d3 fix function inlining within loops (#2675)
fixes #2663
2017-12-28 02:53:14 +08:00
Alex Lam S.L
f30790b11b fix dead_code on return assignments (#2668)
fixes #2666
2017-12-27 07:40:34 +08:00
Alex Lam S.L
5205dbcbf4 retain recursive function names (#2667)
fixes #2665
2017-12-27 07:00:12 +08:00
Alex Lam S.L
3ff625de7e fix bugs on substituted AST_Defun (#2661)
fixes #2660
2017-12-27 05:31:37 +08:00
Alex Lam S.L
4832bc5d88 replace single-use recursive functions (#2659)
fixes #2628
2017-12-26 21:25:35 +08:00
Alex Lam S.L
7f342cb3e3 suppress inline within substituted AST_Scope (#2658)
fixes #2657
2017-12-26 18:56:59 +08:00
Alex Lam S.L
05e7d34ed4 improve unused over duplicate variable names (#2656) 2017-12-26 18:29:28 +08:00
Alex Lam S.L
86607156e3 v3.3.2 2017-12-26 01:38:56 +08:00
Alex Lam S.L
0fe259e9c5 parse LF & comment correctly (#2653)
fixes #2652
2017-12-26 01:38:01 +08:00
50 changed files with 5675 additions and 930 deletions

61
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,61 @@
Contributing
============
## Documentation
Every new feature and API change should be accompanied by a README additon.
## Testing
All features and bugs should have tests that verify the fix. You can run all
tests using `npm test`.
The most common type of test are tests that verify input and output of the
Uglify transforms. These tests exist in `test/compress`. New tests can be added
either to an existing file or in a new file `issue-xxx.js`.
Tests that cannot be expressed as a simple AST can be found in `test/mocha`.
## Code style
- File encoding must be `UTF-8`.
- `LF` is always used as a line ending.
- Statements end with semicolons.
- Indentation uses 4 spaces, switch `case` 2 spaces.
- Identifiers use `snake_case`.
- Strings use double quotes (`"`).
- Use a trailing comma for multiline array and object literals to minimize diffs.
- The Uglify code only uses ES5, even in the `harmony` branch.
- Line length should be at most 80 cols, except when it is easier to read a
longer line.
- If both sides of a comparison are of the same type, `==` and `!=` are used.
- Multiline conditions place `&&` and `||` first on the line.
**Example feature**
```js
OPT(AST_Debugger, function(self, compressor) {
if (compressor.option("drop_debugger"))
return make_node(AST_EmptyStatement, self);
return self;
});
```
**Example test case**
```js
drop_debugger: {
options = {
drop_debugger: true,
}
input: {
debugger;
if (foo) debugger;
}
expect: {
if (foo);
}
}
```

View File

@@ -1,6 +1,6 @@
UglifyJS is released under the BSD license:
Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions

View File

@@ -70,7 +70,7 @@ a double dash to prevent input files being used as option arguments:
`debug` Add debug prefix and suffix.
`domprops` Mangle property names that overlaps
with DOM properties.
`keep_quoted` Only mangle unquoted properies.
`keep_quoted` Only mangle unquoted properties.
`regex` Only mangle matched property names.
`reserved` List of names that should not be mangled.
-b, --beautify [options] Beautify output/specify output options:
@@ -640,7 +640,13 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `inline` (default: `true`) -- embed simple functions
- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
- `false` -- same as `0`
- `0` -- disabled inlining
- `1` -- inline simple functions
- `2` -- inline functions with arguments
- `3` -- inline functions with arguments and variables
- `true` -- same as `3`
- `join_vars` (default: `true`) -- join consecutive `var` statements
@@ -731,7 +737,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)`
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
@@ -743,6 +749,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unsafe_undefined` (default: `false`) -- substitute `void 0` if there is a
variable named `undefined` in scope (variable name will be mangled, typically
reduced to a single character)
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`)
@@ -916,9 +926,6 @@ when this flag is on:
- `new Object()``{}`
- `String(exp)` or `exp.toString()``"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `void 0``undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
### Conditional compilation
@@ -1095,3 +1102,27 @@ To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```
#### Source maps and debugging
Various `compress` transforms that simplify, rearrange, inline and remove code
are known to have an adverse effect on debugging with source maps. This is
expected as code is optimized and mappings are often simply not possible as
some code no longer exists. For highest fidelity in source map debugging
disable the Uglify `compress` option and just use `mangle`.
### Compiler assumptions
To allow for better optimizations, the compiler makes various assumptions:
- `.toString()` and `.valueOf()` don't have side effects, and for built-in
objects they have not been overridden.
- `undefined`, `NaN` and `Infinity` have not been externally redefined.
- `arguments.callee`, `arguments.caller` and `Function.prototype.caller` are not used.
- The code doesn't expect the contents of `Function.prototype.toString()` or
`Error.prototype.stack` to be anything in particular.
- Getting and setting properties on a plain object does not cause other side effects
(using `.watch()` or `Proxy`).
- Object properties can be added, removed and modified (not prevented with
`Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`Object.preventExtensions()` or `Object.seal()`).

View File

@@ -2,23 +2,19 @@ environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4.0"
- nodejs_version: "6.0"
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "8"
install:
- ps: Install-Product node $env:nodejs_version
- set UGLIFYJS_TEST_ALL=1
- npm install
build: off
cache:
- tmp
matrix:
fast_finish: true
platform:
- x86
- x64
install:
- ps: Install-Product node $env:nodejs_version $env:platform
- npm install
test_script:
- node --version
- npm --version
- npm test
build: off

View File

@@ -3,11 +3,7 @@
"use strict";
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
require("../tools/exit");
var fs = require("fs");
var info = require("../package.json");
@@ -15,7 +11,7 @@ var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "enclosed", "inlined", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var files = {};
var options = {
compress: false,
@@ -47,6 +43,7 @@ program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("d
program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--rename", "Force symbol expansion.");
program.option("--no-rename", "Disable symbol expansion.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
@@ -66,13 +63,11 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
"compress",
"ie8",
"mangle",
"rename",
"sourceMap",
"toplevel",
"wrap"
].forEach(function(name) {
if (name in program) {
if (name == "rename" && program[name]) return;
options[name] = program[name];
}
});
@@ -125,6 +120,11 @@ if (program.parse) {
fatal("ERROR: inline source map only works with built-in parser");
}
}
if (~program.rawArgs.indexOf("--rename")) {
options.rename = true;
} else if (!program.rename) {
options.rename = false;
}
var convert_path = function(name) {
return name;
};
@@ -227,7 +227,20 @@ function run() {
}
fatal(ex);
} else if (program.output == "ast") {
if (!options.compress && !options.mangle) {
result.ast.figure_out_scope({});
}
print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) {
case "thedef":
return symdef(value);
case "enclosed":
return value.length ? value.map(symdef) : undefined;
case "variables":
case "functions":
case "globals":
return value.size() ? value.map(symdef) : undefined;
}
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
@@ -381,6 +394,12 @@ function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}
function symdef(def) {
var ret = (1e6 + def.id) + " " + def.name;
if (def.mangled_name) ret += " " + def.mangled_name;
return ret;
}
function format_object(obj) {
var lines = [];
var padding = "";

View File

@@ -267,11 +267,10 @@ var AST_For = DEFNODE("For", "init condition step", {
}
}, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", "init name object", {
var AST_ForIn = DEFNODE("ForIn", "init object", {
$documentation: "A `for ... in` statement",
$propdoc: {
init: "[AST_Node] the `for/in` initialization code",
name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
object: "[AST_Node] the object that we're looping through"
},
_walk: function(visitor) {
@@ -309,6 +308,13 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
},
clone: function(deep) {
var node = this._clone(deep);
if (this.variables) node.variables = this.variables.clone();
if (this.functions) node.functions = this.functions.clone();
if (this.enclosed) node.enclosed = this.enclosed.slice();
return node;
}
}, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
@@ -683,8 +689,8 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$propdoc: {
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.",
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
},
_walk: function(visitor) {
return visitor._visit(this, function(){

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,6 @@ function set_shorthand(name, options, keys) {
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
@@ -39,7 +38,6 @@ function init_cache(cache) {
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}

View File

@@ -451,10 +451,16 @@ function OutputStream(options) {
return OUTPUT;
};
function has_nlb() {
var index = OUTPUT.lastIndexOf("\n");
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function prepend_comments(node) {
var self = this;
var start = node.start;
if (!(start.comments_before && start.comments_before._dumped === self)) {
if (!start) return;
if (start.comments_before && start.comments_before._dumped === self) return;
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
@@ -498,7 +504,7 @@ function OutputStream(options) {
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = /(^|\n) *$/.test(OUTPUT);
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
@@ -527,14 +533,16 @@ function OutputStream(options) {
}
}
}
}
function append_comments(node, tail) {
var self = this;
var token = node.end;
if (!token) return;
var comments = token[tail ? "comments_before" : "comments_after"];
if (comments && comments._dumped !== self) {
if (!comments || comments._dumped === self) return;
if (!(node instanceof AST_Statement || all(comments, function(c) {
return !/comment[134]/.test(c.type);
}))) return;
comments._dumped = self;
var insert = OUTPUT.length;
comments.filter(comment_filter, node).forEach(function(c, i) {
@@ -543,7 +551,7 @@ function OutputStream(options) {
print("\n");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !/(^|\n) *$/.test(OUTPUT))) {
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\n");
indent();
} else if (i > 0 || !tail) {
@@ -559,7 +567,6 @@ function OutputStream(options) {
});
if (OUTPUT.length > insert) newline_insert = insert;
}
}
var stack = [];
return {
@@ -599,7 +606,7 @@ function OutputStream(options) {
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly ? noop : append_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
@@ -671,9 +678,7 @@ function OutputStream(options) {
}
};
PARENS(AST_Node, function(){
return false;
});
PARENS(AST_Node, return_false);
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
@@ -699,9 +704,7 @@ function OutputStream(options) {
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
return first_in_statement(output);
});
PARENS(AST_Object, first_in_statement);
PARENS(AST_Unary, function(output){
var p = output.parent();
@@ -1346,11 +1349,8 @@ function OutputStream(options) {
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
|| !output.option("beautify")
&& +key + "" == key)
&& parseFloat(key) >= 0) {
output.print_string(key);
} else if ("" + +key == key && key >= 0) {
output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {

View File

@@ -774,10 +774,15 @@ function parse($TEXT, options) {
function expect(punc) { return expect_token("punc", punc); };
function has_newline_before(token) {
return token.nlb || !all(token.comments_before, function(comment) {
return !comment.nlb;
});
}
function can_insert_semicolon() {
return !options.strict && (
S.token.nlb || is("eof") || is("punc", "}")
);
return !options.strict
&& (is("eof") || is("punc", "}") || has_newline_before(S.token));
};
function semicolon(optional) {
@@ -795,7 +800,7 @@ function parse($TEXT, options) {
function embed_tokens(parser) {
return function() {
var start = S.token;
var expr = parser();
var expr = parser.apply(null, arguments);
var end = prev();
expr.start = start;
expr.end = end;
@@ -810,17 +815,17 @@ function parse($TEXT, options) {
}
};
var statement = embed_tokens(function() {
var statement = embed_tokens(function(strict_defun) {
handle_regexp();
switch (S.token.type) {
case "string":
if (S.in_directives) {
var token = peek();
if (S.token.raw.indexOf("\\") == -1
&& (token.nlb
|| is_token(token, "eof")
|| is_token(token, "punc", ";")
|| is_token(token, "punc", "}"))) {
&& (is_token(token, "punc", ";")
|| is_token(token, "punc", "}")
|| has_newline_before(token)
|| is_token(token, "eof"))) {
S.input.add_directive(S.token.value);
} else {
S.in_directives = false;
@@ -896,6 +901,9 @@ function parse($TEXT, options) {
return for_();
case "function":
if (!strict_defun && S.input.has_directive("use strict")) {
croak("In strict mode code, functions can only be declared at top level or immediately within another function.");
}
next();
return function_(AST_Defun);
@@ -927,7 +935,7 @@ function parse($TEXT, options) {
case "throw":
next();
if (S.token.nlb)
if (has_newline_before(S.token))
croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
@@ -1046,12 +1054,10 @@ function parse($TEXT, options) {
};
function for_in(init) {
var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
var obj = expression(true);
expect(")");
return new AST_ForIn({
init : init,
name : lhs,
object : obj,
body : in_loop(statement)
});
@@ -1078,7 +1084,7 @@ function parse($TEXT, options) {
S.input.push_directives_stack();
S.in_loop = 0;
S.labels = [];
var body = block_();
var body = block_(true);
if (S.input.has_directive("use strict")) {
if (name) strict_verify_symbol(name);
argnames.forEach(strict_verify_symbol);
@@ -1107,12 +1113,12 @@ function parse($TEXT, options) {
});
};
function block_() {
function block_(strict_defun) {
expect("{");
var a = [];
while (!is("punc", "}")) {
if (is("eof")) unexpected();
a.push(statement());
a.push(statement(strict_defun));
}
next();
return a;
@@ -1220,12 +1226,14 @@ function parse($TEXT, options) {
} else {
args = [];
}
return subscripts(new AST_New({
var call = new AST_New({
start : start,
expression : newexp,
args : args,
end : prev()
}), allow_calls);
});
mark_pure(call);
return subscripts(call, allow_calls);
};
function as_atom_node() {
@@ -1357,7 +1365,7 @@ function parse($TEXT, options) {
if (type == "name" && !is("punc", ":")) {
var key = new AST_SymbolAccessor({
start: S.token,
name: as_property_name(),
name: "" + as_property_name(),
end: prev()
});
if (name == "get") {
@@ -1383,7 +1391,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectKeyVal({
start : start,
quote : start.quote,
key : name,
key : "" + name,
value : expression(false),
end : prev()
}));
@@ -1503,7 +1511,7 @@ function parse($TEXT, options) {
return ex;
}
var val = expr_atom(allow_calls);
while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
while (is("operator") && UNARY_POSTFIX(S.token.value) && !has_newline_before(S.token)) {
val = make_unary(AST_UnaryPostfix, S.token, val);
val.start = start;
val.end = S.token;
@@ -1623,7 +1631,7 @@ function parse($TEXT, options) {
var body = [];
S.input.push_directives_stack();
while (!is("eof"))
body.push(statement());
body.push(statement(true));
S.input.pop_directives_stack();
var end = prev();
var toplevel = options.toplevel;

View File

@@ -110,12 +110,15 @@ function mangle_properties(ast, options) {
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var cache = options.cache;
if (cache == null) {
cache = {
cname: -1,
props: new Dictionary()
};
var cname = -1;
var cache;
if (options.cache) {
cache = options.cache.props;
cache.each(function(mangled_name) {
push_uniq(reserved, mangled_name);
});
} else {
cache = new Dictionary();
}
var regex = options.regex;
@@ -172,7 +175,7 @@ function mangle_properties(ast, options) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.props.has(name);
return cache.has(name);
}
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true;
@@ -181,7 +184,7 @@ function mangle_properties(ast, options) {
function should_mangle(name) {
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
return cache.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
@@ -199,7 +202,7 @@ function mangle_properties(ast, options) {
return name;
}
var mangled = cache.props.get(name);
var mangled = cache.get(name);
if (!mangled) {
if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
@@ -213,11 +216,11 @@ function mangle_properties(ast, options) {
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
do {
mangled = base54(++cache.cname);
mangled = base54(++cname);
} while (!can_mangle(mangled));
}
cache.props.set(name, mangled);
cache.set(name, mangled);
}
return mangled;
}

View File

@@ -43,9 +43,10 @@
"use strict";
function SymbolDef(scope, orig) {
function SymbolDef(scope, orig, init) {
this.name = orig.name;
this.orig = [ orig ];
this.init = init;
this.eliminated = 0;
this.scope = scope;
this.references = [];
@@ -150,7 +151,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
@@ -158,10 +159,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
(node.scope = defun.parent_scope).def_function(node);
(node.scope = defun.parent_scope).def_function(node, defun);
}
else if (node instanceof AST_SymbolVar) {
defun.def_variable(node);
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
@@ -241,10 +242,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
}));
}
if (options.cache) {
this.cname = options.cache.cname;
}
});
AST_Toplevel.DEFMETHOD("def_global", function(node){
@@ -306,27 +303,32 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
AST_Scope.DEFMETHOD("def_function", function(symbol, init){
var def = this.def_variable(symbol, init);
if (!def.init || def.init instanceof AST_Defun) def.init = init;
this.functions.set(symbol.name, def);
return def;
});
AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, symbol);
AST_Scope.DEFMETHOD("def_variable", function(symbol, init){
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) {
def.init = init;
}
} else {
def = new SymbolDef(this, symbol, init);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
}
return symbol.thedef = def;
});
AST_Scope.DEFMETHOD("next_mangled", function(options){
var ext = this.enclosed;
function next_mangled(scope, options) {
var ext = scope.enclosed;
out: while (true) {
var m = base54(++this.cname);
var m = base54(++scope.cname);
if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
@@ -343,6 +345,18 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
}
return m;
}
}
AST_Scope.DEFMETHOD("next_mangled", function(options){
return next_mangled(this, options);
});
AST_Toplevel.DEFMETHOD("next_mangled", function(options){
var name;
do {
name = next_mangled(this, options);
} while (member(name, this.mangled_names));
return name;
});
AST_Function.DEFMETHOD("next_mangled", function(options, def){
@@ -356,7 +370,7 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
var name = next_mangled(this, options);
if (!tricky_name || tricky_name != name)
return name;
}
@@ -407,8 +421,14 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var lname = -1;
var to_mangle = [];
var mangled_names = this.mangled_names = [];
if (options.cache) {
this.globals.each(collect);
if (options.cache.props) {
options.cache.props.each(function(mangled_name) {
push_uniq(mangled_names, mangled_name);
});
}
}
var tw = new TreeWalker(function(node, descend){
@@ -437,10 +457,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) });
if (options.cache) {
options.cache.cname = this.cname;
}
function collect(symbol) {
if (!member(symbol.name, options.reserved)) {
to_mangle.push(symbol);
@@ -448,59 +464,55 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
}
});
AST_Toplevel.DEFMETHOD("find_unique_prefix", function(options) {
var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props;
var prefixes = Object.create(null);
options.reserved.forEach(add_prefix);
var avoid = Object.create(null);
options.reserved.forEach(to_avoid);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
}));
var prefix, i = 0;
do {
prefix = create_name(i++);
} while (prefixes[prefix]);
return prefix;
return avoid;
function add_prefix(name) {
if (/[0-9]$/.test(name)) {
prefixes[name.replace(/[0-9]+$/, "")] = true;
}
function to_avoid(name) {
avoid[name] = true;
}
function add_def(def) {
var name = def.name;
if (def.global && cache && cache.has(name)) name = cache.get(name);
else if (!def.unmangleable(options)) return;
add_prefix(name);
}
function create_name(num) {
var name = "";
do {
name += letters[num % letters.length];
num = Math.floor(num / letters.length);
} while (num);
return name;
to_avoid(name);
}
});
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
base54.reset();
base54.sort();
options = this._default_mangler_options(options);
var prefix = this.find_unique_prefix(options);
var avoid = this.find_colliding_names(options);
var cname = 0;
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
}));
function next_name() {
var name;
do {
name = base54(cname++);
} while (avoid[name] || !is_identifier(name));
return name;
}
function rename(def) {
if (def.global || def.unmangleable(options)) return;
if (def.global && options.cache) return;
if (def.unmangleable(options)) return;
if (member(def.name, options.reserved)) return;
var d = def.redefined();
def.name = d ? d.name : prefix + def.id;
def.name = d ? d.name : next_name();
def.orig.forEach(function(sym) {
sym.name = def.name;
});

View File

@@ -303,6 +303,13 @@ Dictionary.prototype = {
ret.push(f(this._values[i], i.substr(1)));
return ret;
},
clone: function() {
var ret = new Dictionary();
for (var i in this._values)
ret._values[i] = this._values[i];
ret._size = this._size;
return ret;
},
toObject: function() { return this._values }
};
Dictionary.fromObject = function(obj) {

View File

@@ -4,20 +4,15 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.3.1",
"version": "3.3.10",
"engines": {
"node": ">=0.8.0"
},
"maintainers": [
"Alex Lam <alexlamsl@gmail.com>",
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
],
"repository": {
"type": "git",
"url": "https://github.com/mishoo/UglifyJS2.git"
},
"bugs": {
"url": "https://github.com/mishoo/UglifyJS2/issues"
},
"repository": "mishoo/UglifyJS2",
"main": "tools/node.js",
"bin": {
"uglifyjs": "bin/uglifyjs"
@@ -29,13 +24,13 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.12.1",
"commander": "~2.14.1",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.2.1",
"acorn": "~5.4.1",
"mocha": "~3.5.1",
"semver": "~5.4.1"
"semver": "~5.5.0"
},
"scripts": {
"test": "node test/run-tests.js"

View File

@@ -52,13 +52,8 @@ collapse_vars_side_effects_1: {
console.log.bind(console)(s.charAt(i++), s.charAt(i++), s.charAt(i++), 7);
}
function f2() {
var log = console.log.bind(console),
s = "abcdef",
i = 2,
x = s.charAt(i++),
y = s.charAt(i++),
z = s.charAt(i++);
log(x, i, y, z, 7);
var s = "abcdef", i = 2;
console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(i++), 7);
}
function f3() {
var s = "abcdef",
@@ -72,7 +67,7 @@ collapse_vars_side_effects_1: {
var i = 10,
x = i += 2,
y = i += 3;
console.log.bind(console)(x, i += 4, y, i);
console.log.bind(console)(x, i += 4, y, 19);
}
f1(), f2(), f3(), f4();
}
@@ -2197,6 +2192,7 @@ toplevel_single_reference: {
unused_orig: {
options = {
collapse_vars: true,
dead_code: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
@@ -3878,10 +3874,9 @@ recursive_function_replacement: {
console.log(f(c));
}
expect: {
function f(n) {
return x(y(f(n)));
}
console.log(f(c));
console.log(function n(o) {
return x(y(n(o)));
}(c));
}
}
@@ -4012,3 +4007,513 @@ replace_all_var: {
}
expect_stdout: "PASS"
}
cascade_statement: {
options = {
collapse_vars: true,
}
input: {
function f1(a, b) {
var c;
if (a)
return c = b, c || a;
else
c = a, c(b);
}
function f2(a, b) {
var c;
while (a)
c = b, a = c + b;
do
throw c = a + b, c;
while (c);
}
function f3(a, b) {
for (; a < b; a++)
if (c = a, c && b)
var c = (c = b(a), c);
}
}
expect: {
function f1(a, b) {
var c;
if (a)
return (c = b) || a;
else
(c = a)(b);
}
function f2(a, b) {
var c;
while (a)
a = (c = b) + b;
do
throw c = a + b;
while (c);
}
function f3(a, b) {
for (; a < b; a++)
if ((c = a) && b)
var c = c = b(a);
}
}
}
cascade_forin: {
options = {
collapse_vars: true,
}
input: {
var a;
function f(b) {
return [ b, b, b ];
}
for (var c in a = console, f(a))
console.log(c);
}
expect: {
var a;
function f(b) {
return [ b, b, b ];
}
for (var c in f(a = console))
console.log(c);
}
expect_stdout: [
"0",
"1",
"2",
]
}
unsafe_builtin: {
options = {
collapse_vars: true,
pure_getters: "strict",
unsafe: true,
unused: true,
}
input: {
function f(a) {
var b = Math.abs(a);
return Math.pow(b, 2);
}
console.log(f(-1), f(2));
}
expect: {
function f(a) {
return Math.pow(Math.abs(a), 2);
}
console.log(f(-1), f(2));
}
expect_stdout: "1 4"
}
return_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var log = console.log;
function f(b, c) {
var a = c;
if (b) return b;
log(a);
}
f(false, 1);
f(true, 2);
}
expect: {
var log = console.log;
function f(b, c) {
if (b) return b;
log(c);
}
f(false, 1);
f(true, 2);
}
expect_stdout: "1"
}
return_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var log = console.log;
function f(b, c) {
var a = c();
if (b) return b;
log(a);
}
f(false, function() { return 1 });
f(true, function() { return 2 });
}
expect: {
var log = console.log;
function f(b, c) {
var a = c();
if (b) return b;
log(a);
}
f(false, function() { return 1 });
f(true, function() { return 2 });
}
expect_stdout: "1"
}
return_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var log = console.log;
function f(b, c) {
var a = b <<= c;
if (b) return b;
log(a);
}
f(false, 1);
f(true, 2);
}
expect: {
var log = console.log;
function f(b, c) {
var a = b <<= c;
if (b) return b;
log(a);
}
f(false, 1);
f(true, 2);
}
expect_stdout: "0"
}
return_4: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(function(b) {
a = "PASS";
return;
b(a);
})();
console.log(a);
}
expect: {
var a = "FAIL";
(function(b) {
a = "PASS";
return;
b(a);
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_2858: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var b;
(function() {
function f() {
a++;
}
f();
var c = f();
var a = void 0;
c || (b = a);
})();
console.log(b);
}
expect: {
var b;
(function() {
function f() {
a++;
}
f();
var c = f();
var a = void 0;
c || (b = a);
})();
console.log(b);
}
expect_stdout: "undefined"
}
cond_branch_1: {
options = {
collapse_vars: true,
sequences: true,
unused: true,
}
input: {
function f1(b, c) {
var log = console.log;
var a = ++c;
if (b) b++;
log(a, b);
}
function f2(b, c) {
var log = console.log;
var a = ++c;
b && b++;
log(a, b);
}
function f3(b, c) {
var log = console.log;
var a = ++c;
b ? b++ : b--;
log(a, b);
}
f1(1, 2);
f2(3, 4);
f3(5, 6);
}
expect: {
function f1(b, c) {
if (b) b++;
(0, console.log)(++c, b);
}
function f2(b, c) {
b && b++,
(0, console.log)(++c, b);
}
function f3(b, c) {
b ? b++ : b--,
(0, console.log)(++c, b);
}
f1(1, 2),
f2(3, 4),
f3(5, 6);
}
expect_stdout: [
"3 2",
"5 4",
"7 6",
]
}
cond_branch_2: {
options = {
collapse_vars: true,
sequences: true,
unused: true,
}
input: {
function f1(b, c) {
var log = console.log;
var a = ++c;
if (b) b += a;
log(a, b);
}
function f2(b, c) {
var log = console.log;
var a = ++c;
b && (b += a);
log(a, b);
}
function f3(b, c) {
var log = console.log;
var a = ++c;
b ? b += a : b--;
log(a, b);
}
f1(1, 2);
f2(3, 4);
f3(5, 6);
}
expect: {
function f1(b, c) {
var a = ++c;
if (b) b += a;
(0, console.log)(a, b);
}
function f2(b, c) {
var a = ++c;
b && (b += a),
(0, console.log)(a, b);
}
function f3(b, c) {
var a = ++c;
b ? b += a : b--,
(0, console.log)(a, b);
}
f1(1, 2),
f2(3, 4),
f3(5, 6);
}
expect_stdout: [
"3 4",
"5 8",
"7 12",
]
}
cond_branch_switch: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
if (c = 1 + c, 0) switch (c = 1 + c) {
}
console.log(c);
}
expect: {
var c = 0;
if (c = 1 + c, 0) switch (c = 1 + c) {
}
console.log(c);
}
expect_stdout: "1"
}
issue_2873_1: {
options = {
collapse_vars: true,
}
input: {
var b = 1, c = 0;
do {
c++;
if (!--b) break;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect: {
var b = 1, c = 0;
do {
c++;
if (!--b) break;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect_stdout: "0 1"
}
issue_2873_2: {
options = {
collapse_vars: true,
}
input: {
var b = 1, c = 0;
do {
c++;
if (!--b) continue;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect: {
var b = 1, c = 0;
do {
c++;
if (!--b) continue;
c = 1 + c;
} while (0);
console.log(b, c);
}
expect_stdout: "0 1"
}
issue_2878: {
options = {
collapse_vars: true,
sequences: true,
}
input: {
var c = 0;
(function (a, b) {
function f2() {
if (a) c++;
}
b = f2();
a = 1;
b && b.b;
f2();
})();
console.log(c);
}
expect: {
var c = 0;
(function (a, b) {
function f2() {
if (a) c++;
}
b = f2(),
a = 1,
b && b.b,
f2();
})(),
console.log(c);
}
expect_stdout: "1"
}
issue_2891_1: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS", b;
try {
b = c.p = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect: {
var a = "PASS", b;
try {
b = c.p = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect_stdout: "PASS"
}
issue_2891_2: {
options = {
collapse_vars: true,
}
input: {
"use strict";
var a = "PASS", b;
try {
b = c = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect: {
"use strict";
var a = "PASS", b;
try {
b = c = 0;
a = "FAIL";
b();
} catch (e) {
}
console.log(a);
}
expect_stdout: true
}

View File

@@ -112,3 +112,206 @@ self_comparison_2: {
}
expect_stdout: "false true"
}
issue_2857_1: {
options = {
comparisons: true,
}
input: {
function f1(a) {
a === undefined || a === null;
a === undefined || a !== null;
a !== undefined || a === null;
a !== undefined || a !== null;
a === undefined && a === null;
a === undefined && a !== null;
a !== undefined && a === null;
a !== undefined && a !== null;
}
function f2(a) {
a === null || a === undefined;
a === null || a !== undefined;
a !== null || a === undefined;
a !== null || a !== undefined;
a === null && a === undefined;
a === null && a !== undefined;
a !== null && a === undefined;
a !== null && a !== undefined;
}
}
expect: {
function f1(a) {
null == a;
void 0 === a || null !== a;
void 0 !== a || null === a;
void 0 !== a || null !== a;
void 0 === a && null === a;
void 0 === a && null !== a;
void 0 !== a && null === a;
null != a;
}
function f2(a) {
null == a;
null === a || void 0 !== a;
null !== a || void 0 === a;
null !== a || void 0 !== a;
null === a && void 0 === a;
null === a && void 0 !== a;
null !== a && void 0 === a;
null != a;
}
}
}
issue_2857_2: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
a === undefined || a === null || p;
a === undefined || a !== null || p;
a !== undefined || a === null || p;
a !== undefined || a !== null || p;
a === undefined && a === null || p;
a === undefined && a !== null || p;
a !== undefined && a === null || p;
a !== undefined && a !== null || p;
}
}
expect: {
function f(a, p) {
null == a || p;
void 0 === a || null !== a || p;
void 0 !== a || null === a || p;
void 0 !== a || null !== a || p;
void 0 === a && null === a || p;
void 0 === a && null !== a || p;
void 0 !== a && null === a || p;
null != a || p;
}
}
}
issue_2857_3: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
a === undefined || a === null && p;
a === undefined || a !== null && p;
a !== undefined || a === null && p;
a !== undefined || a !== null && p;
a === undefined && a === null && p;
a === undefined && a !== null && p;
a !== undefined && a === null && p;
a !== undefined && a !== null && p;
}
}
expect: {
function f(a, p) {
void 0 === a || null === a && p;
void 0 === a || null !== a && p;
void 0 !== a || null === a && p;
void 0 !== a || null !== a && p;
void 0 === a && null === a && p;
void 0 === a && null !== a && p;
void 0 !== a && null === a && p;
null != a && p;
}
}
}
issue_2857_4: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
p || a === undefined || a === null;
p || a === undefined || a !== null;
p || a !== undefined || a === null;
p || a !== undefined || a !== null;
p || a === undefined && a === null;
p || a === undefined && a !== null;
p || a !== undefined && a === null;
p || a !== undefined && a !== null;
}
}
expect: {
function f(a, p) {
p || null == a;
p || void 0 === a || null !== a;
p || void 0 !== a || null === a;
p || void 0 !== a || null !== a;
p || void 0 === a && null === a;
p || void 0 === a && null !== a;
p || void 0 !== a && null === a;
p || null != a;
}
}
}
issue_2857_5: {
options = {
comparisons: true,
}
input: {
function f(a, p) {
p && a === undefined || a === null;
p && a === undefined || a !== null;
p && a !== undefined || a === null;
p && a !== undefined || a !== null;
p && a === undefined && a === null;
p && a === undefined && a !== null;
p && a !== undefined && a === null;
p && a !== undefined && a !== null;
}
}
expect: {
function f(a, p) {
p && void 0 === a || null === a;
p && void 0 === a || null !== a;
p && void 0 !== a || null === a;
p && void 0 !== a || null !== a;
p && void 0 === a && null === a;
p && void 0 === a && null !== a;
p && void 0 !== a && null === a;
p && null != a;
}
}
}
issue_2857_6: {
options = {
comparisons: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
function f(a) {
if (({}).b === undefined || {}.b === null)
return a.b !== undefined && a.b !== null;
}
console.log(f({
a: [ null ],
get b() {
return this.a.shift();
}
}));
}
expect: {
function f(a) {
if (null == {}.b)
return void 0 !== a.b && null !== a.b;
}
console.log(f({
a: [ null ],
get b() {
return this.a.shift();
}
}));
}
expect_stdout: "true"
}

View File

@@ -1203,3 +1203,67 @@ issue_2560: {
"2",
]
}
hoist_decl: {
options = {
conditionals: true,
join_vars: true,
sequences: true,
}
input: {
if (x()) {
var a;
y();
} else {
z();
var b;
}
}
expect: {
var a, b;
x() ? y() : z();
}
}
to_and_or: {
options = {
conditionals: true,
}
input: {
var values = [
0,
null,
true,
"foo",
false,
-1 / 0,
void 0,
];
values.forEach(function(x) {
values.forEach(function(y) {
values.forEach(function(z) {
console.log(x ? y || z : z);
});
});
});
}
expect: {
var values = [
0,
null,
true,
"foo",
false,
-1 / 0,
void 0,
];
values.forEach(function(x) {
values.forEach(function(y) {
values.forEach(function(z) {
console.log(x && y || z);
});
});
});
}
expect_stdout: true
}

View File

@@ -62,46 +62,6 @@ dead_code_2_should_warn: {
node_version: "<=4"
}
dead_code_2_should_warn_strict: {
options = {
dead_code: true
};
input: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
f();
}
expect: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
}
expect_stdout: true
node_version: ">=4"
}
dead_code_constant_boolean_should_warn_more: {
options = {
dead_code : true,
@@ -137,42 +97,6 @@ dead_code_constant_boolean_should_warn_more: {
node_version: "<=4"
}
dead_code_constant_boolean_should_warn_more_strict: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
side_effects : true,
};
input: {
"use strict";
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var foo;
function bar() {}
}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
bar();
}
expect: {
"use strict";
var foo;
// nothing for the while
// as for the for, it should keep:
var moo;
var x = 10, y;
bar();
}
expect_stdout: true
node_version: ">=4"
}
try_catch_finally: {
options = {
conditionals: true,
@@ -828,3 +752,168 @@ issue_2597: {
}
expect_stdout: "PASS"
}
issue_2666: {
options = {
dead_code: true,
}
input: {
function f(a) {
return a = {
p: function() {
return a;
}
};
}
console.log(typeof f().p());
}
expect: {
function f(a) {
return a = {
p: function() {
return a;
}
};
}
console.log(typeof f().p());
}
expect_stdout: "object"
}
issue_2692: {
options = {
dead_code: true,
reduce_vars: false,
}
input: {
function f(a) {
return a = g;
function g() {
return a;
}
}
console.log(typeof f()());
}
expect: {
function f(a) {
return a = g;
function g() {
return a;
}
}
console.log(typeof f()());
}
expect_stdout: "function"
}
issue_2701: {
options = {
dead_code: true,
inline: false,
}
input: {
function f(a) {
return a = function() {
return function() {
return a;
};
}();
}
console.log(typeof f()());
}
expect: {
function f(a) {
return a = function() {
return function() {
return a;
};
}();
}
console.log(typeof f()());
}
expect_stdout: "function"
}
issue_2749: {
options = {
dead_code: true,
inline: true,
toplevel: true,
unused: true,
}
input: {
var a = 2, c = "PASS";
while (a--)
(function() {
return b ? c = "FAIL" : b = 1;
try {
} catch (b) {
var b;
}
})();
console.log(c);
}
expect: {
var a = 2, c = "PASS";
while (a--)
b = void 0, b ? c = "FAIL" : b = 1;
var b;
console.log(c);
}
expect_stdout: "PASS"
}
unsafe_builtin: {
options = {
side_effects: true,
unsafe: true,
}
input: {
(!w).constructor(x);
Math.abs(y);
[ 1, 2, z ].valueOf();
}
expect: {
w, x;
y;
z;
}
}
issue_2860_1: {
options = {
dead_code: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
}());
}
expect: {
console.log(function(a) {
return 1 ^ a;
}());
}
expect_stdout: "1"
}
issue_2860_2: {
options = {
dead_code: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
}());
}
expect: {
console.log(1);
}
expect_stdout: "1"
}

View File

@@ -1413,3 +1413,309 @@ issue_2516_2: {
Baz(2);
}
}
defun_lambda_same_name: {
options = {
toplevel: true,
unused: true,
}
input: {
function f(n) {
return n ? n * f(n - 1) : 1;
}
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect: {
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect_stdout: "120"
}
issue_2660_1: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 2;
function f(b) {
return b && f() || a--;
}
f(1);
console.log(a);
}
expect: {
var a = 2;
(function f(b) {
return b && f() || a--;
})(1);
console.log(a);
}
expect_stdout: "1"
}
issue_2660_2: {
options = {
collapse_vars: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
function f(b) {
b && f();
--a, a.toString();
}
f();
console.log(a);
}
expect: {
var a = 1;
(function f(b) {
b && f(),
(--a).toString();
})(),
console.log(a);
}
expect_stdout: "0"
}
issue_2665: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var a = 1;
function g() {
a-- && g();
}
typeof h == "function" && h();
function h() {
typeof g == "function" && g();
}
console.log(a);
}
expect: {
var a = 1;
!function g() {
a-- && g();
}();
console.log(a);
}
expect_stdout: "-1"
}
double_assign_1: {
options = {
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var a = {};
var a = [];
return a;
}
function f2() {
var a = {};
a = [];
return a;
}
function f3() {
a = {};
var a = [];
return a;
}
function f4(a) {
a = {};
a = [];
return a;
}
function f5(a) {
var a = {};
a = [];
return a;
}
function f6(a) {
a = {};
var a = [];
return a;
}
console.log(f1(), f2(), f3(), f4(), f5(), f6());
}
expect: {
function f1() {
return [];
}
function f2() {
var a;
a = [];
return a;
}
function f3() {
return [];
}
function f4(a) {
a = [];
return a;
}
function f5(a) {
a = [];
return a;
}
function f6(a) {
a = [];
return a;
}
console.log(f1(), f2(), f3(), f4(), f5(), f6());
}
expect_stdout: true
}
double_assign_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 2; i++)
a = void 0, a = {}, console.log(a);
var a;
}
expect: {
for (var i = 0; i < 2; i++)
void 0, a = {}, console.log(a);
var a;
}
}
double_assign_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 2; i++)
a = void 0, a = { a: a }, console.log(a);
var a;
}
expect: {
for (var i = 0; i < 2; i++)
a = void 0, a = { a: a }, console.log(a);
var a;
}
}
cascade_drop_assign: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a, b = a = "PASS";
console.log(b);
}
expect: {
var b = "PASS";
console.log(b);
}
expect_stdout: "PASS"
}
chained_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect: {
console.log(function(a, b) {
var c = b;
b++;
return c;
}(0, 2));
}
expect_stdout: "2"
}
issue_2768: {
options = {
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL", c = 1;
var c = function(b) {
var d = b = a;
var e = --b + (d && (a = "PASS"));
}();
console.log(a, typeof c);
}
expect: {
var a = "FAIL";
var c = (d = a, 0, void (d && (a = "PASS")));
var d;
console.log(a, typeof c);
}
expect_stdout: "PASS undefined"
}
issue_2846: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
var a = 0;
b && b(a);
return a++;
}
var c = f();
console.log(c);
}
expect: {
var c = function(a, b) {
a = 0;
b && b(a);
return a++;
}();
console.log(c);
}
expect_stdout: "0"
}

View File

@@ -1194,6 +1194,9 @@ issue_2231_1: {
console.log(Object.keys(void 0));
}
expect_stdout: true
expect_warnings: [
"WARN: Error evaluating Object.keys(void 0) [test/compress/evaluate.js:1191,20]",
]
}
issue_2231_2: {
@@ -1208,6 +1211,23 @@ issue_2231_2: {
console.log(Object.getOwnPropertyNames(null));
}
expect_stdout: true
expect_warnings: [
"WARN: Error evaluating Object.getOwnPropertyNames(null) [test/compress/evaluate.js:1208,20]",
]
}
issue_2231_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.keys({ foo: "bar" })[0]);
}
expect: {
console.log("foo");
}
expect_stdout: "foo"
}
self_comparison_1: {
@@ -1330,13 +1350,82 @@ issue_2535_3: {
}
expect_stdout: true
expect_warnings: [
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1316,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1317,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1318,20]",
"WARN: Condition left of && always false [test/compress/evaluate.js:1318,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1319,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1320,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1321,20]",
"WARN: Condition left of || always true [test/compress/evaluate.js:1321,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1336,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1337,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1338,20]",
"WARN: Condition left of && always false [test/compress/evaluate.js:1338,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1339,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1340,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1341,20]",
"WARN: Condition left of || always true [test/compress/evaluate.js:1341,20]",
]
}
issue_2822: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([ function() {}, "PASS", "FAIL" ][1]);
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
string_case: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("İ".toLowerCase().charCodeAt(0));
console.log("I".toLowerCase().charCodeAt(0));
console.log("Ş".toLowerCase().charCodeAt(0));
console.log("Ğ".toLowerCase().charCodeAt(0));
console.log("Ü".toLowerCase().charCodeAt(0));
console.log("Ö".toLowerCase().charCodeAt(0));
console.log("Ç".toLowerCase().charCodeAt(0));
console.log("i".toUpperCase().charCodeAt(0));
console.log("ı".toUpperCase().charCodeAt(0));
console.log("ş".toUpperCase().charCodeAt(0));
console.log("ğ".toUpperCase().charCodeAt(0));
console.log("ü".toUpperCase().charCodeAt(0));
console.log("ö".toUpperCase().charCodeAt(0));
console.log("ç".toUpperCase().charCodeAt(0));
}
expect: {
console.log(105);
console.log(105);
console.log(351);
console.log(287);
console.log(252);
console.log(246);
console.log(231);
console.log(73);
console.log(73);
console.log(350);
console.log(286);
console.log(220);
console.log(214);
console.log(199);
}
expect_stdout: [
"105",
"105",
"351",
"287",
"252",
"246",
"231",
"73",
"73",
"350",
"286",
"220",
"214",
"199",
]
}

View File

@@ -214,51 +214,11 @@ hoist_funs: {
node_version: "<=4"
}
hoist_funs_strict: {
options = {
hoist_funs: true,
}
input: {
"use strict";
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
"use strict";
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'undefined' 'function'",
]
node_version: ">=4"
}
issue_203: {
options = {
keep_fargs: false,
side_effects: true,
unsafe_Func: true,
unsafe_Function: true,
unused: true,
}
input: {
@@ -800,12 +760,12 @@ issue_2601_1: {
expect: {
var a = "FAIL";
(function() {
var b;
b = "foo",
function(b) {
b && b();
}(),
b && (a = "PASS");
var b;
})(),
console.log(a);
}
@@ -1285,7 +1245,7 @@ issue_2630_1: {
expect: {
var c = 0;
(function() {
while (c++, void (c = 1 + c));
while (void (c = 1 + ++c));
})(),
console.log(c);
}
@@ -1299,6 +1259,7 @@ issue_2630_2: {
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
@@ -1316,7 +1277,7 @@ issue_2630_2: {
expect: {
var c = 0;
!function() {
while (c += 1, void (c = 1 + c));
while (void (c = 1 + (c += 1)));
}(), console.log(c);
}
expect_stdout: "2"
@@ -1345,11 +1306,10 @@ issue_2630_3: {
expect: {
var x = 2, a = 1;
(function() {
function f1(a) {
(function f1(a) {
f2();
--x >= 0 && f1({});
}
f1(a++);
})(a++);
function f2() {
a++;
}
@@ -1417,14 +1377,14 @@ issue_2630_5: {
!function() {
do {
c *= 10;
} while (c += 3, (c = 2 + c) < 100);
} while ((c = 2 + (c += 3)) < 100);
}();
console.log(c);
}
expect_stdout: "155"
}
recursive_inline: {
recursive_inline_1: {
options = {
inline: true,
reduce_funcs: true,
@@ -1447,3 +1407,585 @@ recursive_inline: {
}
expect: {}
}
recursive_inline_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(n) {
return n ? n * f(n - 1) : 1;
}
console.log(f(5));
}
expect: {
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect_stdout: "120"
}
issue_2657: {
options = {
inline: true,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
"use strict";
console.log(function f() {
return h;
function g(b) {
return b || b();
}
function h(a) {
g(a);
return a;
}
}()(42));
}
expect: {
"use strict";
console.log(function(a) {
return b = a, b || b(), a;
var b;
}(42));
}
expect_stdout: "42"
}
issue_2663_1: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var i, o = {};
function createFn(j) {
return function() {
console.log(j);
};
}
for (i in { a: 1, b: 2, c: 3 })
o[i] = createFn(i);
for (i in o)
o[i]();
})();
}
expect: {
(function() {
var i, o = {};
function createFn(j) {
return function() {
console.log(j);
};
}
for (i in { a: 1, b: 2, c: 3 })
o[i] = createFn(i);
for (i in o)
o[i]();
})();
}
expect_stdout: [
"a",
"b",
"c",
]
}
issue_2663_2: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var i;
function fn(j) {
return function() {
console.log(j);
}();
}
for (i in { a: 1, b: 2, c: 3 })
fn(i);
})();
}
expect: {
(function() {
var i;
for (i in { a: 1, b: 2, c: 3 })
j = i, console.log(j);
var j;
})();
}
expect_stdout: [
"a",
"b",
"c",
]
}
issue_2663_3: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function () {
var outputs = [
{ type: 0, target: null, eventName: "ngSubmit", propName: null },
{ type: 0, target: null, eventName: "submit", propName: null },
{ type: 0, target: null, eventName: "reset", propName: null },
];
function listenToElementOutputs(outputs) {
var handlers = [];
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var handleEventClosure = renderEventHandlerClosure(output.eventName);
handlers.push(handleEventClosure)
}
var target, name;
return handlers;
}
function renderEventHandlerClosure(eventName) {
return function () {
return console.log(eventName);
};
}
listenToElementOutputs(outputs).forEach(function (handler) {
return handler()
});
})();
}
expect: {
(function() {
function renderEventHandlerClosure(eventName) {
return function() {
return console.log(eventName);
};
}
(function(outputs) {
var handlers = [];
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var handleEventClosure = renderEventHandlerClosure(output.eventName);
handlers.push(handleEventClosure);
}
return handlers;
})([ {
type: 0,
target: null,
eventName: "ngSubmit",
propName: null
}, {
type: 0,
target: null,
eventName: "submit",
propName: null
}, {
type: 0,
target: null,
eventName: "reset",
propName: null
} ]).forEach(function(handler) {
return handler();
});
})();
}
expect_stdout: [
"ngSubmit",
"submit",
"reset",
]
}
duplicate_argnames: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
function f(b, b, b) {
b && (a = "FAIL");
}
f(0, console);
console.log(a);
}
expect: {
var a = "PASS";
console, b && (a = "FAIL");
var b;
console.log(a);
}
expect_stdout: "PASS"
}
loop_init_arg: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
for (var k in "12") (function (b) {
(b >>= 1) && (a = "FAIL"), b = 2;
})();
console.log(a);
}
expect: {
var a = "PASS";
for (var k in "12")
b = void 0, (b >>= 1) && (a = "FAIL"), b = 2;
var b;
console.log(a);
}
expect_stdout: "PASS"
}
inline_false: {
options = {
inline: false,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_0: {
options = {
inline: 0,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_1: {
options = {
inline: 1,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_2: {
options = {
inline: 2,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
a = 2, console.log(a);
var a;
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_3: {
options = {
inline: 3,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
a = 2, console.log(a);
var a;
b = 3, c = b, console.log(c);
var b, c;
}
expect_stdout: [
"1",
"2",
"3",
]
}
inline_true: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
(function() {
console.log(1);
})();
(function(a) {
console.log(a);
})(2);
(function(b) {
var c = b;
console.log(c);
})(3);
}
expect: {
console.log(1);
a = 2, console.log(a);
var a;
b = 3, c = b, console.log(c);
var b, c;
}
expect_stdout: [
"1",
"2",
"3",
]
}
use_before_init_in_loop: {
options = {
inline: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
for (var b = 2; --b >= 0;) (function() {
var c = function() {
return 1;
}(c && (a = "FAIL"));
})();
console.log(a);
}
expect: {
var a = "PASS";
for (var b = 2; --b >= 0;)
c = void 0, c = (c && (a = "FAIL"), 1);
var c;
console.log(a);
}
expect_stdout: "PASS"
}
duplicate_arg_var: {
options = {
inline: true,
toplevel: true,
}
input: {
console.log(function(b) {
return b;
var b;
}("PASS"));
}
expect: {
console.log((b = "PASS", b));
var b;
}
expect_stdout: "PASS"
}
issue_2737_1: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
while (a());
})(function f() {
console.log(typeof f);
});
}
expect: {
(function(a) {
while (a());
})(function f() {
console.log(typeof f);
});
}
expect_stdout: "function"
}
issue_2737_2: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(bar) {
for (;bar(); ) break;
})(function qux() {
return console.log("PASS"), qux;
});
}
expect: {
(function(bar) {
for (;bar(); ) break;
})(function qux() {
return console.log("PASS"), qux;
});
}
expect_stdout: "PASS"
}
issue_2783: {
options = {
collapse_vars: true,
conditionals: true,
if_return: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
return g;
function f(a) {
var b = a.b;
if (b) return b;
return a;
}
function g(o, i) {
while (i--) {
console.log(f(o));
}
}
})()({ b: "PASS" }, 1);
}
expect: {
(function() {
return function(o,i) {
while (i--) console.log(f(o));
};
function f(a) {
var b = a.b;
return b || a;
}
})()({ b: "PASS" },1);
}
expect_stdout: "PASS"
}

View File

@@ -664,3 +664,25 @@ issue_2519: {
}
expect_stdout: "5.5"
}
undefined_key: {
options = {
evaluate: true,
hoist_props: true,
join_vars: true,
passes: 4,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a, o = {};
o[a] = 1;
o.b = 2;
console.log(o[a] + o.b);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}

View File

@@ -326,3 +326,73 @@ issue_512: {
}
}
}
if_var_return: {
options = {
conditionals: true,
if_return: true,
join_vars: true,
sequences: true,
}
input: {
function f() {
var a;
return;
var b;
}
function g() {
var a;
if (u()) {
var b;
return v();
var c;
}
var d;
if (w()) {
var e;
return x();
var f;
} else {
var g;
y();
var h;
}
var i;
z();
var j;
}
}
expect: {
function f() {
var a, b;
}
function g() {
var a, b, c, d, e, f, g, h, i, j;
return u() ? v() : w() ? x() : (y(), z(), void 0);
}
}
}
if_if_return_return: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f(a, b) {
if (a) {
if (b)
return b;
return;
}
g();
}
}
expect: {
function f(a, b) {
if (a)
return b || void 0;
g();
}
}
}

View File

@@ -4,7 +4,7 @@ unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true
unsafe_undefined: true,
}
mangle = {}
input: {
@@ -30,7 +30,7 @@ keep_fnames: {
options = {
conditionals: true,
if_return: true,
unsafe: true
unsafe_undefined: true,
}
mangle = {
keep_fnames: true

View File

@@ -61,7 +61,7 @@ unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true,
unsafe_undefined: true,
}
mangle = {}
input: {

View File

@@ -84,12 +84,12 @@ numeric_literal: {
' 0: 0,',
' "-0": 1,',
' 42: 2,',
' "42": 3,',
' 42: 3,',
' 37: 4,',
' o: 5,',
' 1e42: 6,',
' b: 7,',
' "1e+42": 8',
' 1e42: 8',
'};',
'',
'console.log(obj[-0], obj[-""], obj["-0"]);',

View File

@@ -0,0 +1,33 @@
insert_semicolon: {
beautify = {
beautify: true,
comments: "all",
}
input: {
var a
/* foo */ var b
}
expect_exact: [
"var a",
"/* foo */;",
"",
"var b;",
]
}
unary_postfix: {
beautify = {
beautify: true,
comments: "all",
}
input: {
a
/* foo */++b
}
expect_exact: [
"a",
"/* foo */;",
"",
"++b;",
]
}

View File

@@ -64,3 +64,27 @@ strings_concat: {
);
}
}
regexp: {
options = {
evaluate: true,
unsafe: true,
}
input: {
RegExp("foo");
RegExp("bar", "ig");
RegExp(foo);
RegExp("bar", ig);
RegExp("should", "fail");
}
expect: {
/foo/;
/bar/ig;
RegExp(foo);
RegExp("bar", ig);
RegExp("should", "fail");
}
expect_warnings: [
'WARN: Error converting RegExp("should","fail") [test/compress/issue-269.js:78,2]',
]
}

View File

@@ -0,0 +1,32 @@
warn: {
options = {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return g();
}
function g() {
return g["call" + "er"].arguments;
}
// 3
console.log(f(1, 2, 3).length);
}
expect: {
// TypeError: Cannot read property 'arguments' of null
console.log(function g() {
return g.caller.arguments;
}().length);
}
expect_warnings: [
"WARN: Function.protoype.caller not supported [test/compress/issue-2719.js:17,19]",
"WARN: Function.protoype.arguments not supported [test/compress/issue-2719.js:17,19]",
]
}

View File

@@ -0,0 +1,37 @@
comparison_with_undefined: {
options = {
comparisons: true,
}
input: {
a == undefined;
a != undefined;
a === undefined;
a !== undefined;
undefined == a;
undefined != a;
undefined === a;
undefined !== a;
void 0 == a;
void 0 != a;
void 0 === a;
void 0 !== a;
}
expect: {
null == a;
null != a;
void 0 === a;
void 0 !== a;
null == a;
null != a;
void 0 === a;
void 0 !== a;
null == a;
null != a;
void 0 === a;
void 0 !== a;
}
}

View File

@@ -492,3 +492,116 @@ dead_code_condition: {
}
expect_stdout: "1"
}
issue_2740_1: {
options = {
dead_code: true,
loops: true,
}
input: {
for (; ; ) break;
for (a(); ; ) break;
for (; b(); ) break;
for (c(); d(); ) break;
for (; ; e()) break;
for (f(); ; g()) break;
for (; h(); i()) break;
for (j(); k(); l()) break;
}
expect: {
a();
b();
c();
d();
f();
h();
j();
k();
}
}
issue_2740_2: {
options = {
dead_code: true,
loops: true,
passes: 2,
}
input: {
L1: while (x()) {
break L1;
}
}
expect: {
x();
}
}
issue_2740_3: {
options = {
dead_code: true,
loops: true,
}
input: {
L1: for (var x = 0; x < 3; x++) {
L2: for (var y = 0; y < 2; y++) {
break L1;
}
}
console.log(x, y);
}
expect: {
L1: for (var x = 0; x < 3; x++)
for (var y = 0; y < 2; y++)
break L1;
console.log(x, y);
}
expect_stdout: "0 0"
}
issue_2740_4: {
options = {
dead_code: true,
loops: true,
passes: 2,
}
input: {
L1: for (var x = 0; x < 3; x++) {
L2: for (var y = 0; y < 2; y++) {
break L2;
}
}
console.log(x, y);
}
expect: {
for (var x = 0; x < 3; x++) {
var y = 0;
y < 2;
}
console.log(x, y);
}
expect_stdout: "3 0"
}
issue_2740_5: {
options = {
dead_code: true,
loops: true,
passes: 2,
}
input: {
L1: for (var x = 0; x < 3; x++) {
break L1;
L2: for (var y = 0; y < 2; y++) {
break L2;
}
}
console.log(x, y);
}
expect: {
var x = 0;
x < 3;
var y;
console.log(x,y);
}
expect_stdout: "0 undefined"
}

View File

@@ -567,16 +567,41 @@ native_prototype: {
}
input: {
Array.prototype.splice.apply(a, [1, 2, b, c]);
Function.prototype.call.apply(console.log, console, [ "foo" ]);
Number.prototype.toFixed.call(Math.PI, 2);
Object.prototype.hasOwnProperty.call(d, "foo");
RegExp.prototype.test.call(/foo/, "bar");
String.prototype.indexOf.call(e, "bar");
}
expect: {
[].splice.apply(a, [1, 2, b, c]);
(function() {}).call.apply(console.log, console, [ "foo" ]);
0..toFixed.call(Math.PI, 2);
({}).hasOwnProperty.call(d, "foo");
/t/.test.call(/foo/, "bar");
"".indexOf.call(e, "bar");
}
}
native_prototype_lhs: {
options = {
unsafe_proto: true,
}
input: {
console.log(function() {
Function.prototype.bar = "PASS";
return function() {};
}().bar);
}
expect: {
console.log(function() {
Function.prototype.bar = "PASS";
return function() {};
}().bar);
}
expect_stdout: "PASS"
}
accessor_boolean: {
input: {
var a = 1;
@@ -1094,3 +1119,472 @@ const_prop_assign_pure: {
x();
}
}
join_object_assignments_1: {
options = {
evaluate: true,
join_vars: true,
}
input: {
console.log(function() {
var x = {
a: 1,
c: (console.log("c"), "C"),
};
x.b = 2;
x[3] = function() {
console.log(x);
},
x["a"] = /foo/,
x.bar = x;
return x;
}());
}
expect: {
console.log(function() {
var x = {
a: 1,
c: (console.log("c"), "C"),
b: 2,
3: function() {
console.log(x);
},
a: /foo/,
};
x.bar = x;
return x;
}());
}
expect_stdout: true
}
join_object_assignments_2: {
options = {
evaluate: true,
hoist_props: true,
join_vars: true,
passes: 3,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
foo: 1,
};
o.bar = 2;
o.baz = 3;
console.log(o.foo, o.bar + o.bar, o.foo * o.bar * o.baz);
}
expect: {
console.log(1, 4, 6);
}
expect_stdout: "1 4 6"
}
join_object_assignments_3: {
options = {
evaluate: true,
join_vars: true,
}
input: {
console.log(function() {
var o = {
a: "PASS",
}, a = o.a;
o.a = "FAIL";
return a;
}());
}
expect: {
console.log(function() {
var o = {
a: "PASS",
}, a = o.a;
o.a = "FAIL";
return a;
}());
}
expect_stdout: "PASS"
}
join_object_assignments_return_1: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = "foo";
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: "foo"
};
return o.q;
}());
}
expect_stdout: "foo"
}
join_object_assignments_return_2: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = /foo/,
o.r = "bar";
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: /foo/,
r: "bar"
};
return o.r;
}());
}
expect_stdout: "bar"
}
join_object_assignments_return_3: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
return o.q = "foo",
o.p += "",
console.log(o.q),
o.p;
}());
}
expect: {
console.log(function() {
var o = {
p: 3,
q: "foo"
};
return o.p += "",
console.log(o.q),
o.p;
}());
}
expect_stdout: [
"foo",
"3",
]
}
join_object_assignments_for: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {
p: 3
};
for (o.q = "foo"; console.log(o.q););
return o.p;
}());
}
expect: {
console.log(function() {
for (var o = {
p: 3,
q: "foo"
}; console.log(o.q););
return o.p;
}());
}
expect_stdout: [
"foo",
"3",
]
}
join_object_assignments_if: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {};
if (o.a = "PASS") return o.a;
}())
}
expect: {
console.log(function() {
var o = { a: "PASS" };
if (o.a) return o.a;
}());
}
expect_stdout: "PASS"
}
join_object_assignments_forin: {
options = {
join_vars: true,
}
input: {
console.log(function() {
var o = {};
for (var a in o.a = "PASS", o)
return o[a];
}())
}
expect: {
console.log(function() {
var o = { a: "PASS" };
for (var a in o)
return o[a];
}());
}
expect_stdout: "PASS"
}
join_object_assignments_negative: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[0] = 0;
o[-0] = 1;
o[-1] = 2;
console.log(o[0], o[-0], o[-1]);
}
expect: {
var o = {
0: 0,
0: 1,
"-1": 2
};
console.log(o[0], o[-0], o[-1]);
}
expect_stdout: "1 1 2"
}
join_object_assignments_NaN_1: {
options = {
join_vars: true,
}
input: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect_stdout: "2 2"
}
join_object_assignments_NaN_2: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[NaN] = 1;
o[0/0] = 2;
console.log(o[NaN], o[NaN]);
}
expect: {
var o = {
NaN: 1,
NaN: 2
};
console.log(o.NaN, o.NaN);
}
expect_stdout: "2 2"
}
join_object_assignments_null_0: {
options = {
join_vars: true,
}
input: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect_stdout: "1"
}
join_object_assignments_null_1: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[null] = 1;
console.log(o[null]);
}
expect: {
var o = {
null: 1
};
console.log(o.null);
}
expect_stdout: "1"
}
join_object_assignments_void_0: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var o = {};
o[void 0] = 1;
console.log(o[void 0]);
}
expect: {
var o = {
undefined: 1
};
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_undefined_1: {
options = {
join_vars: true,
}
input: {
var o = {};
o[undefined] = 1;
console.log(o[undefined]);
}
expect: {
var o = {};
o[void 0] = 1;
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_undefined_2: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[undefined] = 1;
console.log(o[undefined]);
}
expect: {
var o = {
undefined : 1
};
console.log(o[void 0]);
}
expect_stdout: "1"
}
join_object_assignments_Infinity: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[Infinity] = 1;
o[1/0] = 2;
o[-Infinity] = 3;
o[-1/0] = 4;
console.log(o[Infinity], o[1/0], o[-Infinity], o[-1/0]);
}
expect: {
var o = {
Infinity: 1,
Infinity: 2,
"-Infinity": 3,
"-Infinity": 4
};
console.log(o[1/0], o[1/0], o[-1/0], o[-1/0]);
}
expect_stdout: "2 2 4 4"
}
join_object_assignments_regex: {
options = {
evaluate: true,
join_vars: true,
properties: true,
}
input: {
var o = {};
o[/rx/] = 1;
console.log(o[/rx/]);
}
expect: {
var o = {
"/rx/": 1
};
console.log(o[/rx/]);
}
expect_stdout: "1"
}
issue_2816: {
options = {
join_vars: true,
}
input: {
"use strict";
var o = {
a: 1
};
o.b = 2;
o.a = 3;
o.c = 4;
console.log(o.a, o.b, o.c);
}
expect: {
"use strict";
var o = {
a: 1,
b: 2
};
o.a = 3;
o.c = 4;
console.log(o.a, o.b, o.c);
}
expect_stdout: "3 2 4"
}

View File

@@ -414,3 +414,124 @@ issue_2638: {
"/* */(a()||b())(c(),d());",
]
}
issue_2705_1: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a();
/*@__PURE__*/ (new b());
new (/*@__PURE__*/ c)();
(/*@__PURE__*/ new d());
}
expect_exact: [
"new/* */c;",
]
}
issue_2705_2: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a(1)(2)(3);
/*@__PURE__*/ new (b(1))(2)(3);
/*@__PURE__*/ new (c(1)(2))(3);
/*@__PURE__*/ new (d(1)(2)(3));
new (/*@__PURE__*/ e)(1)(2)(3);
(/*@__PURE__*/ new f(1))(2)(3);
(/*@__PURE__*/ new g(1)(2))(3);
(/*@__PURE__*/ new h(1)(2)(3));
}
expect_exact: [
"new/* */e(1)(2)(3);",
"/* */new f(1)(2)(3);",
"/* */new g(1)(2)(3);",
]
}
issue_2705_3: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/ new a.x(1).y(2).z(3);
/*@__PURE__*/ new (b.x)(1).y(2).z(3);
/*@__PURE__*/ new (c.x(1)).y(2).z(3);
/*@__PURE__*/ new (d.x(1).y)(2).z(3);
/*@__PURE__*/ new (e.x(1).y(2)).z(3);
/*@__PURE__*/ new (f.x(1).y(2).z)(3);
/*@__PURE__*/ new (g.x(1).y(2).z(3));
new (/*@__PURE__*/ h).x(1).y(2).z(3);
/* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3);
(/*@__PURE__*/ new j.x(1)).y(2).z(3);
(/*@__PURE__*/ new k.x(1).y)(2).z(3);
(/*@__PURE__*/ new l.x(1).y(2)).z(3);
(/*@__PURE__*/ new m.x(1).y(2).z)(3);
(/*@__PURE__*/ new n.x(1).y(2).z(3));
}
expect_exact: [
"new/* */h.x(1).y(2).z(3);",
"/* */new/* */i.x(1).y(2).z(3);",
"/* */new j.x(1).y(2).z(3);",
"/* */new k.x(1).y(2).z(3);",
"/* */new l.x(1).y(2).z(3);",
"/* */new m.x(1).y(2).z(3);",
]
}
issue_2705_4: {
options = {
side_effects: true,
}
input: {
(/*@__PURE__*/ new x(), y());
(w(), /*@__PURE__*/ new x(), y());
}
expect: {
y();
w(), y();
}
}
issue_2705_5: {
options = {
side_effects: true,
}
input: {
[ /*@__PURE__*/ new x() ];
[ /*@__PURE__*/ new x(), y() ];
[ w(), /*@__PURE__*/ new x(), y() ];
}
expect: {
y();
w(), y();
}
}
issue_2705_6: {
options = {
side_effects: true,
}
beautify = {
comments: "all",
}
input: {
/*@__PURE__*/new (g() || h())(x(), y());
/* */ new (/*@__PURE__*/ (a() || b()))(c(), d());
}
expect_exact: [
"/* */x(),y();",
"/* */new(/* */a()||b())(c(),d());",
]
}

View File

@@ -348,6 +348,57 @@ set_immutable_4: {
expect_stdout: true
}
set_immutable_5: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
1..foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_immutable_6: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
@@ -611,3 +662,62 @@ issue_2313_6: {
x();
}
}
issue_2678: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a = 1, c = "FAIL";
(function f() {
(a-- && f()).p;
return {
get p() {
c = "PASS";
}
};
})();
console.log(c);
}
expect: {
var a = 1, c = "FAIL";
(function f() {
(a-- && f()).p;
return {
get p() {
c = "PASS";
}
};
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_2838: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
function f(a, b) {
(a || b).c = "PASS";
(function() {
return f(a, b);
}).prototype.foo = "bar";
}
var o = {};
f(null, o);
console.log(o.c);
}
expect: {
function f(a, b) {
(a || b).c = "PASS";
}
var o = {};
f(null, o);
console.log(o.c);
}
expect_stdout: "PASS"
}

View File

@@ -76,14 +76,12 @@ modified: {
console.log(a + 1);
console.log(b + 1);
}
function f1() {
var a = 1, b = 2;
--b;
console.log(a + 1);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
@@ -92,7 +90,6 @@ modified: {
console.log(a + c);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
@@ -101,7 +98,6 @@ modified: {
console.log(a + c);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
if (a) {
@@ -114,28 +110,26 @@ modified: {
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
console.log(typeof A ? "yes" : "no");
console.log(typeof B ? "yes" : "no");
}
f0(), f1(), f2(), f3(), f4(), f5();
}
expect: {
function f0() {
var b = 2;
b++;
console.log(2);
console.log(b + 1);
console.log(4);
}
function f1() {
var b = 2;
--b;
console.log(2);
console.log(b + 1);
console.log(2);
}
function f2() {
3;
console.log(4);
@@ -143,16 +137,14 @@ modified: {
console.log(4);
console.log(7);
}
function f3() {
var b = 2;
b *= 3;
console.log(1 + b);
console.log(b + 3);
console.log(7);
console.log(9);
console.log(4);
console.log(1 + b + 3);
console.log(10);
}
function f4() {
var b = 2, c = 3;
b = c;
@@ -161,13 +153,33 @@ modified: {
console.log(1 + c);
console.log(1 + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
console.log(typeof A ? "yes" : "no");
console.log(typeof B ? "yes" : "no");
}
f0(), f1(), f2(), f3(), f4(), f5();
}
expect_stdout: [
"2",
"4",
"2",
"2",
"4",
"6",
"4",
"7",
"7",
"9",
"4",
"10",
"4",
"6",
"4",
"7",
"yes",
"yes",
]
}
unsafe_evaluate: {
@@ -299,7 +311,7 @@ unsafe_evaluate_modified: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());
@@ -745,7 +757,7 @@ iife: {
expect: {
!function(a, b, c) {
b++;
console.log(0, 1 * b, 5);
console.log(0, 3, 5);
}(1, 2, 3);
}
expect_stdout: true
@@ -766,7 +778,7 @@ iife_new: {
expect: {
var A = new function(a, b, c) {
b++;
console.log(0, 1 * b, 5);
console.log(0, 3, 5);
}(1, 2, 3);
}
expect_stdout: true
@@ -1355,10 +1367,9 @@ defun_inline_1: {
function f() {
return function(b) {
return b;
}(2) + h();
function h() {
}(2) + function h() {
return h();
}
}();
}
}
}
@@ -1382,12 +1393,11 @@ defun_inline_2: {
}
expect: {
function f() {
function h() {
return h();
}
return function(b) {
return b;
}(2) + h();
}(2) + function h() {
return h();
}();
}
}
}
@@ -2314,8 +2324,7 @@ delay_def: {
}
expect: {
function f() {
return a;
var a;
return;
}
function g() {
return a;
@@ -2326,6 +2335,28 @@ delay_def: {
expect_stdout: true
}
delay_def_lhs: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
long_name++;
return long_name;
var long_name;
}());
}
expect: {
console.log(function() {
long_name++;
return long_name;
var long_name;
}());
}
expect_stdout: "NaN"
}
booleans: {
options = {
booleans: true,
@@ -4954,3 +4985,551 @@ issue_2598: {
}
expect_stdout: "true"
}
var_if: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
if (x()) {
var a;
if (!g) a = true;
if (a) g();
}
}
}
expect: {
function f() {
if (x()) {
var a;
if (!g) a = true;
if (a) g();
}
}
}
}
defun_assign: {
options = {
reduce_vars: true,
toplevel: true,
}
input: {
console.log(typeof a);
a = 42;
console.log(typeof a);
function a() {}
console.log(typeof a);
}
expect: {
console.log(typeof a);
a = 42;
console.log(typeof a);
function a() {}
console.log(typeof a);
}
expect_stdout: [
"function",
"number",
"number",
]
}
defun_var_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var a = 42, b;
function a() {}
function b() {}
console.log(typeof a, typeof b);
}
expect: {
var a = 42;
function a() {}
console.log(typeof a, "function");
}
expect_stdout: "number function"
}
defun_var_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function a() {}
function b() {}
var a = 42, b;
console.log(typeof a, typeof b);
}
expect: {
function a() {}
var a = 42;
console.log(typeof a, "function");
}
expect_stdout: "number function"
}
defun_var_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function a() {}
function b() {}
console.log(typeof a, typeof b);
var a = 42, b;
}
expect: {
function a() {}
console.log(typeof a, "function");
var a = 42;
}
expect_stdout: "function function"
}
defun_catch_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function a() {}
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
defun_catch_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
function a() {}
throw 42;
} catch (a) {
console.log(a);
}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
defun_catch_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
function a() {}
} catch (a) {
console.log(a);
}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
defun_catch_4: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect_stdout: true
}
defun_catch_5: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect_stdout: true
}
defun_catch_6: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (a) {
console.log(a);
}
function a() {}
}
expect: {
try {
throw 42;
} catch (a) {
console.log(a);
}
}
expect_stdout: "42"
}
duplicate_lambda_defun_name_1: {
options = {
reduce_vars: true,
}
input: {
console.log(function f(a) {
function f() {}
return f.length;
}());
}
expect: {
console.log(function f(a) {
function f() {}
return f.length;
}());
}
expect_stdout: "0"
}
duplicate_lambda_defun_name_2: {
options = {
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
console.log(function f(a) {
function f() {}
return f.length;
}());
}
expect: {
console.log(function(a) {
return function() {}.length;
}());
}
expect_stdout: "0"
}
issue_2774: {
options = {
reduce_vars: true,
unused: true,
}
input: {
console.log({
get a() {
var b;
(b = true) && b.c;
b = void 0;
}
}.a);
}
expect: {
console.log({
get a() {
var b;
(b = true) && b.c;
b = void 0;
}
}.a);
}
expect_stdout: "undefined"
}
issue_2799_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
return f;
function f(n) {
function g(i) {
return i && i + g(i - 1);
}
function h(j) {
return g(j);
}
return h(n);
}
}()(5));
}
expect: {
console.log(function() {
return function(n) {
return function(j) {
return function g(i) {
return i && i + g(i - 1);
}(j);
}(n);
}
}()(5));
}
expect_stdout: "15"
}
issue_2799_2: {
options = {
reduce_vars: true,
unsafe_proto: true,
unused: true,
}
input: {
(function() {
function foo() {
Function.prototype.call.apply(console.log, [ null, "PASS" ]);
}
foo();
})();
}
expect: {
(function() {
(function() {
(function() {}).call.apply(console.log, [ null, "PASS" ]);
})();
})();
}
expect_stdout: "PASS"
}
issue_2836: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return "FAIL";
}
console.log(f());
function f() {
return "PASS";
}
}
expect: {
console.log(function() {
return "PASS";
}());
}
expect_stdout: "PASS"
}
lvalues_def_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
var a = b++, b = NaN;
console.log(a, b);
}
expect: {
var b = 1;
var a = b++;
b = NaN;
console.log(a, b);
}
expect_stdout: "1 NaN"
}
lvalues_def_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
var a = b += 1, b = NaN;
console.log(a, b);
}
expect: {
var b = 1;
var a = b += 1;
b = NaN;
console.log(a, b);
}
expect_stdout: "2 NaN"
}
chained_assignments: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function f() {
var a = [0x5e, 0xad, 0xbe, 0xef];
var b = 0;
b |= a[0];
b <<= 8;
b |= a[1];
b <<= 8;
b |= a[2];
b <<= 8;
b |= a[3];
return b;
}
console.log(f().toString(16));
}
expect: {
console.log("5eadbeef");
}
expect_stdout: "5eadbeef"
}
issue_2860_1: {
options = {
dead_code: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
a ^= 2;
}());
}
expect: {
console.log(function(a) {
return 1 ^ a;
}());
}
expect_stdout: "1"
}
issue_2860_2: {
options = {
dead_code: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
}
input: {
console.log(function(a) {
return a ^= 1;
a ^= 2;
}());
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_2869: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = "FAIL";
(function f(a) {
var a;
if (!f) a = 0;
if (a) c = "PASS";
})(1);
console.log(c);
}
expect: {
var c = "FAIL";
(function f(a) {
var a;
if (!f) a = 0;
if (a) c = "PASS";
})(1);
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -288,7 +288,7 @@ unsafe_undefined: {
if_return: true,
sequences: true,
side_effects: true,
unsafe: true,
unsafe_undefined: true,
}
input: {
function f(undefined) {
@@ -796,3 +796,84 @@ cascade_assignment_in_return: {
}
}
}
hoist_defun: {
options = {
join_vars: true,
sequences: true,
}
input: {
x();
function f() {}
y();
}
expect: {
function f() {}
x(), y();
}
}
hoist_decl: {
options = {
join_vars: true,
sequences: true,
}
input: {
var a;
w();
var b = x();
y();
for (var c; 0;) z();
var d;
}
expect: {
var a;
w();
var b = x(), c, d;
for (y(); 0;) z();
}
}
for_init_var: {
options = {
join_vars: true,
unused: false,
}
input: {
var a = "PASS";
(function() {
var b = 42;
for (var c = 5; c > 0;) c--;
a = "FAIL";
var a;
})();
console.log(a);
}
expect: {
var a = "PASS";
(function() {
for (var b = 42, c = 5, a; c > 0;) c--;
a = "FAIL";
})();
console.log(a);
}
expect_stdout: "PASS"
}
forin: {
options = {
sequences: true,
}
input: {
var o = [];
o.push("PASS");
for (var a in o)
console.log(o[a]);
}
expect: {
var o = [];
for (var a in o.push("PASS"), o)
console.log(o[a]);
}
expect_stdout: "PASS"
}

View File

@@ -100,7 +100,7 @@ typeof_defun_1: {
g = 42;
console.log("YES");
"function" == typeof g && g();
h();
"function" == typeof h && h();
}
expect_stdout: [
"YES",
@@ -138,3 +138,166 @@ typeof_defun_2: {
"2",
]
}
duplicate_defun_arg_name: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
function long_name(long_name) {
return typeof long_name;
}
console.log(typeof long_name, long_name());
}
expect: {
function long_name(long_name) {
return typeof long_name;
}
console.log(typeof long_name, long_name());
}
expect_stdout: "function undefined"
}
duplicate_lambda_arg_name: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
console.log(function long_name(long_name) {
return typeof long_name;
}());
}
expect: {
console.log(function long_name(long_name) {
return typeof long_name;
}());
}
expect_stdout: "undefined"
}
issue_2728_1: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
(function arguments() {
console.log(typeof arguments);
})();
}
expect: {
(function arguments() {
console.log(typeof arguments);
})();
}
expect_stdout: "object"
}
issue_2728_2: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
function arguments() {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect: {
function arguments() {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect_stdout: "function object"
}
issue_2728_3: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
(function() {
function arguments() {
}
console.log(typeof arguments);
})();
}
expect: {
(function() {
function arguments() {
}
console.log("function");
})();
}
expect_stdout: "function"
}
issue_2728_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
}
input: {
function arguments() {
}
console.log(typeof arguments);
}
expect: {
function arguments() {
}
console.log("function");
}
expect_stdout: "function"
}
issue_2728_5: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
(function arguments(arguments) {
console.log(typeof arguments);
})();
}
expect: {
(function arguments(arguments) {
console.log(typeof arguments);
})();
}
expect_stdout: "undefined"
}
issue_2728_6: {
options = {
evaluate: true,
reduce_vars: true,
typeofs: true,
}
input: {
function arguments(arguments) {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect: {
function arguments(arguments) {
return typeof arguments;
}
console.log(typeof arguments, arguments());
}
expect_stdout: "function undefined"
}

View File

@@ -1,4 +1,4 @@
var a = bar(1+2);
var b = baz(3+9);
print('q' + 'u' + 'x', a, b);
var x = bar(1+2);
var y = baz(3+9);
print('q' + 'u' + 'x', x, y);
foo(5+6);

View File

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

View File

@@ -5,11 +5,7 @@
var site = "http://browserbench.org/JetStream";
if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
require("../tools/exit");
var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {

View File

@@ -1,29 +1,24 @@
var Mocha = require('mocha'),
fs = require('fs'),
path = require('path');
var fs = require("fs");
var Mocha = require("mocha");
var path = require("path");
// Instantiate a Mocha instance.
var mocha = new Mocha({});
// Instantiate a Mocha instance
var mocha = new Mocha({
timeout: 5000
});
var testDir = __dirname + "/mocha/";
var testDir = __dirname + '/mocha/';
// Add each .js file to the mocha instance
fs.readdirSync(testDir).filter(function(file){
// Only keep the .js files
return file.substr(-3) === '.js';
}).forEach(function(file){
mocha.addFile(
path.join(testDir, file)
);
// Add each .js file to the Mocha instance
fs.readdirSync(testDir).filter(function(file) {
return /\.js$/.test(file);
}).forEach(function(file) {
mocha.addFile(path.join(testDir, file));
});
module.exports = function() {
mocha.run(function(failures) {
if (failures !== 0) {
process.on('exit', function () {
if (failures) process.on("exit", function() {
process.exit(failures);
});
}
});
};

View File

@@ -650,4 +650,36 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should work with explicit --rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js --rename";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c}}\n");
done();
});
});
it("Should work with explicit --no-rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -mc --no-rename";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n");
done();
});
});
it("Should work with implicit --rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -mc";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(n){return n}\n");
done();
});
});
it("Should work with implicit --no-rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -c";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n");
done();
});
});
});

View File

@@ -219,4 +219,24 @@ describe("Comment", function() {
if (result.error) throw result.error;
assert.strictEqual(result.code, "/*a*/ /*b*/(function(){/*c*/}/*d*/ /*e*/)();");
});
it("Should output line comments after statements", function() {
var result = uglify.minify([
"x()//foo",
"{y()//bar",
"}",
].join("\n"), {
compress: false,
mangle: false,
output: {
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"x();//foo",
"{y();//bar",
"}",
].join("\n"));
});
});

View File

@@ -42,8 +42,15 @@ describe("minify", function() {
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 20), '{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}var c=console.log.bind(console);function l(o){c("Foo:",2*o)}var f=n(3),i=r(12);c("qux",f,i),l(11);');
assert.strictEqual(JSON.stringify(cache).slice(0, 10), '{"props":{');
assert.strictEqual(compressed, [
"function n(n){return 3*n}",
"function r(n){return n/2}",
"var o=console.log.bind(console);",
'function c(n){o("Foo:",2*n)}',
"var a=n(3),b=r(12);",
'o("qux",a,b),c(11);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
@@ -68,12 +75,48 @@ describe("minify", function() {
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 28), '{"vars":{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}var c=console.log.bind(console);function l(o){c("Foo:",2*o)}var f=n(3),i=r(12);c("qux",f,i),l(11);');
assert.strictEqual(JSON.stringify(cache).slice(0, 18), '{"vars":{"props":{');
assert.strictEqual(compressed, [
"function n(n){return 3*n}",
"function r(n){return n/2}",
"var o=console.log.bind(console);",
'function c(n){o("Foo:",2*n)}',
"var a=n(3),b=r(12);",
'o("qux",a,b),c(11);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
it("should not parse invalid use of reserved words", function() {
it("Should avoid mangled names in cache", function() {
var cache = {};
var original = "";
var compressed = "";
[
'"xxxyy";var i={s:1};',
'"xxyyy";var j={t:2,u:3},k=4;',
'console.log(i.s,j.t,j.u,k);',
].forEach(function(code) {
var result = Uglify.minify(code, {
compress: false,
mangle: {
properties: true,
toplevel: true
},
nameCache: cache
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(compressed, [
'"xxxyy";var x={x:1};',
'"xxyyy";var y={y:2,a:3},a=4;',
'console.log(x.x,y.y,y.a,a);',
].join(""));
assert.strictEqual(run_code(compressed), run_code(original));
});
it("Should not parse invalid use of reserved words", function() {
assert.strictEqual(Uglify.minify("function enum(){}").error, undefined);
assert.strictEqual(Uglify.minify("function static(){}").error, undefined);
assert.strictEqual(Uglify.minify("function this(){}").error.message, "Unexpected token: name (this)");
@@ -330,4 +373,18 @@ describe("minify", function() {
assert.strictEqual(stat.print_to_string(), "a=x()");
});
});
describe("rename", function() {
it("Should be repeatable", function() {
var code = "!function(x){return x(x)}(y);";
for (var i = 0; i < 2; i++) {
assert.strictEqual(Uglify.minify(code, {
compress: {
toplevel: true,
},
rename: true,
}).code, "var a;(a=y)(a);");
}
});
});
});

View File

@@ -10,6 +10,7 @@ var semver = require("semver");
var tests_dir = path.dirname(module.filename);
var failures = 0;
var failed_files = {};
var minify_options = require("./ufuzz.json").map(JSON.stringify);
run_compress_tests();
if (failures) {
@@ -100,6 +101,15 @@ function run_compress_tests() {
quote_style: 3,
keep_quoted_props: true
});
try {
U.parse(input_code);
} catch (ex) {
log("!!! Cannot parse input\n---INPUT---\n{input}\n--PARSE ERROR--\n{error}\n\n", {
input: input_formatted,
error: ex,
});
return false;
}
var options = U.defaults(test.options, {
warnings: false
});
@@ -139,21 +149,18 @@ function run_compress_tests() {
output: output,
expected: expect
});
failures++;
failed_files[file] = 1;
return false;
}
else {
// expect == output
try {
var reparsed_ast = U.parse(output);
U.parse(output);
} catch (ex) {
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
input: input_formatted,
output: output,
error: ex.toString(),
error: ex,
});
failures++;
failed_files[file] = 1;
return false;
}
if (test.expect_warnings) {
U.AST_Node.warn_function = original_warn_function;
@@ -171,8 +178,7 @@ function run_compress_tests() {
expected_warnings: expected_warnings,
actual_warnings: actual_warnings,
});
failures++;
failed_files[file] = 1;
return false;
}
}
if (test.expect_stdout
@@ -189,9 +195,8 @@ function run_compress_tests() {
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
} else {
return false;
}
stdout = sandbox.run_code(output);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
@@ -201,16 +206,20 @@ function run_compress_tests() {
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
}
}
return false;
}
if (!reminify(test.options, input_code, input_formatted, test.expect_stdout)) {
return false;
}
}
return true;
}
var tests = parse_test(path.resolve(dir, file));
for (var i in tests) if (tests.hasOwnProperty(i)) {
test_case(tests[i]);
if (!test_case(tests[i])) {
failures++;
failed_files[file] = 1;
}
}
}
files.forEach(function(file){
@@ -346,3 +355,46 @@ function evaluate(code) {
code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")();
}
// Try to reminify original input with standard options
// to see if it matches expect_stdout.
function reminify(orig_options, input_code, input_formatted, expect_stdout) {
for (var i = 0; i < minify_options.length; i++) {
var options = JSON.parse(minify_options[i]);
if (options.compress) [
"keep_fargs",
"keep_fnames",
].forEach(function(name) {
if (name in orig_options) {
options.compress[name] = orig_options[name];
}
});
var options_formatted = JSON.stringify(options, null, 4);
var result = U.minify(input_code, options);
if (result.error) {
log("!!! failed input reminify\n---INPUT---\n{input}\n--ERROR---\n{error}\n\n", {
input: input_formatted,
error: result.error,
});
return false;
} else {
var stdout = sandbox.run_code(result.code);
if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
stdout = expect_stdout;
}
if (!sandbox.same_stdout(expect_stdout, stdout)) {
log("!!! failed running reminified input\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n---OUTPUT---\n{output}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
options: options_formatted,
output: result.code,
expected_type: typeof expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
return false;
}
}
}
return true;
}

View File

@@ -17,24 +17,30 @@ function safe_log(arg, level) {
return arg;
}
function strip_func_ids(text) {
return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>");
}
var FUNC_TOSTRING = [
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
" f.valueOf = Function.prototype.valueOf;",
"});",
"Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 100000;",
" return function() {",
' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";',
" var i = this.name;",
' if (typeof i != "number") {',
" i = ++id;",
" var n = this.name;",
' if (!/^F[0-9]{6}N$/.test(n)) {',
' n = "F" + ++id + "N";',
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return i;",
" return n;",
" }",
" });",
] : [], [
" }",
' return "[Function: " + i + "]";',
' return "[Function: " + n + "]";',
" }",
"}();",
'Object.defineProperty(Function.prototype, "valueOf", { enumerable: false });',
@@ -77,7 +83,7 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
}
return expected == actual;
return strip_func_ids(expected) == strip_func_ids(actual);
} : function(expected, actual) {
return typeof expected == typeof actual && expected.toString() == actual.toString();
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};

82
test/travis-ufuzz.js Normal file
View File

@@ -0,0 +1,82 @@
"use strict";
var child_process = require("child_process");
var https = require("https");
var url = require("url");
var period = 45 * 60 * 1000;
var wait = 2 * 60 * 1000;
var ping = 5 * 60 * 1000;
if (process.argv[2] == "run") {
var endTime = Date.now() + period;
for (var i = 0; i < 2; i++) spawn(endTime);
} else if (process.argv.length > 2) {
var token = process.argv[2];
var branch = process.argv[3] || "v" + require("../package.json").version;
var repository = encodeURIComponent(process.argv[4] || "mishoo/UglifyJS2");
var concurrency = process.argv[5] || 1;
(function request() {
setTimeout(request, (period + wait) / concurrency);
var options = url.parse("https://api.travis-ci.org/repo/" + repository + "/requests");
options.method = "POST";
options.headers = {
"Content-Type": "application/json",
"Travis-API-Version": 3,
"Authorization": "token " + token
};
https.request(options, function(res) {
console.log("HTTP", res.statusCode);
console.log(JSON.stringify(res.headers, null, 2));
console.log();
res.setEncoding("utf8");
res.on("data", console.log);
}).on("error", console.error).end(JSON.stringify({
request: {
message: "ufuzz testing (when idle)",
branch: branch,
config: {
merge_mode: "replace",
language: "node_js",
node_js: "9",
sudo: false,
script: "node test/travis-ufuzz run"
}
}
}));
})();
} else {
console.log("Usage: test/travis-ufuzz.js <token> [branch] [repository] [concurrency]");
}
function spawn(endTime) {
var child = child_process.spawn("node", [
"--max-old-space-size=2048",
"test/ufuzz"
], {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
var line = "";
child.stdout.on("data", function(data) {
line += data;
});
child.stderr.on("data", function() {
process.exitCode = 1;
}).pipe(process.stdout);
var keepAlive = setInterval(function() {
var end = line.lastIndexOf("\r");
console.log(line.slice(line.lastIndexOf("\r", end - 1) + 1, end));
line = line.slice(end + 1);
}, ping);
var timer = setTimeout(function() {
clearInterval(keepAlive);
child.removeListener("exit", respawn);
child.kill();
}, endTime - Date.now());
function respawn() {
console.log(line);
clearInterval(keepAlive);
clearTimeout(timer);
spawn(endTime);
}
}

View File

@@ -6,11 +6,7 @@
// bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m
// cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
require("../tools/exit");
var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes;
@@ -127,6 +123,9 @@ for (var i = 2; i < process.argv.length; ++i) {
}
var VALUES = [
'"a"',
'"b"',
'"c"',
'""',
'true',
'false',
@@ -329,7 +328,8 @@ function createTopLevelCode() {
rng(2) == 0
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
: createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
'console.log(null, a, b, c);' // preceding `null` makes for a cleaner output (empty string still shows up etc)
// preceding `null` makes for a cleaner output (empty string still shows up etc)
'console.log(null, a, b, c, Infinity, NaN, undefined);'
].join('\n');
}
@@ -635,6 +635,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
return getVarName();
case p++:
return getVarName() + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:
@@ -996,10 +998,11 @@ function log_suspects(minify_options, component) {
if (typeof options != "object") options = {};
var defs = default_options[component];
var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var flip = name == "keep_fargs";
if (flip ? name in options : (name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options));
o[name] = false;
o[name] = flip;
m[component] = o;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
@@ -1020,6 +1023,23 @@ function log_suspects(minify_options, component) {
}
}
function log_rename(options) {
var m = JSON.parse(JSON.stringify(options));
m.rename = false;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
errorln("Error testing options.rename");
errorln(result.error.stack);
} else {
var r = sandbox.run_code(result.code);
if (sandbox.same_stdout(original_result, r)) {
errorln("Suspicious options:");
errorln(" rename");
errorln();
}
}
}
function log(options) {
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
errorln("//=============================================================");
@@ -1054,6 +1074,7 @@ function log(options) {
errorln();
if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options));
log_rename(options);
errorln("!!!!!! Failed... round " + round);
}
}

View File

@@ -5,7 +5,8 @@
"output": {
"beautify": true,
"bracketize": true
}
},
"rename": true
},
{
"compress": false
@@ -20,7 +21,13 @@
{
"compress": {
"keep_fargs": false,
"passes": 100
"passes": 1e6,
"sequences": 1e6,
"unsafe": true,
"unsafe_Function": true,
"unsafe_math": true,
"unsafe_proto": true,
"unsafe_regexp": true
}
}
]

15
tools/exit.js Normal file
View File

@@ -0,0 +1,15 @@
// workaround for tty output truncation upon process.exit()
var exit = process.exit;
process.exit = function() {
var args = [].slice.call(arguments);
process.once("uncaughtException", function() {
(function callback() {
if (process.stdout.bufferSize || process.stderr.bufferSize) {
setImmediate(callback);
} else {
exit.apply(process, args);
}
})();
});
throw exit;
};