Compare commits

...

70 Commits

Author SHA1 Message Date
Alex Lam S.L
370c8e0385 v3.6.4 2019-10-23 15:38:05 +08:00
Alex Lam S.L
4240fba9b8 fix corner cases in unused (#3519) 2019-10-23 06:46:05 +08:00
Alex Lam S.L
267bc70d33 fix corner case in unused (#3517)
fixes #3515
2019-10-23 01:58:40 +08:00
Alex Lam S.L
a53ab99378 fix corner case in side_effects (#3514)
fixes #3512
2019-10-23 01:04:00 +08:00
Alex Lam S.L
02308a7b56 fix corner case in reduce_vars (#3510)
fixes #3509
2019-10-22 20:36:05 +08:00
Alex Lam S.L
0b3705e82f fix corner cases in inline (#3507)
fixes #3506
2019-10-22 15:41:55 +08:00
Alex Lam S.L
da5a21b240 fix GitHub Actions script for fuzzing (#3504) 2019-10-21 04:30:00 +08:00
Alex Lam S.L
5bd0cf8633 enable GitHub Actions (#3503) 2019-10-21 04:11:14 +08:00
Alex Lam S.L
9199ab5846 minor tweaks (#3502) 2019-10-20 15:19:19 +08:00
Alex Lam S.L
ca6dce43fe fix corner case in collapse_vars (#3501) 2019-10-20 03:53:20 +08:00
Alex Lam S.L
543dd7d3d7 fix corner case in comments (#3500) 2019-10-20 03:21:30 +08:00
Alex Lam S.L
6b4886c908 v3.6.3 2019-10-19 14:28:11 +08:00
Alex Lam S.L
0201cb4b52 fix corner case in unused (#3499)
fixes #3497
2019-10-18 20:08:05 +08:00
Alex Lam S.L
cd072317d0 fix corner case in unused (#3496)
fixes #3495
2019-10-18 17:09:43 +08:00
Alex Lam S.L
0785a15ace fix corner case in dead_code & ie8 (#3494)
fixes #3493
2019-10-17 09:58:05 +08:00
Alex Lam S.L
b1279a46d9 fix corner case in sequences (#3491)
fixes #3490
2019-10-17 09:57:50 +08:00
Alex Lam S.L
b571619d31 handle throw of non-Errors gracefully (#3492) 2019-10-17 06:29:02 +08:00
Alex Lam S.L
7b5350b459 tweak Travis CI execution environment (#3489) 2019-10-16 15:47:06 +08:00
Alex Lam S.L
1549db70e6 fix corner case in ie8 (#3487)
fixes #3486
2019-10-16 12:18:27 +08:00
Alex Lam S.L
8ff9a3c8fb fix corner cases in ie8 (#3485)
fixes #3484
2019-10-16 06:37:40 +08:00
Alex Lam S.L
91cae51d8f fix corner case in evaluate & ie8 (#3483)
fixes #3482
2019-10-16 01:09:16 +08:00
Alex Lam S.L
8af2f5fbcf fix corner case in rename (#3481)
fixes #3480
2019-10-15 19:44:07 +08:00
Alex Lam S.L
86a8016323 fix corner case in ie8 & mangle (#3479)
fixes #3478
2019-10-15 17:14:48 +08:00
David xu
009dcdae01 avoid mangling of MutationObserver properties (#3477) 2019-10-15 16:16:43 +08:00
Alex Lam S.L
f86f615d83 fix corner case in ie8 & mangle (#3476)
fixes #3475
2019-10-15 14:18:12 +08:00
Alex Lam S.L
d3d1d11926 fix corner case in ie8 & rename (#3474)
fixes #3473
2019-10-15 07:27:02 +08:00
Alex Lam S.L
736019b767 fix corner cases in ie8 (#3472)
fixes #3471
2019-10-14 18:15:40 +08:00
Alex Lam S.L
a39bdb5840 fix corner case with collapse_vars & ie8 (#3469)
fixes #3468
2019-10-14 13:34:35 +08:00
Alex Lam S.L
e8ab0a44b2 update dependency (#3470) 2019-10-14 13:34:22 +08:00
Alex Lam S.L
c3ca293e6b v3.6.2 2019-10-12 20:19:05 +08:00
Alex Lam S.L
516b67a43b minor tweaks to CI test scripts (#3467) 2019-10-12 05:36:38 +08:00
Alex Lam S.L
eba3a37bb5 fix boolean context detection (#3466)
fixes #3465
2019-10-12 03:42:57 +08:00
Alex Lam S.L
6d57ca1a59 improve source map handling (#3464)
fixes #2947
fixes #3277
fixes #3411
2019-10-11 03:52:33 +08:00
Alex Lam S.L
3320251b4b update benchmark URLs (#3462) 2019-10-11 01:00:09 +08:00
Alex Lam S.L
33c94d3bd9 detect boolean context across IIFEs (#3461) 2019-10-10 09:37:02 +08:00
Alex Lam S.L
b18f717b46 improve readability of --help ast (#3460) 2019-10-10 04:32:32 +08:00
Alex Lam S.L
a0d4b648bb remove extraneous property (#3459)
fixes #3455
2019-10-10 01:36:58 +08:00
Alex Lam S.L
6db880e16d clean up AST_Binary optimisation logic (#3458) 2019-10-09 23:45:41 +08:00
Alex Lam S.L
a82003d6ac v3.6.1 2019-10-07 14:36:46 +08:00
Alex Lam S.L
da9f1622fc report errors correctly in ufuzz (#3456) 2019-10-07 14:36:00 +08:00
Alex Lam S.L
8a4c7077bb account for catch in constant lambda expressions (#3454) 2019-10-06 16:51:37 +08:00
Alex Lam S.L
0a63f2f2b0 workaround V8 RegExp bug (#3453)
fixes #3434
2019-10-06 11:49:39 +08:00
Alex Lam S.L
931ac66638 fix corner case in hoist_props (#3452)
fixes #3440
2019-10-06 10:29:13 +08:00
Alex Lam S.L
35338a100f handle function/variable name collisions correctly (#3451)
fixes #3439
2019-10-06 08:51:38 +08:00
David xu
d57b606e73 exclude mangling of addEventListener parameters (#3445) 2019-10-06 05:29:08 +08:00
Sampson Crowley
00ada04111 facilitate Webpack compatibility (#3435)
Verbose application of `require.resolve` instead of `[].map`
2019-10-06 05:20:47 +08:00
Alex Lam S.L
a31c477fea fix variable scope determination (#3449)
fixes #3444
2019-10-06 05:13:44 +08:00
Alex Lam S.L
bde7418ce1 update & fix dependencies (#3450) 2019-10-06 03:10:12 +08:00
Alex Lam S.L
70bb304a0a v3.6.0 2019-05-30 15:30:00 +08:00
Alex Lam S.L
9d3b1efd86 fix corner case in assignments (#3430)
fixes #3429
2019-05-30 05:01:53 +08:00
Alex Lam S.L
482e1baea3 enhance assignments & unused (#3428)
closes #3427
2019-05-29 01:21:08 +08:00
Alex Lam S.L
e4f5ba1d29 v3.5.15 2019-05-21 14:26:58 +08:00
Alex Lam S.L
b9053c7a25 fix corner case in keep_fargs (#3424)
fixes #3423
2019-05-21 12:55:34 +08:00
Alex Lam S.L
d357a7aabc v3.5.14 2019-05-20 00:13:06 +08:00
Alex Lam S.L
ae77ebe5a5 fix corner case in arguments (#3421)
fixes #3420
2019-05-19 12:59:40 +08:00
Alex Lam S.L
04439edcec v3.5.13 2019-05-17 14:10:33 +08:00
Alex Lam S.L
a246195412 enhance unsafe comparisons (#3419) 2019-05-17 01:28:18 +08:00
Alex Lam S.L
8939a36bc7 reduce false positives from fuzzing (#3417) 2019-05-16 16:15:03 +08:00
Alex Lam S.L
a21c348d93 improve sandbox fidelity (#3415) 2019-05-15 23:26:57 +08:00
Alex Lam S.L
1f0def10eb fix corner case in comparisons (#3414)
fixes #3413
2019-05-15 01:01:18 +08:00
Alex Lam S.L
f87caac9d8 fix corner case in hoist_props (#3412)
fixes #3411
2019-05-14 19:12:00 +08:00
Alex Lam S.L
d538a73250 enhance side_effects (#3410) 2019-05-14 05:26:40 +08:00
Alex Lam S.L
2e4fbdeb08 enhance keep_fargs (#3409) 2019-05-13 21:58:04 +08:00
Alex Lam S.L
3bc7cc82bb v3.5.12 2019-05-12 10:40:13 +08:00
Alex Lam S.L
45fbdbc2dc improve tests (#3408) 2019-05-12 09:44:02 +08:00
Alex Lam S.L
54cb678055 fix corner case in assignments (#3407)
fixes #3406
2019-05-12 03:52:46 +08:00
Alex Lam S.L
e88c439eac improve tests (#3405) 2019-05-11 22:06:14 +08:00
Alex Lam S.L
9fc8cd4076 fix corner case in functions (#3403)
fixes #3402
2019-05-11 18:55:45 +08:00
Alex Lam S.L
5476cb8f05 fix corner case in inline (#3401)
fixes #3400
2019-05-10 01:22:44 +08:00
Alex Lam S.L
6a30e1d6be improve tests (#3399) 2019-05-09 07:18:22 +08:00
54 changed files with 5189 additions and 1078 deletions

27
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: CI
on: [ push, pull_request ]
jobs:
test:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
node: [ "0.10", 0.12, 4, 6, 8, 10, latest ]
script: [ compress, mocha, release/benchmark, release/jetstream ]
name: ${{ matrix.os }} ${{ matrix.node }} ${{ matrix.script }}
runs-on: ${{ matrix.os }}
env:
NODE: ${{ matrix.node }}
TYPE: ${{ matrix.script }}
steps:
- uses: actions/checkout@v1
- shell: bash
run: |
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh
nvs --version
nvs add node/$NODE
nvs use node/$NODE
node --version
npm --version --no-update-notifier
npm install --no-audit --no-optional --no-save --no-update-notifier
node test/$TYPE

24
.github/workflows/ufuzz.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Fuzzing
on:
schedule:
- cron: "*/15 * * * *"
jobs:
ufuzz:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- shell: bash
run: |
git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh
nvs --version
nvs add node
nvs use node
node --version
npm --version --no-update-notifier
npm install --no-audit --no-optional --no-save --no-update-notifier
node test/ufuzz/job 3600000

View File

@@ -1,35 +1,46 @@
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libstdc++-4.9-dev
cache: cache:
directories: tmp directories: tmp
language: generic language: shell
matrix: matrix:
fast_finish: true fast_finish: true
sudo: false
env: env:
global: - NODE=0.10 TYPE=compress
- UGLIFYJS_TEST_ALL=1 - NODE=0.10 TYPE=mocha
matrix: - NODE=0.10 TYPE=release/benchmark
- NODEJS_VER=node/0.10 - NODE=0.10 TYPE=release/jetstream
- NODEJS_VER=node/0.12 - NODE=0.12 TYPE=compress
- NODEJS_VER=node/4 - NODE=0.12 TYPE=mocha
- NODEJS_VER=node/6 - NODE=0.12 TYPE=release/benchmark
- NODEJS_VER=node/8 - NODE=0.12 TYPE=release/jetstream
- NODEJS_VER=node/10 - NODE=4 TYPE=compress
- NODEJS_VER=node/latest - NODE=4 TYPE=mocha
- NODE=4 TYPE=release/benchmark
- NODE=4 TYPE=release/jetstream
- NODE=6 TYPE=compress
- NODE=6 TYPE=mocha
- NODE=6 TYPE=release/benchmark
- NODE=6 TYPE=release/jetstream
- NODE=8 TYPE=compress
- NODE=8 TYPE=mocha
- NODE=8 TYPE=release/benchmark
- NODE=8 TYPE=release/jetstream
- NODE=10 TYPE=compress
- NODE=10 TYPE=mocha
- NODE=10 TYPE=release/benchmark
- NODE=10 TYPE=release/jetstream
- NODE=latest TYPE=compress
- NODE=latest TYPE=mocha
- NODE=latest TYPE=release/benchmark
- NODE=latest TYPE=release/jetstream
before_install: before_install:
- git clone --branch v1.4.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs - git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
- . ~/.nvs/nvs.sh - . ~/.nvs/nvs.sh
- nvs --version - nvs --version
install: install:
- nvs add $NODEJS_VER - nvs add node/$NODE
- nvs use $NODEJS_VER - nvs use node/$NODE
- node --version - node --version
- npm --version --no-update-notifier - npm --version --no-update-notifier
- npm install --no-optional --no-save --no-update-notifier - npm install --no-audit --no-optional --no-save --no-update-notifier
script: script:
- npm test --no-update-notifier - node test/$TYPE

View File

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

View File

@@ -664,8 +664,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `join_vars` (default: `true`) -- join consecutive `var` statements - `join_vars` (default: `true`) -- join consecutive `var` statements
- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused - `keep_fargs` (default: `strict`) -- Discard unused function arguments. Code
function arguments. You need this for code which relies on `Function.length`. which relies on `Function.length` will break if this is done indiscriminately,
i.e. when passing `true`. Pass `false` to always retain function arguments.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the - `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on compressor from discarding function names. Useful for code relying on

View File

@@ -1,26 +1,74 @@
environment:
UGLIFYJS_TEST_ALL: 1
matrix:
- NODEJS_VER: node/0.10
- NODEJS_VER: node/0.12
- NODEJS_VER: node/4
- NODEJS_VER: node/6
- NODEJS_VER: node/8
- NODEJS_VER: node/10
- NODEJS_VER: node/latest
install:
- git clone --branch v1.4.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
- nvs --version
- nvs add %NODEJS_VER%
- nvs use %NODEJS_VER%
- node --version
- npm --version --no-update-notifier
- npm install --no-optional --no-save --no-update-notifier
build: off build: off
cache: cache:
- tmp - tmp
matrix: matrix:
fast_finish: true fast_finish: true
environment:
matrix:
- NODE: 0.10
TYPE: compress
- NODE: 0.10
TYPE: mocha
- NODE: 0.10
TYPE: release/benchmark
- NODE: 0.10
TYPE: release/jetstream
- NODE: 0.12
TYPE: compress
- NODE: 0.12
TYPE: mocha
- NODE: 0.12
TYPE: release/benchmark
- NODE: 0.12
TYPE: release/jetstream
- NODE: 4
TYPE: compress
- NODE: 4
TYPE: mocha
- NODE: 4
TYPE: release/benchmark
- NODE: 4
TYPE: release/jetstream
- NODE: 6
TYPE: compress
- NODE: 6
TYPE: mocha
- NODE: 6
TYPE: release/benchmark
- NODE: 6
TYPE: release/jetstream
- NODE: 8
TYPE: compress
- NODE: 8
TYPE: mocha
- NODE: 8
TYPE: release/benchmark
- NODE: 8
TYPE: release/jetstream
- NODE: 10
TYPE: compress
- NODE: 10
TYPE: mocha
- NODE: 10
TYPE: release/benchmark
- NODE: 10
TYPE: release/jetstream
- NODE: latest
TYPE: compress
- NODE: latest
TYPE: mocha
- NODE: latest
TYPE: release/benchmark
- NODE: latest
TYPE: release/jetstream
install:
- git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
- nvs --version
- nvs add node/%NODE%
- nvs use node/%NODE%
- node --version
- npm --version --no-update-notifier
- npm install --no-audit --no-optional --no-save --no-update-notifier
test_script: test_script:
- npm test --no-update-notifier - node test/%TYPE%

View File

@@ -376,7 +376,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
} }
}, AST_Scope); }, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
name: "[AST_SymbolDeclaration?] the name of this function", name: "[AST_SymbolDeclaration?] the name of this function",
@@ -614,6 +614,18 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
$propdoc: { $propdoc: {
expression: "[AST_Node] the “container” expression", expression: "[AST_Node] the “container” expression",
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
},
getProperty: function() {
var p = this.property;
if (p instanceof AST_Constant) {
return p.getValue();
}
if (p instanceof AST_UnaryPrefix
&& p.operator == "void"
&& p.expression instanceof AST_Constant) {
return;
}
return p;
} }
}); });
@@ -825,11 +837,10 @@ var AST_String = DEFNODE("String", "value quote", {
} }
}, AST_Constant); }, AST_Constant);
var AST_Number = DEFNODE("Number", "value literal", { var AST_Number = DEFNODE("Number", "value", {
$documentation: "A number literal", $documentation: "A number literal",
$propdoc: { $propdoc: {
value: "[number] the numeric value", value: "[number] the numeric value",
literal: "[string] numeric value as string (optional)"
} }
}, AST_Constant); }, AST_Constant);
@@ -968,6 +979,15 @@ TreeWalker.prototype = {
|| p instanceof AST_Conditional || p instanceof AST_Conditional
|| p.tail_node() === self) { || p.tail_node() === self) {
self = p; self = p;
} else if (p instanceof AST_Return) {
var fn;
do {
fn = this.parent(++i);
if (!fn) return false;
} while (!(fn instanceof AST_Lambda));
if (fn.name) return false;
self = this.parent(++i);
if (!self || self.TYPE != "Call" || self.expression !== fn) return false;
} else { } else {
return false; return false;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,39 @@
"use strict"; "use strict";
var to_ascii = typeof atob == "undefined" ? function(b64) { var to_ascii, to_base64;
return new Buffer(b64, "base64").toString(); if (typeof Buffer == "undefined") {
} : atob; to_ascii = atob;
var to_base64 = typeof btoa == "undefined" ? function(str) { to_base64 = btoa;
return new Buffer(str).toString("base64"); } else if (typeof Buffer.alloc == "undefined") {
} : btoa; to_ascii = function(b64) {
return new Buffer(b64, "base64").toString();
};
to_base64 = function(str) {
return new Buffer(str).toString("base64");
};
} else {
to_ascii = function(b64) {
return Buffer.from(b64, "base64").toString();
};
to_base64 = function(str) {
return Buffer.from(str).toString("base64");
};
}
function read_source_map(name, code) { function read_source_map(name, toplevel) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(\S+)\s*$/.exec(code); var comments = toplevel.end.comments_after;
if (!match) { for (var i = comments.length; --i >= 0;) {
AST_Node.warn("inline source map not found: " + name); var comment = comments[i];
return null; if (comment.type != "comment1") break;
var match = /^# ([^\s=]+)=(\S+)\s*$/.exec(comment.value);
if (!match) break;
if (match[1] == "sourceMappingURL") {
match = /^data:application\/json(;.*?)?;base64,(\S+)$/.exec(match[2]);
if (!match) break;
return to_ascii(match[2]);
}
} }
return to_ascii(match[2]); AST_Node.warn("inline source map not found: " + name);
} }
function parse_source_map(content) { function parse_source_map(content) {
@@ -134,10 +154,10 @@ function minify(files, options) {
source_maps = source_map_content && Object.create(null); source_maps = source_map_content && Object.create(null);
for (var name in files) if (HOP(files, name)) { for (var name in files) if (HOP(files, name)) {
options.parse.filename = name; options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse); options.parse.toplevel = toplevel = parse(files[name], options.parse);
if (source_maps) { if (source_maps) {
if (source_map_content == "inline") { if (source_map_content == "inline") {
var inlined_content = read_source_map(name, files[name]); var inlined_content = read_source_map(name, toplevel);
if (inlined_content) { if (inlined_content) {
source_maps[name] = parse_source_map(inlined_content); source_maps[name] = parse_source_map(inlined_content);
} }
@@ -146,7 +166,6 @@ function minify(files, options) {
} }
} }
} }
toplevel = options.parse.toplevel;
} }
if (quoted_props) { if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props); reserve_quoted_keys(toplevel, quoted_props);

View File

@@ -403,7 +403,7 @@
var def = M.definition(); var def = M.definition();
return { return {
type: "Identifier", type: "Identifier",
name: def ? def.mangled_name || def.name : M.name name: def && def.mangled_name || M.name
}; };
}); });

View File

@@ -91,13 +91,11 @@ function OutputStream(options) {
comment_filter = function(comment) { comment_filter = function(comment) {
return comment.type != "comment5" && comments.test(comment.value); return comment.type != "comment5" && comments.test(comment.value);
}; };
} } else if (typeof comments === "function") {
else if (typeof comments === "function") {
comment_filter = function(comment) { comment_filter = function(comment) {
return comment.type != "comment5" && comments(this, comment); return comment.type != "comment5" && comments(this, comment);
}; };
} } else if (comments === "some") {
else if (comments === "some") {
comment_filter = is_some_comments; comment_filter = is_some_comments;
} else { // NOTE includes "all" option } else { // NOTE includes "all" option
comment_filter = return_true; comment_filter = return_true;
@@ -136,8 +134,7 @@ function OutputStream(options) {
function make_string(str, quote) { function make_string(str, quote) {
var dq = 0, sq = 0; var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
function(s, i) {
switch (s) { switch (s) {
case '"': ++dq; return '"'; case '"': ++dq; return '"';
case "'": ++sq; return "'"; case "'": ++sq; return "'";
@@ -453,7 +450,7 @@ function OutputStream(options) {
var self = this; var self = this;
var scan = node instanceof AST_Exit && node.value; var scan = node instanceof AST_Exit && node.value;
var comments = dump(node); var comments = dump(node);
if (!comments) return; if (!comments) comments = [];
if (scan) { if (scan) {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
@@ -599,7 +596,6 @@ function OutputStream(options) {
} }
print(encoded); print(encoded);
}, },
encode_string : encode_string,
next_indent : next_indent, next_indent : next_indent,
with_indent : with_indent, with_indent : with_indent,
with_block : with_block, with_block : with_block,
@@ -645,8 +641,7 @@ function OutputStream(options) {
var self = this, generator = self._codegen; var self = this, generator = self._codegen;
if (self instanceof AST_Scope) { if (self instanceof AST_Scope) {
active_scope = self; active_scope = self;
} } else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope; use_asm = active_scope;
} }
function doit() { function doit() {
@@ -1045,11 +1040,9 @@ function OutputStream(options) {
return; return;
} }
b = b.alternative; b = b.alternative;
} } else if (b instanceof AST_StatementWithBody) {
else if (b instanceof AST_StatementWithBody) {
b = b.body; b = b.body;
} } else break;
else break;
} }
force_statement(self.body, output); force_statement(self.body, output);
} }
@@ -1357,7 +1350,7 @@ function OutputStream(options) {
}); });
DEFPRINT(AST_Symbol, function(self, output) { DEFPRINT(AST_Symbol, function(self, output) {
var def = self.definition(); var def = self.definition();
output.print_name(def ? def.mangled_name || def.name : self.name); output.print_name(def && def.mangled_name || self.name);
}); });
DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output) { DEFPRINT(AST_This, function(self, output) {
@@ -1383,8 +1376,27 @@ function OutputStream(options) {
if (regexp.raw_source) { if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/")); str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
} }
str = output.to_utf8(str); output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(seq) {
output.print(str); switch (seq[1]) {
case "\n": return "\\n";
case "\r": return "\\r";
case "\t": return "\t";
case "\b": return "\b";
case "\f": return "\f";
case "\0": return "\0";
case "\x0B": return "\v";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
default: return seq;
}
}).replace(/[\n\r\u2028\u2029]/g, function(c) {
switch (c) {
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
}
}));
var p = output.parent(); var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
output.print(" "); output.print(" ");

View File

@@ -234,6 +234,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
directives : {}, directives : {},
directive_stack : [] directive_stack : []
}; };
var prev_was_dot = false;
function peek() { function peek() {
return S.text.charAt(S.pos); return S.text.charAt(S.pos);
@@ -286,16 +287,12 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
S.tokpos = S.pos; S.tokpos = S.pos;
} }
var prev_was_dot = false;
function token(type, value, is_comment) { function token(type, value, is_comment) {
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX[value]) || S.regex_allowed = type == "operator" && !UNARY_POSTFIX[value]
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION[value]) || || type == "keyword" && KEYWORDS_BEFORE_EXPRESSION[value]
(type == "punc" && PUNC_BEFORE_EXPRESSION[value])); || type == "punc" && PUNC_BEFORE_EXPRESSION[value];
if (type == "punc" && value == ".") { if (type == "punc" && value == ".") prev_was_dot = true;
prev_was_dot = true; else if (!is_comment) prev_was_dot = false;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = { var ret = {
type : type, type : type,
value : value, value : value,
@@ -358,11 +355,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
parse_error("Legacy octal literals are not allowed in strict mode"); parse_error("Legacy octal literals are not allowed in strict mode");
} }
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) { if (!isNaN(valid)) return token("num", valid);
return token("num", valid); parse_error("Invalid syntax: " + num);
} else {
parse_error("Invalid syntax: " + num);
}
} }
function read_escaped_char(in_string) { function read_escaped_char(in_string) {
@@ -463,8 +457,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if (ch == "\\") escaped = backslash = true, next(); if (ch == "\\") escaped = backslash = true, next();
else if (is_identifier_char(ch)) name += next(); else if (is_identifier_char(ch)) name += next();
else break; else break;
} } else {
else {
if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX"); if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
ch = read_escaped_char(); ch = read_escaped_char();
if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
@@ -538,9 +531,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function handle_dot() { function handle_dot() {
next(); next();
return is_digit(peek().charCodeAt(0)) return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", ".");
? read_num(".")
: token("punc", ".");
} }
function read_word() { function read_word() {
@@ -592,11 +583,10 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
switch (code) { switch (code) {
case 34: case 39: return read_string(ch); case 34: case 39: return read_string(ch);
case 46: return handle_dot(); case 46: return handle_dot();
case 47: { case 47:
var tok = handle_slash(); var tok = handle_slash();
if (tok === next_token) continue; if (tok === next_token) continue;
return tok; return tok;
}
} }
if (is_digit(code)) return read_num(); if (is_digit(code)) return read_num();
if (PUNC_CHARS[ch]) return token("punc", next()); if (PUNC_CHARS[ch]) return token("punc", next());
@@ -614,12 +604,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
next_token.add_directive = function(directive) { next_token.add_directive = function(directive) {
S.directive_stack[S.directive_stack.length - 1].push(directive); S.directive_stack[S.directive_stack.length - 1].push(directive);
if (S.directives[directive]) S.directives[directive]++;
if (S.directives[directive] === undefined) { else S.directives[directive] = 1;
S.directives[directive] = 1;
} else {
S.directives[directive]++;
}
} }
next_token.push_directives_stack = function() { next_token.push_directives_stack = function() {
@@ -627,13 +613,10 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
next_token.pop_directives_stack = function() { next_token.pop_directives_stack = function() {
var directives = S.directive_stack[S.directive_stack.length - 1]; var directives = S.directive_stack.pop();
for (var i = directives.length; --i >= 0;) {
for (var i = 0; i < directives.length; i++) {
S.directives[directives[i]]--; S.directives[directives[i]]--;
} }
S.directive_stack.pop();
} }
next_token.has_directive = function(directive) { next_token.has_directive = function(directive) {
@@ -645,27 +628,17 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
/* -----[ Parser (constants) ]----- */ /* -----[ Parser (constants) ]----- */
var UNARY_PREFIX = makePredicate([ var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
"typeof",
"void",
"delete",
"--",
"++",
"!",
"~",
"-",
"+"
]);
var UNARY_POSTFIX = makePredicate([ "--", "++" ]); var UNARY_POSTFIX = makePredicate("-- ++");
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); var ASSIGNMENT = makePredicate("= += -= /= *= %= >>= <<= >>>= |= ^= &=");
var PRECEDENCE = function(a, ret) { var PRECEDENCE = function(a, ret) {
for (var i = 0; i < a.length; ++i) { for (var i = 0; i < a.length;) {
var b = a[i]; var b = a[i++];
for (var j = 0; j < b.length; ++j) { for (var j = 0; j < b.length; j++) {
ret[b[j]] = i + 1; ret[b[j]] = i;
} }
} }
return ret; return ret;
@@ -682,7 +655,7 @@ var PRECEDENCE = function(a, ret) {
["*", "/", "%"] ["*", "/", "%"]
], {}); ], {});
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]); var ATOMIC_START_TOKEN = makePredicate("atom num string regexp name");
/* -----[ Parser ]----- */ /* -----[ Parser ]----- */
@@ -698,10 +671,9 @@ function parse($TEXT, options) {
}, true); }, true);
var S = { var S = {
input : (typeof $TEXT == "string" input : typeof $TEXT == "string"
? tokenizer($TEXT, options.filename, ? tokenizer($TEXT, options.filename, options.html5_comments, options.shebang)
options.html5_comments, options.shebang) : $TEXT,
: $TEXT),
token : null, token : null,
prev : null, prev : null,
peeked : null, peeked : null,
@@ -757,15 +729,12 @@ function parse($TEXT, options) {
} }
function unexpected(token) { function unexpected(token) {
if (token == null) if (token == null) token = S.token;
token = S.token;
token_error(token, "Unexpected token: " + token_to_string(token.type, token.value)); token_error(token, "Unexpected token: " + token_to_string(token.type, token.value));
} }
function expect_token(type, val) { function expect_token(type, val) {
if (is(type, val)) { if (is(type, val)) return next();
return next();
}
token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val)); token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val));
} }

View File

@@ -55,6 +55,7 @@ function SymbolDef(scope, orig, init) {
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
this.lambda = orig instanceof AST_SymbolLambda;
} }
SymbolDef.next_id = 1; SymbolDef.next_id = 1;
@@ -191,13 +192,21 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 3: fix up any scoping issue with IE8 // pass 3: fix up any scoping issue with IE8
if (options.ie8) self.walk(new TreeWalker(function(node) { if (options.ie8) self.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
redefine(node, node.thedef.defun); var scope = node.thedef.defun;
if (scope.name instanceof AST_SymbolLambda && scope.name.name == node.name) {
scope = scope.parent_scope.resolve();
}
redefine(node, scope);
return true; return true;
} }
if (node instanceof AST_SymbolLambda) { if (node instanceof AST_SymbolLambda) {
var def = node.thedef; var def = node.thedef;
redefine(node, node.scope.parent_scope); redefine(node, node.scope.parent_scope.resolve());
node.thedef.init = def.init; if (typeof node.thedef.init !== "undefined") {
node.thedef.init = false;
} else if (def.init) {
node.thedef.init = def.init;
}
return true; return true;
} }
})); }));
@@ -210,6 +219,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = new_def; node.thedef = new_def;
node.reference(options); node.reference(options);
}); });
if (old_def.lambda) new_def.lambda = true;
if (new_def.undeclared) self.variables.set(name, new_def);
} }
}); });
@@ -281,9 +292,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name); var def = this.variables.get(symbol.name);
if (def) { if (def) {
def.orig.push(symbol); def.orig.push(symbol);
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) { if (def.init instanceof AST_Function) def.init = init;
def.init = init;
}
} else { } else {
def = new SymbolDef(this, symbol, init); def = new SymbolDef(this, symbol, init);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
@@ -413,7 +422,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
if (options.cache && node instanceof AST_Toplevel) { if (options.cache && node instanceof AST_Toplevel) {
node.globals.each(mangle); node.globals.each(mangle);
} }
node.variables.each(mangle); node.variables.each(function(def) {
if (!defer_redef(def)) mangle(def);
});
return true; return true;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
@@ -426,22 +437,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
} }
if (!options.ie8 && node instanceof AST_Catch) { if (!options.ie8 && node instanceof AST_Catch) {
var def = node.argname.definition(); var def = node.argname.definition();
var redef = def.redefined(); var redef = defer_redef(def, node.argname);
if (redef) {
redefined.push(def);
reference(node.argname);
def.references.forEach(reference);
}
descend(); descend();
if (!redef) mangle(def); if (!redef) mangle(def);
return true; return true;
} }
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
}); });
this.walk(tw); this.walk(tw);
redefined.forEach(mangle); redefined.forEach(mangle);
@@ -450,6 +450,21 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
if (options.reserved.has[def.name]) return; if (options.reserved.has[def.name]) return;
def.mangle(options); def.mangle(options);
} }
function defer_redef(def, node) {
var redef = def.redefined();
if (!redef) return false;
redefined.push(def);
def.references.forEach(reference);
if (node) reference(node);
return true;
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
}
}); });
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
@@ -499,13 +514,14 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
if (def.global && options.cache) return; if (def.global && options.cache) return;
if (def.unmangleable(options)) return; if (def.unmangleable(options)) return;
if (options.reserved.has[def.name]) return; if (options.reserved.has[def.name]) return;
var d = def.redefined(); var redef = def.redefined();
def.name = d ? d.name : next_name(); var name = redef ? redef.rename || redef.name : next_name();
def.rename = name;
def.orig.forEach(function(sym) { def.orig.forEach(function(sym) {
sym.name = def.name; sym.name = name;
}); });
def.references.forEach(function(sym) { def.references.forEach(function(sym) {
sym.name = def.name; sym.name = name;
}); });
} }
}); });

View File

@@ -127,8 +127,7 @@ var MAP = (function() {
} else { } else {
top.push(val); top.push(val);
} }
} } else if (val !== skip) {
else if (val !== skip) {
if (val instanceof Splice) { if (val instanceof Splice) {
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
} else { } else {
@@ -145,8 +144,7 @@ var MAP = (function() {
} else { } else {
for (i = 0; i < a.length; ++i) if (doit()) break; for (i = 0; i < a.length; ++i) if (doit()) break;
} }
} } else {
else {
for (i in a) if (HOP(a, i)) if (doit()) break; for (i in a) if (HOP(a, i)) if (doit()) break;
} }
return top.concat(ret); return top.concat(ret);

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.5.11", "version": "3.6.4",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -23,15 +23,15 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.20.0", "commander": "~2.20.3",
"source-map": "~0.6.1" "source-map": "~0.6.1"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~6.1.1", "acorn": "~7.1.0",
"semver": "~6.0.0" "semver": "~6.3.0"
}, },
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/compress.js && node test/mocha.js"
}, },
"keywords": [ "keywords": [
"cli", "cli",

View File

@@ -13,15 +13,15 @@ if (!args.length) {
} }
args.push("--timings"); args.push("--timings");
var urls = [ var urls = [
"https://code.jquery.com/jquery-3.2.1.js", "https://code.jquery.com/jquery-3.4.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js", "https://code.angularjs.org/1.7.8/angular.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js", "https://unpkg.com/mathjs@6.2.3/dist/math.js",
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://unpkg.com/react@15.3.2/dist/react.js", "https://unpkg.com/react@15.3.2/dist/react.js",
"http://builds.emberjs.com/tags/v2.11.0/ember.prod.js", "https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.js",
"https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js", "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js",
"https://raw.githubusercontent.com/kangax/html-minifier/v3.5.7/dist/htmlminifier.js", "https://cdnjs.cloudflare.com/ajax/libs/ember.js/2.12.2/ember.prod.js",
"https://raw.githubusercontent.com/kangax/html-minifier/v4.0.0/dist/htmlminifier.js",
]; ];
var results = {}; var results = {};
var remaining = 2 * urls.length; var remaining = 2 * urls.length;
@@ -94,3 +94,6 @@ urls.forEach(function(url) {
}); });
}); });
}); });
setInterval(function() {
process.stderr.write("\0");
}, 5 * 60 * 1000).unref();

450
test/compress.js Normal file
View File

@@ -0,0 +1,450 @@
var assert = require("assert");
var child_process = require("child_process");
var fs = require("fs");
var path = require("path");
var sandbox = require("./sandbox");
var semver = require("semver");
var U = require("./node");
var file = process.argv[2];
var dir = path.resolve(path.dirname(module.filename), "compress");
if (file) {
var minify_options = require("./ufuzz/options.json").map(JSON.stringify);
log("--- {file}", { file: file });
var tests = parse_test(path.resolve(dir, file));
process.exit(Object.keys(tests).filter(function(name) {
return !test_case(tests[name]);
}).length);
} else {
var files = fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
});
var failures = 0;
var failed_files = Object.create(null);
(function next() {
var file = files.shift();
if (file) {
child_process.spawn(process.argv[0], [ process.argv[1], file ], {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) {
failures += code;
failed_files[file] = code;
}
next();
});
} else if (failures) {
console.error();
console.error("!!! Failed " + failures + " test case(s).");
console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1);
}
})();
}
function evaluate(code) {
if (code instanceof U.AST_Node) code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")();
}
function log() {
console.log("%s", tmpl.apply(null, arguments));
}
function make_code(ast, options) {
var stream = U.OutputStream(options);
ast.print(stream);
return stream.get();
}
function parse_test(file) {
var script = fs.readFileSync(file, "utf8");
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348
try {
var ast = U.parse(script, {
filename: file
});
} catch (e) {
console.error("Caught error while parsing tests in " + file);
console.error(e);
process.exit(1);
}
var tests = Object.create(null);
var tw = new U.TreeWalker(function(node, descend) {
if (node instanceof U.AST_LabeledStatement
&& tw.parent() instanceof U.AST_Toplevel) {
var name = node.label.name;
if (name in tests) {
throw new Error('Duplicated test name "' + name + '" in ' + file);
}
tests[name] = get_one_test(name, node.body);
return true;
}
if (!(node instanceof U.AST_Toplevel)) croak(node);
});
ast.walk(tw);
return tests;
function croak(node) {
throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", {
file: file,
line: node.start.line,
col: node.start.col,
code: make_code(node, { beautify: false })
}));
}
function read_string(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
switch(body.TYPE) {
case "String":
return body.value;
case "Array":
return body.elements.map(function(element) {
if (element.TYPE !== "String")
throw new Error("Should be array of strings");
return element.value;
}).join("\n");
}
}
throw new Error("Should be string or array of strings");
}
function get_one_test(name, block) {
var test = { name: name, options: {} };
var tw = new U.TreeWalker(function(node, descend) {
if (node instanceof U.AST_Assign) {
if (!(node.left instanceof U.AST_SymbolRef)) {
croak(node);
}
var name = node.left.name;
test[name] = evaluate(node.right);
return true;
}
if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
assert.ok([
"input",
"expect",
"expect_exact",
"expect_warnings",
"expect_stdout",
"node_version",
].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name,
line: label.start.line,
col: label.start.col
}));
var stat = node.body;
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
var body = stat.body;
if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
} else {
test[label.name] = read_string(stat) + "\n";
}
} else {
test[label.name] = stat;
}
return true;
}
});
block.walk(tw);
return test;
}
}
// Try to reminify original input with standard options
// to see if it matches expect_stdout.
function reminify(orig_options, input_code, input_formatted, 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",
"---INPUT---",
"{input}",
"---OPTIONS---",
"{options}",
"--ERROR---",
"{error}",
"",
"",
].join("\n"), {
input: input_formatted,
options: options_formatted,
error: result.error,
});
return false;
} else {
var expected = stdout[options.toplevel ? 1 : 0];
var actual = run_code(result.code, options.toplevel);
if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
actual = expected;
}
if (!sandbox.same_stdout(expected, actual)) {
log([
"!!! failed running reminified input",
"---INPUT---",
"{input}",
"---OPTIONS---",
"{options}",
"---OUTPUT---",
"{output}",
"---EXPECTED {expected_type}---",
"{expected}",
"---ACTUAL {actual_type}---",
"{actual}",
"",
"",
].join("\n"), {
input: input_formatted,
options: options_formatted,
output: result.code,
expected_type: typeof expected == "string" ? "STDOUT" : "ERROR",
expected: expected,
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
actual: actual,
});
return false;
}
}
}
return true;
}
function run_code(code, toplevel) {
var result = sandbox.run_code(code, toplevel);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}
function test_case(test) {
log(" Running test [{name}]", { name: test.name });
var output_options = test.beautify || {};
var expect;
if (test.expect) {
expect = make_code(to_toplevel(test.expect, test.mangle), output_options);
} else {
expect = test.expect_exact;
}
var input = to_toplevel(test.input, test.mangle);
var input_code = make_code(input);
var input_formatted = make_code(test.input, {
beautify: true,
comments: "all",
keep_quoted_props: true,
quote_style: 3,
});
try {
U.parse(input_code);
} catch (ex) {
log([
"!!! Cannot parse input",
"---INPUT---",
"{input}",
"--PARSE ERROR--",
"{error}",
"",
"",
].join("\n"), {
input: input_formatted,
error: ex,
});
return false;
}
var warnings_emitted = [];
if (test.expect_warnings) {
var expected_warnings = make_code(test.expect_warnings, {
beautify: false,
quote_style: 2, // force double quote to match JSON
});
U.AST_Node.log_function(function(text) {
warnings_emitted.push(text);
}, /"INFO: /.test(expected_warnings));
}
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props);
}
if (test.rename) {
input.figure_out_scope(test.mangle);
input.expand_names(test.mangle);
}
var cmp = new U.Compressor(test.options, true);
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle) {
output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle);
if (test.mangle.properties) {
output = U.mangle_properties(output, test.mangle.properties);
}
}
output = make_code(output, output_options);
if (expect != output) {
log([
"!!! failed",
"---INPUT---",
"{input}",
"---OUTPUT---",
"{output}",
"---EXPECTED---",
"{expected}",
"",
"",
].join("\n"), {
input: input_formatted,
output: output,
expected: expect
});
return false;
}
// expect == output
try {
U.parse(output);
} catch (ex) {
log([
"!!! Test matched expected result but cannot parse output",
"---INPUT---",
"{input}",
"---OUTPUT---",
"{output}",
"--REPARSE ERROR--",
"{error}",
"",
"",
].join("\n"), {
input: input_formatted,
output: output,
error: ex,
});
return false;
}
if (test.expect_warnings) {
warnings_emitted = warnings_emitted.map(function(input) {
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
});
var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) {
log([
"!!! failed",
"---INPUT---",
"{input}",
"---EXPECTED WARNINGS---",
"{expected_warnings}",
"---ACTUAL WARNINGS---",
"{actual_warnings}",
"",
"",
].join("\n"), {
input: input_formatted,
expected_warnings: expected_warnings,
actual_warnings: actual_warnings,
});
return false;
}
}
if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = [ run_code(input_code), run_code(input_code, true) ];
var toplevel = test.options.toplevel;
var actual = stdout[toplevel ? 1 : 0];
if (test.expect_stdout === true) {
test.expect_stdout = actual;
}
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! Invalid input or expected stdout",
"---INPUT---",
"{input}",
"---EXPECTED {expected_type}---",
"{expected}",
"---ACTUAL {actual_type}---",
"{actual}",
"",
"",
].join("\n"), {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
actual: actual,
});
return false;
}
actual = run_code(output, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! failed",
"---INPUT---",
"{input}",
"---EXPECTED {expected_type}---",
"{expected}",
"---ACTUAL {actual_type}---",
"{actual}",
"",
"",
].join("\n"), {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
actual: actual,
});
return false;
}
if (!reminify(test.options, input_code, input_formatted, stdout)) {
return false;
}
}
return true;
}
function tmpl() {
return U.string_template.apply(null, arguments);
}
function to_toplevel(input, mangle_options) {
if (!(input instanceof U.AST_BlockStatement)) throw new Error("Unsupported input syntax");
var directive = true;
var offset = input.start.line;
var tokens = [];
var toplevel = new U.AST_Toplevel(input.transform(new U.TreeTransformer(function(node) {
if (U.push_uniq(tokens, node.start)) node.start.line -= offset;
if (!directive || node === input) return;
if (node instanceof U.AST_SimpleStatement && node.body instanceof U.AST_String) {
return new U.AST_Directive(node.body);
} else {
directive = false;
}
})));
toplevel.figure_out_scope(mangle_options);
return toplevel;
}

View File

@@ -405,6 +405,52 @@ issue_3273_global_strict_reduce_vars: {
] ]
} }
issue_3273_keep_fargs_false: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
(function() {
"use strict";
arguments[0]++;
console.log(arguments[0]);
})(0);
}
expect: {
(function(argument_0) {
"use strict";
argument_0++;
console.log(argument_0);
})(0);
}
expect_stdout: "1"
}
issue_3273_keep_fargs_strict: {
options = {
arguments: true,
keep_fargs: "strict",
reduce_vars: true,
}
input: {
(function() {
"use strict";
arguments[0]++;
console.log(arguments[0]);
})(0);
}
expect: {
(function(argument_0) {
"use strict";
argument_0++;
console.log(argument_0);
})(0);
}
expect_stdout: "1"
}
issue_3282_1: { issue_3282_1: {
options = { options = {
arguments: true, arguments: true,
@@ -576,3 +622,157 @@ issue_3282_2_passes: {
} }
expect_stdout: true expect_stdout: true
} }
issue_3420_1: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
return function() {
return arguments[0];
};
}().length);
}
expect: {
console.log(function() {
return function() {
return arguments[0];
};
}().length);
}
expect_stdout: "0"
}
issue_3420_2: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
var foo = function() {
delete arguments[0];
};
foo();
}
expect: {
var foo = function() {
delete arguments[0];
};
foo();
}
expect_stdout: true
}
issue_3420_3: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
var foo = function() {
delete arguments[0];
};
foo();
}
expect: {
"use strict";
var foo = function() {
delete arguments[0];
};
foo();
}
expect_stdout: true
}
issue_3420_4: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
!function() {
console.log(arguments[0]);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect: {
!function(argument_0) {
console.log(argument_0);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect_stdout: [
"42",
"undefined",
]
}
issue_3420_5: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
!function() {
console.log(arguments[0]);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect: {
"use strict";
!function(argument_0) {
console.log(argument_0);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect_stdout: [
"42",
"undefined",
]
}
issue_3420_6: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
return delete arguments[0];
}());
}
expect: {
console.log(function() {
return delete arguments[0];
}());
}
expect_stdout: "true"
}
issue_3420_7: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
console.log(function() {
return delete arguments[0];
}());
}
expect: {
"use strict";
console.log(function() {
return delete arguments[0];
}());
}
expect_stdout: "true"
}

View File

@@ -311,3 +311,65 @@ issue_3375: {
} }
expect_stdout: "string" expect_stdout: "string"
} }
issue_3427: {
options = {
assignments: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a || (a = {});
})();
}
expect: {}
}
issue_3429_1: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a = "PASS";
(function(b) {
b && (b = a = "FAIL");
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(b) {
b = b && (a = "FAIL");
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_3429_2: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a;
(function(b) {
b || (b = a = "FAIL");
})(42);
console.log(a);
}
expect: {
var a;
(function(b) {
b = b || (a = "FAIL");
})(42);
console.log(a);
}
expect_stdout: "undefined"
}

88
test/compress/booleans.js Normal file
View File

@@ -0,0 +1,88 @@
iife_boolean_context: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(function() {
return Object(1) || false;
}() ? "PASS" : "FAIL");
console.log(function() {
return [].length || true;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function() {
return Object(1);
}() ? "PASS" : "FAIL");
console.log(function() {
return [].length, 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: [
"PASS",
"PASS",
]
expect_warnings: [
"WARN: Dropping side-effect-free || [test/compress/booleans.js:2,19]",
"WARN: Boolean || always true [test/compress/booleans.js:5,19]",
]
}
issue_3465_1: {
options = {
booleans: true,
}
input: {
console.log(function(a) {
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function(a) {
return 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_3465_2: {
options = {
booleans: true,
}
input: {
console.log(function f(a) {
if (!a) console.log(f(42));
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function f(a) {
if (!a) console.log(f(42));
return typeof a;
}() ? "PASS" : "FAIL");
}
expect_stdout: [
"number",
"PASS",
]
}
issue_3465_3: {
options = {
booleans: true,
passes: 2,
unused: true,
}
input: {
console.log(function f(a) {
return typeof a;
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function(a) {
return 1;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}

View File

@@ -3497,10 +3497,10 @@ issue_2437_1: {
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result; result;
} }
var req, detectFunc = function(){}; var req = new XMLHttpRequest(), detectFunc = function(){};
(req = new XMLHttpRequest()).onreadystatechange = detectFunc; return req.onreadystatechange = detectFunc,
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
return req.onreadystatechange = null, result; req.onreadystatechange = null, result;
}()); }());
} }
} }
@@ -3511,7 +3511,7 @@ issue_2437_2: {
conditionals: true, conditionals: true,
inline: true, inline: true,
join_vars: true, join_vars: true,
passes: 2, passes: 3,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
sequences: true, sequences: true,
@@ -3545,8 +3545,8 @@ issue_2437_2: {
if (xhrDesc) if (xhrDesc)
return (req = new XMLHttpRequest()).onreadystatechange, return (req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}); Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {});
var req; var req = new XMLHttpRequest();
(req = new XMLHttpRequest).onreadystatechange = function(){}, req.onreadystatechange = function(){},
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1], req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
req.onreadystatechange = null; req.onreadystatechange = null;
}(); }();
@@ -4388,7 +4388,7 @@ replace_all_var: {
} }
replace_all_var_scope: { replace_all_var_scope: {
rename = true; rename = true
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true, unused: true,
@@ -6178,3 +6178,83 @@ assign_undeclared: {
"object", "object",
] ]
} }
Infinity_assignment: {
options = {
collapse_vars: true,
pure_getters: "strict",
unsafe: true,
}
input: {
var Infinity;
Infinity = 42;
console.log(Infinity);
}
expect: {
var Infinity;
Infinity = 42;
console.log(Infinity);
}
expect_stdout: true
}
issue_3439_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect_stdout: "function"
}
issue_3439_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
var a = 42;
function a() {}
return a;
}());
}
expect: {
console.log(typeof function() {
return 42;
}());
}
expect_stdout: "number"
}
cond_sequence_return: {
options = {
collapse_vars: true,
}
input: {
console.log(function(n) {
var c = 0;
for (var k in [0, 1])
if (c++, k == n) return c;
}(1));
}
expect: {
console.log(function(n) {
var c = 0;
for (var k in [0, 1])
if (c++, k == n) return c;
}(1));
}
expect_stdout: "2"
}

View File

@@ -373,10 +373,70 @@ unsafe_indexOf: {
unsafe: true, unsafe: true,
} }
input: { input: {
if (Object.keys({ foo: 42 }).indexOf("foo") >= 0) console.log("PASS"); var a = Object.keys({ foo: 42 });
if (a.indexOf("bar") < 0) console.log("PASS");
if (0 > a.indexOf("bar")) console.log("PASS");
if (a.indexOf("foo") >= 0) console.log("PASS");
if (0 <= a.indexOf("foo")) console.log("PASS");
if (a.indexOf("foo") > -1) console.log("PASS");
if (-1 < a.indexOf("foo")) console.log("PASS");
if (a.indexOf("bar") == -1) console.log("PASS");
if (-1 == a.indexOf("bar")) console.log("PASS");
if (a.indexOf("bar") === -1) console.log("PASS");
if (-1 === a.indexOf("bar")) console.log("PASS");
if (a.indexOf("foo") != -1) console.log("PASS");
if (-1 != a.indexOf("foo")) console.log("PASS");
if (a.indexOf("foo") !== -1) console.log("PASS");
if (-1 !== a.indexOf("foo")) console.log("PASS");
} }
expect: { expect: {
if (~Object.keys({ foo: 42 }).indexOf("foo")) console.log("PASS"); var a = Object.keys({ foo: 42 });
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (!~a.indexOf("bar")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
if (~a.indexOf("foo")) console.log("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
"PASS",
]
}
issue_3413: {
options = {
comparisons: true,
evaluate: true,
side_effects: true,
}
input: {
var b;
void 0 !== ("" < b || void 0) || console.log("PASS");
}
expect: {
var b;
void 0 !== ("" < b || void 0) || console.log("PASS");
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }

View File

@@ -960,3 +960,56 @@ unsafe_string_replace: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3402: {
options = {
dead_code: true,
evaluate: true,
functions: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var f = function f() {
f = 42;
console.log(typeof f);
};
"function" == typeof f && f();
"function" == typeof f && f();
console.log(typeof f);
}
expect: {
function f() {
console.log(typeof f);
}
f();
f();
console.log(typeof f);
}
expect_stdout: [
"function",
"function",
"function",
]
}
issue_3406: {
options = {
dead_code: true,
}
input: {
console.log(function f(a) {
return delete (f = a);
}());
}
expect: {
console.log(function f(a) {
return delete (0, a);
}());
}
expect_stdout: "true"
}

View File

@@ -797,6 +797,7 @@ assign_chain: {
issue_1583: { issue_1583: {
options = { options = {
keep_fargs: true, keep_fargs: true,
passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
@@ -1004,7 +1005,7 @@ issue_1715_4: {
delete_assign_1: { delete_assign_1: {
options = { options = {
booleans: true, booleans: true,
side_effects: true, evaluate: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
} }
@@ -1023,7 +1024,7 @@ delete_assign_1: {
console.log((1 / 0, !0)); console.log((1 / 0, !0));
console.log((1 / 0, !0)); console.log((1 / 0, !0));
console.log((NaN, !0)); console.log((NaN, !0));
console.log((0 / 0, !0)); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -1031,8 +1032,8 @@ delete_assign_1: {
delete_assign_2: { delete_assign_2: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
keep_infinity: true, keep_infinity: true,
side_effects: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
} }
@@ -1051,7 +1052,7 @@ delete_assign_2: {
console.log((Infinity, !0)); console.log((Infinity, !0));
console.log((1 / 0, !0)); console.log((1 / 0, !0));
console.log((NaN, !0)); console.log((NaN, !0));
console.log((0 / 0, !0)); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -1144,6 +1145,7 @@ var_catch_toplevel: {
options = { options = {
conditionals: true, conditionals: true,
negate_iife: true, negate_iife: true,
passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -2026,3 +2028,161 @@ issue_3375: {
} }
expect_stdout: "0 0" expect_stdout: "0 0"
} }
issue_3427_1: {
options = {
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a = a || {};
})();
}
expect: {}
}
issue_3427_2: {
options = {
unused: true,
}
input: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect_stdout: "PASS"
}
issue_3495: {
options = {
dead_code: true,
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
console.log(function f() {
f = 0;
var a = f.p;
}());
}
expect: {
console.log(void 0);
}
expect_stdout: "undefined"
}
issue_3497: {
options = {
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
var a;
console.log(function(b) {
(b += a).p = 0;
}());
}
expect: {
var a;
console.log(function(b) {
(b += a).p = 0;
}());
}
expect_stdout: "undefined"
}
issue_3515_1: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
var c = 0;
(function() {
this[c++] = 0;
var expr20 = !0;
for (var key20 in expr20);
})();
console.log(c);
}
expect: {
var c = 0;
(function() {
for (var key20 in !(this[c++] = 0));
})();
console.log(c);
}
expect_stdout: "1"
}
issue_3515_2: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL";
function f() {
typeof b === "number";
delete a;
}
var b = f(a = "PASS");
console.log(a);
}
expect: {
var a = "FAIL";
function f() {
delete a;
}
f(a = "PASS");
console.log(a);
}
expect_stdout: "PASS"
}
issue_3515_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var c = "FAIL";
(function() {
function f() {
c = "PASS";
}
var a = f();
var a = function g(b) {
b && (b.p = this);
}(a);
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
function f() {
c = "PASS";
}
(function(b) {
b && (b.p = this);
})(f());
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -1150,7 +1150,7 @@ issue_2620_3: {
} }
issue_2620_4: { issue_2620_4: {
rename = true, rename = true
options = { options = {
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
@@ -2860,10 +2860,10 @@ issue_2437: {
result; result;
} }
function detectFunc() {} function detectFunc() {}
var req; var req = new XMLHttpRequest();
(req = new XMLHttpRequest()).onreadystatechange = detectFunc; return req.onreadystatechange = detectFunc,
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
return req.onreadystatechange = null, result; req.onreadystatechange = null, result;
}()); }());
} }
} }
@@ -3065,3 +3065,308 @@ class_iife: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3400_1: {
options = {
collapse_vars: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(f) {
console.log(f()()[0].p);
})(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
}
return g;
});
}
expect: {
void console.log(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
}
return g;
}()()[0].p);
}
expect_stdout: [
"undefined",
"42",
]
}
issue_3400_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(f) {
console.log(f()()[0].p);
})(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
}
return g;
});
}
expect: {
void console.log(function g() {
return [ 42 ].map(function(v) {
return function(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}(v);
});
}()[0].p);
}
expect_stdout: [
"undefined",
"42",
]
}
issue_3402: {
options = {
evaluate: true,
functions: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var f = function f() {
f = 42;
console.log(typeof f);
};
"function" == typeof f && f();
"function" == typeof f && f();
console.log(typeof f);
}
expect: {
var f = function f() {
f = 42;
console.log(typeof f);
};
f();
f();
console.log(typeof f);
}
expect_stdout: [
"function",
"function",
"function",
]
}
issue_3439: {
options = {
inline: true,
}
input: {
console.log(typeof function() {
return function(a) {
function a() {}
return a;
}(42);
}());
}
expect: {
console.log(typeof function(a) {
function a() {}
return a;
}(42));
}
expect_stdout: "function"
}
issue_3444: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function(h) {
return f;
function f() {
g();
}
function g() {
h("PASS");
}
})(console.log)();
}
expect: {
(function(h) {
return function() {
void h("PASS");
};
})(console.log)();
}
expect_stdout: "PASS"
}
issue_3506_1: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = "FAIL";
(function(b) {
(function(b) {
b && (a = "PASS");
})(b);
})(a);
console.log(a);
}
expect: {
var a = "FAIL";
!function(b) {
b && (a = "PASS");
}(a);
console.log(a);
}
expect_stdout: "PASS"
}
issue_3506_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = "FAIL";
(function(b) {
(function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
})(b);
})(a);
console.log(a);
}
expect: {
var a = "FAIL";
!function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
}(a);
console.log(a);
}
expect_stdout: "PASS"
}
issue_3506_3: {
options = {
collapse_vars: true,
dead_code: true,
evaluate: true,
inline: true,
loops: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = "FAIL";
(function(b) {
(function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
})(b);
})(a);
console.log(a);
}
expect: {
var a = "FAIL";
!function(c) {
var d = 1;
for (;c && (a = "PASS") && 0 < --d;);
}(a);
console.log(a);
}
expect_stdout: "PASS"
}
issue_3512: {
options = {
collapse_vars: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
unused: true,
}
input: {
var a = "PASS";
(function(b) {
(function() {
b <<= this || 1;
b.a = "FAIL";
})();
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(b) {
(function() {
(b <<= this || 1).a = "FAIL";
})();
})(),
console.log(a);
}
expect_stdout: "PASS"
}

View File

@@ -862,3 +862,55 @@ issue_3071_3: {
} }
expect_stdout: "2" expect_stdout: "2"
} }
issue_3411: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
var c = 1;
!function f() {
var o = {
p: --c && f()
};
+o || console.log("PASS");
}();
}
expect: {
var c = 1;
!function f() {
var o_p = --c && f();
+{} || console.log("PASS");
}();
}
expect_stdout: "PASS"
}
issue_3440: {
options = {
hoist_props: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
function f() {
console.log(o.p);
}
var o = {
p: "PASS",
};
return f;
})()();
}
expect: {
(function() {
var o_p = "PASS";
return function() {
console.log(o_p);
};
})()();
}
expect_stdout: "PASS"
}

File diff suppressed because it is too large Load Diff

View File

@@ -366,7 +366,7 @@ mangle_catch_redef_3: {
console.log(o); console.log(o);
} }
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);' expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
expect_stdout: "PASS" expect_stdout: true
} }
mangle_catch_redef_3_toplevel: { mangle_catch_redef_3_toplevel: {
@@ -389,10 +389,10 @@ mangle_catch_redef_3_toplevel: {
console.log(o); console.log(o);
} }
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);' expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
expect_stdout: "PASS" expect_stdout: true
} }
mangle_catch_redef_ie8_3: { mangle_catch_redef_3_ie8: {
mangle = { mangle = {
ie8: true, ie8: true,
toplevel: false, toplevel: false,
@@ -412,7 +412,7 @@ mangle_catch_redef_ie8_3: {
console.log(o); console.log(o);
} }
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);' expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
expect_stdout: "PASS" expect_stdout: true
} }
mangle_catch_redef_3_ie8_toplevel: { mangle_catch_redef_3_ie8_toplevel: {
@@ -435,5 +435,5 @@ mangle_catch_redef_3_ie8_toplevel: {
console.log(o); console.log(o);
} }
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);' expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
expect_stdout: "PASS" expect_stdout: true
} }

1157
test/compress/keep_fargs.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -673,3 +673,19 @@ issue_3371: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
step: {
options = {
loops: true,
side_effects: true,
}
input: {
for (var i = 0; i < 42; "foo", i++, "bar");
console.log(i);
}
expect: {
for (var i = 0; i < 42; i++);
console.log(i);
}
expect_stdout: "42"
}

View File

@@ -1161,3 +1161,51 @@ collapse_rhs_lhs: {
} }
expect_stdout: "1 3" expect_stdout: "1 3"
} }
drop_arguments: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
(function() {
arguments.slice = function() {
console.log("PASS");
};
arguments[42];
arguments.length;
arguments.slice();
})();
}
expect: {
(function() {
arguments.slice = function() {
console.log("PASS");
};
arguments.slice();
})();
}
expect_stdout: "PASS"
}
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
passes: 2,
pure_getters: "strict",
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
(function(b) {
b.p = 42;
})(a || (a = {}));
}
expect: {}
expect_stdout: true
}

View File

@@ -4947,7 +4947,7 @@ defun_single_use_loop: {
unused: true, unused: true,
} }
input: { input: {
for (var x, i = 2; --i >= 0; ) { for (var x, i = 2; --i >= 0;) {
var y = x; var y = x;
x = f; x = f;
console.log(x === y); console.log(x === y);
@@ -4955,7 +4955,7 @@ defun_single_use_loop: {
function f() {}; function f() {};
} }
expect: { expect: {
for (var x, i = 2; --i >= 0; ) { for (var x, i = 2; --i >= 0;) {
var y = x; var y = x;
x = f; x = f;
console.log(x === y); console.log(x === y);
@@ -6609,10 +6609,10 @@ issues_3267_1: {
} }
expect: { expect: {
!function(i) { !function(i) {
if (i) if (Object())
return console.log("PASS"); return console.log("PASS");
throw "FAIL"; throw "FAIL";
}(Object()); }();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
@@ -6752,3 +6752,31 @@ issue_3377: {
} }
expect_stdout: "42" expect_stdout: "42"
} }
issue_3509: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function a() {
console.log("PASS");
}
try {
} catch (a) {
var a;
}
a();
}
expect: {
try {
} catch (a) {
var a;
}
(function() {
console.log("PASS");
})();
}
expect_stdout: "PASS"
}

View File

@@ -35,3 +35,140 @@ regexp_2: {
} }
expect_stdout: '["PASS","pass"]' expect_stdout: '["PASS","pass"]'
} }
issue_3434_1: {
options = {
evaluate: true,
unsafe: true,
}
beautify = {
beautify: true,
}
input: {
var o = {
"\n": RegExp("\n"),
"\r": RegExp("\r"),
"\t": RegExp("\t"),
"\b": RegExp("\b"),
"\f": RegExp("\f"),
"\0": RegExp("\0"),
"\x0B": RegExp("\x0B"),
"\u2028": RegExp("\u2028"),
"\u2029": RegExp("\u2029"),
};
for (var c in o)
console.log(o[c].test("\\"), o[c].test(c));
}
expect_exact: [
"var o = {",
' "\\n": /\\n/,',
' "\\r": /\\r/,',
' "\\t": /\t/,',
' "\\b": /\b/,',
' "\\f": /\f/,',
' "\\0": /\0/,',
' "\\v": /\v/,',
' "\\u2028": /\\u2028/,',
' "\\u2029": /\\u2029/',
"};",
"",
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
]
expect_stdout: [
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
]
}
issue_3434_2: {
options = {
evaluate: true,
unsafe: true,
}
beautify = {
beautify: true,
}
input: {
var o = {
"\n": RegExp("\\\n"),
"\r": RegExp("\\\r"),
"\t": RegExp("\\\t"),
"\b": RegExp("\\\b"),
"\f": RegExp("\\\f"),
"\0": RegExp("\\\0"),
"\x0B": RegExp("\\\x0B"),
"\u2028": RegExp("\\\u2028"),
"\u2029": RegExp("\\\u2029"),
};
for (var c in o)
console.log(o[c].test("\\"), o[c].test(c));
}
expect_exact: [
"var o = {",
' "\\n": /\\n/,',
' "\\r": /\\r/,',
' "\\t": /\t/,',
' "\\b": /\b/,',
' "\\f": /\f/,',
' "\\0": /\0/,',
' "\\v": /\v/,',
' "\\u2028": /\\u2028/,',
' "\\u2029": /\\u2029/',
"};",
"",
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
]
expect_stdout: [
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
"false true",
]
}
issue_3434_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
RegExp("\n");
RegExp("\r");
RegExp("\\n");
RegExp("\\\n");
RegExp("\\\\n");
RegExp("\\\\\n");
RegExp("\\\\\\n");
RegExp("\\\\\\\n");
RegExp("\u2028");
RegExp("\u2029");
RegExp("\n\r\u2028\u2029");
RegExp("\\\nfo\n[\n]o\\bbb");
}
expect: {
/\n/;
/\r/;
/\n/;
/\n/;
/\\n/;
/\\\n/;
/\\\n/;
/\\\n/;
/\u2028/;
/\u2029/;
/\n\r\u2028\u2029/;
/\nfo\n[\n]o\bbb/;
}
}

View File

@@ -579,7 +579,7 @@ function_do_catch_ie8: {
console.log(b, c); console.log(b, c);
} }
expect: { expect: {
var t = 1, u = 1, y = 0; var u = 1, y = 1, a = 0;
function c(c) { function c(c) {
var d; var d;
do { do {
@@ -587,7 +587,7 @@ function_do_catch_ie8: {
try { try {
var e = void 0; var e = void 0;
} catch (i) { } catch (i) {
--t && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy"); --u && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy");
0; 0;
0; 0;
0; 0;
@@ -596,18 +596,146 @@ function_do_catch_ie8: {
d[1]; d[1];
} catch (l) { } catch (l) {
var g; var g;
switch(function x() { switch (function n() {
y++; a++;
}()) { }()) {
case e + --g: case e + --g:
} }
} }
} catch (n) {} } catch (t) {}
} while (--d); } while (--d);
u--; y--;
} }
c(); c();
console.log(u, y); console.log(y, a);
} }
expect_stdout: "0 1" expect_stdout: "0 1"
} }
issue_3480: {
rename = true,
mangle = {
ie8: false,
toplevel: false,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var d, a, b, c = "FAIL";
(function n() {
(function() {
try {
c = "PASS";
} catch (c) {}
})();
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3480_ie8: {
rename = true,
mangle = {
ie8: true,
toplevel: false,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {}
})();
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3480_toplevel: {
rename = true,
mangle = {
ie8: false,
toplevel: true,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var c, n, o, t = "FAIL";
(function c() {
(function() {
try {
t = "PASS";
} catch (c) {}
})();
})();
console.log(t);
}
expect_stdout: "PASS"
}
issue_3480_ie8_toplevel: {
rename = true,
mangle = {
ie8: true,
toplevel: true,
}
input: {
var d, a, b, c = "FAIL";
(function b() {
(function() {
try {
c = "PASS";
} catch (b) {
}
})();
})();
console.log(c);
}
expect: {
var c, n, o, t = "FAIL";
(function o() {
(function() {
try {
t = "PASS";
} catch (o) {}
})();
})();
console.log(t);
}
expect_stdout: "PASS"
}

View File

@@ -12,3 +12,71 @@ console_log: {
"% %s", "% %s",
] ]
} }
typeof_arguments: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var arguments;
console.log((typeof arguments).length);
}
expect: {
var arguments;
console.log((typeof arguments).length);
}
expect_stdout: "6"
}
typeof_arguments_assigned: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var arguments = void 0;
console.log((typeof arguments).length);
}
expect: {
console.log("undefined".length);
}
expect_stdout: "9"
}
toplevel_Infinity_NaN_undefined: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var Infinity = "foo";
var NaN = 42;
var undefined = null;
console.log(Infinity, NaN, undefined);
}
expect: {
console.log("foo", 42, null);
}
expect_stdout: "foo 42 null"
}
log_global: {
input: {
console.log(function() {
return this;
}());
}
expect: {
console.log(function() {
return this;
}());
}
expect_stdout: "[object global]"
}

View File

@@ -490,6 +490,7 @@ issue_1758: {
delete_seq_1: { delete_seq_1: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
side_effects: true, side_effects: true,
} }
input: { input: {
@@ -514,6 +515,7 @@ delete_seq_1: {
delete_seq_2: { delete_seq_2: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
side_effects: true, side_effects: true,
} }
input: { input: {
@@ -538,6 +540,7 @@ delete_seq_2: {
delete_seq_3: { delete_seq_3: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
keep_infinity: true, keep_infinity: true,
side_effects: true, side_effects: true,
} }
@@ -563,6 +566,7 @@ delete_seq_3: {
delete_seq_4: { delete_seq_4: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
sequences: true, sequences: true,
side_effects: true, side_effects: true,
} }
@@ -590,6 +594,7 @@ delete_seq_4: {
delete_seq_5: { delete_seq_5: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
keep_infinity: true, keep_infinity: true,
sequences: true, sequences: true,
side_effects: true, side_effects: true,
@@ -618,6 +623,7 @@ delete_seq_5: {
delete_seq_6: { delete_seq_6: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
side_effects: true, side_effects: true,
} }
input: { input: {
@@ -1000,3 +1006,66 @@ angularjs_chain: {
} }
} }
} }
issue_3490_1: {
options = {
conditionals: true,
dead_code: true,
inline: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 42, c = "FAIL";
if ({
3: function() {
var a;
return (a && a.p) < this;
}(),
}) c = "PASS";
if (b) while ("" == typeof d);
console.log(c, b);
}
expect: {
var b = 42, c = "FAIL";
if (function() {
var a;
a && a.p;
}(), c = "PASS", b) while ("" == typeof d);
console.log(c, b);
}
expect_stdout: "PASS 42"
}
issue_3490_2: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var b = 42, c = "FAIL";
if ({
3: function() {
var a;
return (a && a.p) < this;
}(),
}) c = "PASS";
if (b) for (; "" == typeof d;);
console.log(c, b);
}
expect: {
var b = 42, c = "FAIL";
for (function() {
var a;
}(), c = "PASS", b; "" == typeof d;);
console.log(c, b);
}
expect_stdout: "PASS 42"
}

View File

@@ -8,6 +8,7 @@ exports["parse"] = parse;
exports["push_uniq"] = push_uniq; exports["push_uniq"] = push_uniq;
exports["reserve_quoted_keys"] = reserve_quoted_keys; exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["string_template"] = string_template; exports["string_template"] = string_template;
exports["to_ascii"] = to_ascii;
exports["tokenizer"] = tokenizer; exports["tokenizer"] = tokenizer;
exports["TreeTransformer"] = TreeTransformer; exports["TreeTransformer"] = TreeTransformer;
exports["TreeWalker"] = TreeWalker; exports["TreeWalker"] = TreeWalker;

View File

@@ -0,0 +1,8 @@
// Generated by CoffeeScript 2.4.1
(function() {
console.log('hello');
}).call(this);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4uY29mZmVlIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTtFQUFBLE9BQU8sQ0FBQyxHQUFSLENBQVksT0FBWjtBQUFBIiwic291cmNlc0NvbnRlbnQiOlsiY29uc29sZS5sb2cgJ2hlbGxvJ1xuIl19
//# sourceURL=/Users/mohamed/Downloads/main.coffee

View File

@@ -62,8 +62,17 @@ if (typeof phantom == "undefined") {
if (debug) { if (debug) {
console.log("http://localhost:" + port + "/"); console.log("http://localhost:" + port + "/");
} else { } else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) { child_process.spawn(process.platform == "win32" ? "npm.cmd" : "npm", [
if (error) throw error; "install",
"phantomjs-prebuilt@2.1.14",
"--no-audit",
"--no-optional",
"--no-save",
"--no-update-notifier",
], {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) throw new Error("npm install failed!");
var program = require("phantomjs-prebuilt").exec(process.argv[1], port); var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
program.stdout.pipe(process.stdout); program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr); program.stderr.pipe(process.stderr);

View File

@@ -48,53 +48,84 @@ describe("comments", function() {
} }
}); });
it("Should handle comment within return correctly", function() { describe("comment within return", function() {
var result = UglifyJS.minify([ it("Should handle leading return", function() {
"function unequal(x, y) {", var result = UglifyJS.minify([
" return (", "function unequal(x, y) {",
" // Either one", " return (",
" x < y", " // Either one",
" ||", " x < y",
" y < x", " ||",
" );", " y < x",
"}", " );",
].join("\n"), { "}",
compress: false, ].join("\n"), {
mangle: false, compress: false,
output: { mangle: false,
beautify: true, output: {
comments: "all", beautify: true,
}, comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"function unequal(x, y) {",
" // Either one",
" return x < y || y < x;",
"}",
].join("\n"));
}); });
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"function unequal(x, y) {",
" // Either one",
" return x < y || y < x;",
"}",
].join("\n"));
});
it("Should handle comment folded into return correctly", function() { it("Should handle trailing return", function() {
var result = UglifyJS.minify([ var result = UglifyJS.minify([
"function f() {", "function unequal(x) {",
" /* boo */ x();", " var y;",
" return y();", " return (",
"}", " // Either one",
].join("\n"), { " x < y",
mangle: false, " ||",
output: { " y < x",
beautify: true, " );",
comments: "all", "}",
}, ].join("\n"), {
compress: false,
mangle: false,
output: {
beautify: true,
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"function unequal(x) {",
" var y;",
" // Either one",
" return x < y || y < x;",
"}",
].join("\n"));
});
it("Should handle comment folded into return", function() {
var result = UglifyJS.minify([
"function f() {",
" /* boo */ x();",
" return y();",
"}",
].join("\n"), {
mangle: false,
output: {
beautify: true,
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"function f() {",
" /* boo */",
" return x(), y();",
"}",
].join("\n"));
}); });
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"function f() {",
" /* boo */",
" return x(), y();",
"}",
].join("\n"));
}); });
it("Should not drop comments after first OutputStream", function() { it("Should not drop comments after first OutputStream", function() {

View File

@@ -1,50 +0,0 @@
var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) {
spawn(command, args, {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(10 * 60 * 1000);
[
"-b",
"-b braces",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",
"-mc passes=3,unsafe",
"-mc keep_fargs=false,passes=3",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].forEach(function(options) {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
run(process.argv[0], args, done);
});
});
});
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000);
[
"-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].forEach(function(options) {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done);
});
});
});

View File

@@ -1,7 +1,7 @@
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
var SourceMapConsumer = require("source-map").SourceMapConsumer; var SourceMapConsumer = require("source-map").SourceMapConsumer;
var UglifyJS = require("../.."); var UglifyJS = require("../node");
function read(path) { function read(path) {
return readFileSync(path, "utf8"); return readFileSync(path, "utf8");
@@ -181,6 +181,18 @@ describe("sourcemaps", function() {
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8")); assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8"));
}); });
it("Should work in presence of unrecognised annotations", function() {
var result = UglifyJS.minify(read("./test/input/issue-3441/input.js"), {
compress: false,
mangle: false,
sourceMap: {
content: "inline",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, '(function(){console.log("hello")}).call(this);');
assert.strictEqual(result.map, '{"version":3,"sources":["main.coffee"],"names":["console","log"],"mappings":"CAAA,WAAAA,QAAQC,IAAI"}');
});
}); });
describe("sourceMapInline", function() { describe("sourceMapInline", function() {
@@ -232,7 +244,7 @@ describe("sourcemaps", function() {
assert.strictEqual(map.sourcesContent.length, 1); assert.strictEqual(map.sourcesContent.length, 1);
assert.strictEqual(map.sourcesContent[0], code); assert.strictEqual(map.sourcesContent[0], code);
var encoded = result.code.slice(result.code.lastIndexOf(",") + 1); var encoded = result.code.slice(result.code.lastIndexOf(",") + 1);
map = JSON.parse(new Buffer(encoded, "base64").toString()); map = JSON.parse(UglifyJS.to_ascii(encoded));
assert.strictEqual(map.sourcesContent.length, 1); assert.strictEqual(map.sourcesContent.length, 1);
assert.strictEqual(map.sourcesContent[0], code); assert.strictEqual(map.sourcesContent[0], code);
result = UglifyJS.minify(result.code, { result = UglifyJS.minify(result.code, {

14
test/release/benchmark.js Normal file
View File

@@ -0,0 +1,14 @@
require("./run")([
"-b",
"-b braces",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",
"-mc passes=3,unsafe",
"-mc keep_fargs=false,passes=3",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
return args;
}));

View File

@@ -0,0 +1,9 @@
require("./run")([
"-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
return args;
}));

16
test/release/run.js Normal file
View File

@@ -0,0 +1,16 @@
var child_process = require("child_process");
module.exports = function(tasks) {
(function next() {
if (!tasks.length) return;
var args = tasks.shift();
console.log();
console.log("\u001B[36m$> " + args.join(" ") + "\u001B[39m");
child_process.spawn(process.argv[0], args, {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) process.exit(code);
next();
});
})();
};

View File

@@ -1,362 +0,0 @@
#! /usr/bin/env node
var U = require("./node");
var path = require("path");
var fs = require("fs");
var assert = require("assert");
var sandbox = require("./sandbox");
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) {
console.error("\n!!! Failed " + failures + " test cases.");
console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1);
}
console.log();
require("./mocha.js");
/* -----[ utils ]----- */
function evaluate(code) {
if (code instanceof U.AST_Node) code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")();
}
function log() {
console.log("%s", tmpl.apply(null, arguments));
}
function make_code(ast, options) {
var stream = U.OutputStream(options);
ast.print(stream);
return stream.get();
}
function parse_test(file) {
var script = fs.readFileSync(file, "utf8");
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348
try {
var ast = U.parse(script, {
filename: file
});
} catch (e) {
console.log("Caught error while parsing tests in " + file + "\n");
console.log(e);
throw e;
}
var tests = {};
var tw = new U.TreeWalker(function(node, descend) {
if (node instanceof U.AST_LabeledStatement
&& tw.parent() instanceof U.AST_Toplevel) {
var name = node.label.name;
if (name in tests) {
throw new Error('Duplicated test name "' + name + '" in ' + file);
}
tests[name] = get_one_test(name, node.body);
return true;
}
if (!(node instanceof U.AST_Toplevel)) croak(node);
});
ast.walk(tw);
return tests;
function croak(node) {
throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", {
file: file,
line: node.start.line,
col: node.start.col,
code: make_code(node, { beautify: false })
}));
}
function read_string(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
switch(body.TYPE) {
case "String":
return body.value;
case "Array":
return body.elements.map(function(element) {
if (element.TYPE !== "String")
throw new Error("Should be array of strings");
return element.value;
}).join("\n");
}
}
throw new Error("Should be string or array of strings");
}
function get_one_test(name, block) {
var test = { name: name, options: {} };
var tw = new U.TreeWalker(function(node, descend) {
if (node instanceof U.AST_Assign) {
if (!(node.left instanceof U.AST_SymbolRef)) {
croak(node);
}
var name = node.left.name;
test[name] = evaluate(node.right);
return true;
}
if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
assert.ok([
"input",
"expect",
"expect_exact",
"expect_warnings",
"expect_stdout",
"node_version",
].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name,
line: label.start.line,
col: label.start.col
}));
var stat = node.body;
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
var body = stat.body;
if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
} else {
test[label.name] = read_string(stat) + "\n";
}
} else {
test[label.name] = stat;
}
return true;
}
});
block.walk(tw);
return test;
}
}
// 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---OPTIONS---\n{options}\n--ERROR---\n{error}\n\n", {
input: input_formatted,
options: options_formatted,
error: result.error,
});
return false;
} else {
var stdout = 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;
}
function run_code(code) {
var result = sandbox.run_code(code, true);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}
function run_compress_tests() {
var dir = path.resolve(tests_dir, "compress");
fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
}).forEach(function(file) {
log("--- {file}", { file: file });
function test_case(test) {
log(" Running test [{name}]", { name: test.name });
var output_options = test.beautify || {};
var expect;
if (test.expect) {
expect = make_code(to_toplevel(test.expect, test.mangle), output_options);
} else {
expect = test.expect_exact;
}
var input = to_toplevel(test.input, test.mangle);
var input_code = make_code(input);
var input_formatted = make_code(test.input, {
beautify: true,
comments: "all",
keep_quoted_props: true,
quote_style: 3,
});
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 warnings_emitted = [];
if (test.expect_warnings) {
var expected_warnings = make_code(test.expect_warnings, {
beautify: false,
quote_style: 2, // force double quote to match JSON
});
U.AST_Node.log_function(function(text) {
warnings_emitted.push(text);
}, /"INFO: /.test(expected_warnings));
}
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props);
}
if (test.rename) {
input.figure_out_scope(test.mangle);
input.expand_names(test.mangle);
}
var cmp = new U.Compressor(test.options, true);
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle) {
output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle);
if (test.mangle.properties) {
output = U.mangle_properties(output, test.mangle.properties);
}
}
output = make_code(output, output_options);
if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
input: input_formatted,
output: output,
expected: expect
});
return false;
}
// expect == output
try {
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,
});
return false;
}
if (test.expect_warnings) {
warnings_emitted = warnings_emitted.map(function(input) {
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
});
var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
input: input_formatted,
expected_warnings: expected_warnings,
actual_warnings: actual_warnings,
});
return false;
}
}
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = run_code(input_code);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
}
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
return false;
}
stdout = 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", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
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)) {
if (!test_case(tests[i])) {
failures++;
failed_files[file] = 1;
}
}
});
}
function tmpl() {
return U.string_template.apply(null, arguments);
}
function to_toplevel(input, mangle_options) {
if (!(input instanceof U.AST_BlockStatement)) throw new Error("Unsupported input syntax");
var directive = true;
var offset = input.start.line;
var tokens = [];
var toplevel = new U.AST_Toplevel(input.transform(new U.TreeTransformer(function(node) {
if (U.push_uniq(tokens, node.start)) node.start.line -= offset;
if (!directive || node === input) return;
if (node instanceof U.AST_SimpleStatement && node.body instanceof U.AST_String) {
return new U.AST_Directive(node.body);
} else {
directive = false;
}
})));
toplevel.figure_out_scope(mangle_options);
return toplevel;
}

View File

@@ -1,29 +1,7 @@
var semver = require("semver"); var semver = require("semver");
var vm = require("vm"); var vm = require("vm");
function safe_log(arg, level) { var setupContext = new vm.Script([
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
}
}
return arg;
}
function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
var func_toString = new vm.Script([
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {", "[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
" f.toString = Function.prototype.toString;", " f.toString = Function.prototype.toString;",
"});", "});",
@@ -44,40 +22,51 @@ var func_toString = new vm.Script([
' return "function(){}";', ' return "function(){}";',
" };", " };",
"}();", "}();",
"this;",
]).join("\n")); ]).join("\n"));
function createContext() { function createContext() {
var ctx = vm.createContext(Object.defineProperty({}, "console", { value: { log: log } })); var ctx = vm.createContext(Object.defineProperty({}, "console", { value: { log: log } }));
func_toString.runInContext(ctx); var global = setupContext.runInContext(ctx);
return ctx; return ctx;
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
}
}
return arg;
}
function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
} }
var context; exports.run_code = function(code, toplevel) {
exports.run_code = function(code, reuse) {
var stdout = ""; var stdout = "";
var original_write = process.stdout.write; var original_write = process.stdout.write;
process.stdout.write = function(chunk) { process.stdout.write = function(chunk) {
stdout += chunk; stdout += chunk;
}; };
try { try {
if (!reuse || !context) context = createContext(); vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: 5000 });
vm.runInContext([
"!function() {",
code,
"}();",
].join("\n"), context, { timeout: 5000 });
return stdout; return stdout;
} catch (ex) { } catch (ex) {
return ex; return ex;
} finally { } finally {
process.stdout.write = original_write; process.stdout.write = original_write;
if (!reuse || code.indexOf(".prototype") >= 0) {
context = null;
} else {
vm.runInContext(Object.keys(context).map(function(name) {
return "delete " + name;
}).join("\n"), context);
}
} }
}; };

View File

@@ -1,81 +0,0 @@
"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;
var platform = process.argv[6] || "node/latest";
(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",
branch: branch,
config: {
cache: false,
env: "NODEJS_VER=" + platform,
script: "node test/travis-ufuzz run"
}
}
}));
})();
} else {
console.log("Usage: test/travis-ufuzz.js <token> [branch] [repository] [concurrency] [platform]");
}
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

@@ -1,4 +1,3 @@
// ufuzz.js
// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee // derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
"use strict"; "use strict";
@@ -6,11 +5,11 @@
// bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m // 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 // 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
require("../tools/exit"); require("../../tools/exit");
var UglifyJS = require(".."); var UglifyJS = require("../..");
var randomBytes = require("crypto").randomBytes; var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox"); var sandbox = require("../sandbox");
var MAX_GENERATED_TOPLEVELS_PER_RUN = 1; var MAX_GENERATED_TOPLEVELS_PER_RUN = 1;
var MAX_GENERATION_RECURSION_DEPTH = 12; var MAX_GENERATION_RECURSION_DEPTH = 12;
@@ -959,17 +958,22 @@ if (require.main !== module) {
return; return;
} }
function writeln(stream, msg) {
if (typeof msg != "undefined") {
stream.write(typeof msg == "string" ? msg : msg.stack || "" + msg);
}
stream.write("\n");
}
function println(msg) { function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg); writeln(process.stdout, msg);
process.stdout.write("\n");
} }
function errorln(msg) { function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg); writeln(process.stderr, msg);
process.stderr.write("\n");
} }
function try_beautify(code, result, printfn) { function try_beautify(code, toplevel, result, printfn) {
var beautified = UglifyJS.minify(code, { var beautified = UglifyJS.minify(code, {
compress: false, compress: false,
mangle: false, mangle: false,
@@ -980,8 +984,8 @@ function try_beautify(code, result, printfn) {
}); });
if (beautified.error) { if (beautified.error) {
printfn("// !!! beautify failed !!!"); printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack); printfn(beautified.error);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) { } else if (sandbox.same_stdout(sandbox.run_code(beautified.code, toplevel), result)) {
printfn("// (beautified)"); printfn("// (beautified)");
printfn(beautified.code); printfn(beautified.code);
return; return;
@@ -1007,9 +1011,9 @@ function log_suspects(minify_options, component) {
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (result.error) {
errorln("Error testing options." + component + "." + name); errorln("Error testing options." + component + "." + name);
errorln(result.error.stack); errorln(result.error);
} else { } else {
var r = sandbox.run_code(result.code); var r = sandbox.run_code(result.code, m.toplevel);
return sandbox.same_stdout(original_result, r); return sandbox.same_stdout(original_result, r);
} }
} }
@@ -1029,9 +1033,9 @@ function log_rename(options) {
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (result.error) {
errorln("Error testing options.rename"); errorln("Error testing options.rename");
errorln(result.error.stack); errorln(result.error);
} else { } else {
var r = sandbox.run_code(result.code); var r = sandbox.run_code(result.code, m.toplevel);
if (sandbox.same_stdout(original_result, r)) { if (sandbox.same_stdout(original_result, r)) {
errorln("Suspicious options:"); errorln("Suspicious options:");
errorln(" rename"); errorln(" rename");
@@ -1045,31 +1049,31 @@ function log(options) {
errorln("//============================================================="); errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round); if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code"); errorln("// original code");
try_beautify(original_code, original_result, errorln); try_beautify(original_code, false, original_result, errorln);
errorln(); errorln();
errorln(); errorln();
errorln("//-------------------------------------------------------------"); errorln("//-------------------------------------------------------------");
options = JSON.parse(options);
if (typeof uglify_code == "string") { if (typeof uglify_code == "string") {
errorln("// uglified code"); errorln("// uglified code");
try_beautify(uglify_code, uglify_result, errorln); try_beautify(uglify_code, options.toplevel, uglify_result, errorln);
errorln(); errorln();
errorln(); errorln();
errorln("original result:"); errorln("original result:");
errorln(typeof original_result == "string" ? original_result : original_result.stack); errorln(original_result);
errorln("uglified result:"); errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack); errorln(uglify_result);
} else { } else {
errorln("// !!! uglify failed !!!"); errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack); errorln(uglify_code);
if (typeof original_result != "string") { if (errored) {
errorln(); errorln();
errorln(); errorln();
errorln("original stacktrace:"); errorln("original stacktrace:");
errorln(original_result.stack); errorln(original_result);
} }
} }
errorln("minify(options):"); errorln("minify(options):");
options = JSON.parse(options);
errorln(JSON.stringify(options, null, 2)); errorln(JSON.stringify(options, null, 2));
errorln(); errorln();
if (!ok && typeof uglify_code == "string") { if (!ok && typeof uglify_code == "string") {
@@ -1083,35 +1087,39 @@ var fallback_options = [ JSON.stringify({
compress: false, compress: false,
mangle: false mangle: false
}) ]; }) ];
var minify_options = require("./ufuzz.json").map(JSON.stringify); var minify_options = require("./options.json").map(JSON.stringify);
var original_code, original_result; var original_code, original_result, errored;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r"); process.stdout.write(round + " of " + num_iterations + "\r");
original_code = createTopLevelCode(); original_code = createTopLevelCode();
original_result = sandbox.run_code(original_code); var orig_result = [ sandbox.run_code(original_code) ];
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) { errored = typeof orig_result[0] != "string";
uglify_code = UglifyJS.minify(original_code, JSON.parse(options)); if (!errored) orig_result.push(sandbox.run_code(original_code, true));
(errored ? fallback_options : minify_options).forEach(function(options) {
var o = JSON.parse(options);
uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[o.toplevel ? 1 : 0];
if (!uglify_code.error) { if (!uglify_code.error) {
uglify_code = uglify_code.code; uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code); uglify_result = sandbox.run_code(uglify_code, o.toplevel);
ok = sandbox.same_stdout(original_result, uglify_result); ok = sandbox.same_stdout(original_result, uglify_result);
} else { } else {
uglify_code = uglify_code.error; uglify_code = uglify_code.error;
if (typeof original_result != "string") { if (errored) {
ok = uglify_code.name == original_result.name; ok = uglify_code.name == original_result.name;
} }
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") { else if (errored) {
println("//============================================================="); println("//=============================================================");
println("// original code"); println("// original code");
try_beautify(original_code, original_result, println); try_beautify(original_code, o.toplevel, original_result, println);
println(); println();
println(); println();
println("original result:"); println("original result:");
println(original_result.stack); println(original_result);
println(); println();
} }
if (!ok && isFinite(num_iterations)) { if (!ok && isFinite(num_iterations)) {

39
test/ufuzz/job.js Normal file
View File

@@ -0,0 +1,39 @@
var child_process = require("child_process");
var ping = 5 * 60 * 1000;
var period = +process.argv[2];
var endTime = Date.now() + period;
for (var i = 0; i < 2; i++) spawn(endTime);
function spawn(endTime) {
var child = child_process.spawn("node", [
"--max-old-space-size=2048",
"test/ufuzz"
], {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
var line = "";
child.stdout.on("data", function(data) {
line += data;
});
child.stderr.once("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);
}
}

44
test/ufuzz/travis.js Normal file
View File

@@ -0,0 +1,44 @@
"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;
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;
var platform = process.argv[6] || "latest";
(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",
branch: branch,
config: {
cache: false,
env: "NODE=" + platform,
script: "node test/ufuzz/job " + period
}
}
}));
})();
} else {
console.log("Usage: test/ufuzz/travis.js <token> [branch] [repository] [concurrency] [platform]");
}

View File

@@ -2702,8 +2702,10 @@
"attrChange", "attrChange",
"attrName", "attrName",
"attributeChangedCallback", "attributeChangedCallback",
"attributeFilter",
"attributeName", "attributeName",
"attributeNamespace", "attributeNamespace",
"attributeOldValue",
"attributeStyleMap", "attributeStyleMap",
"attributes", "attributes",
"audioTracks", "audioTracks",
@@ -2993,6 +2995,7 @@
"caption", "caption",
"caption-side", "caption-side",
"captionSide", "captionSide",
"capture",
"captureEvents", "captureEvents",
"captureStackTrace", "captureStackTrace",
"captureStream", "captureStream",
@@ -3024,6 +3027,8 @@
"charCode", "charCode",
"charCodeAt", "charCodeAt",
"charIndex", "charIndex",
"characterData",
"characterDataOldValue",
"characterSet", "characterSet",
"charging", "charging",
"chargingTime", "chargingTime",
@@ -3035,6 +3040,7 @@
"checkValidity", "checkValidity",
"checked", "checked",
"childElementCount", "childElementCount",
"childList",
"childNodes", "childNodes",
"children", "children",
"chrome", "chrome",
@@ -4957,6 +4963,7 @@
"oncandidatewindowupdate", "oncandidatewindowupdate",
"oncanplay", "oncanplay",
"oncanplaythrough", "oncanplaythrough",
"once",
"oncellchange", "oncellchange",
"onchange", "onchange",
"onchargingchange", "onchargingchange",
@@ -5343,6 +5350,7 @@
"parseInt", "parseInt",
"part", "part",
"participants", "participants",
"passive",
"password", "password",
"pasteHTML", "pasteHTML",
"path", "path",
@@ -6147,6 +6155,7 @@
"substring", "substring",
"substringData", "substringData",
"subtle", "subtle",
"subtree",
"suffix", "suffix",
"suffixes", "suffixes",
"summary", "summary",

View File

@@ -1,21 +1,19 @@
var fs = require("fs"); var fs = require("fs");
exports.FILES = [ exports.FILES = [
"../lib/utils.js", require.resolve("../lib/utils.js"),
"../lib/ast.js", require.resolve("../lib/ast.js"),
"../lib/parse.js", require.resolve("../lib/parse.js"),
"../lib/transform.js", require.resolve("../lib/transform.js"),
"../lib/scope.js", require.resolve("../lib/scope.js"),
"../lib/output.js", require.resolve("../lib/output.js"),
"../lib/compress.js", require.resolve("../lib/compress.js"),
"../lib/sourcemap.js", require.resolve("../lib/sourcemap.js"),
"../lib/mozilla-ast.js", require.resolve("../lib/mozilla-ast.js"),
"../lib/propmangle.js", require.resolve("../lib/propmangle.js"),
"../lib/minify.js", require.resolve("../lib/minify.js"),
"./exports.js", require.resolve("./exports.js"),
].map(function(file) { ];
return require.resolve(file);
});
new Function("MOZ_SourceMap", "exports", function() { new Function("MOZ_SourceMap", "exports", function() {
var code = exports.FILES.map(function(file) { var code = exports.FILES.map(function(file) {
@@ -48,7 +46,9 @@ function describe_ast() {
if (ctor.SUBCLASSES.length > 0) { if (ctor.SUBCLASSES.length > 0) {
out.space(); out.space();
out.with_block(function() { out.with_block(function() {
ctor.SUBCLASSES.forEach(function(ctor, i) { ctor.SUBCLASSES.sort(function(a, b) {
return a.TYPE < b.TYPE ? -1 : 1;
}).forEach(function(ctor, i) {
out.indent(); out.indent();
doitem(ctor); doitem(ctor);
out.newline(); out.newline();