Compare commits

..

66 Commits

Author SHA1 Message Date
Alex Lam S.L
1786c69070 v3.11.1 2020-10-04 22:12:07 +08:00
Alex Lam S.L
95ef4d5377 fix corner case in mangle (#4174) 2020-10-04 08:24:41 +08:00
Alex Lam S.L
04017215cc support JSON dump beyond AST_Toplevel (#4173) 2020-10-03 22:53:06 +08:00
Alex Lam S.L
142bd1bd1a workaround quirks on latter specs (#4172)
closes #4171
2020-10-03 18:27:17 +08:00
Alex Lam S.L
8cb509d50e fix corner case in merge_vars (#4170)
fixes #4168
2020-10-03 07:03:39 +08:00
Alex Lam S.L
baf4903aa7 fix corner cases of catch variable inlining (#4169) 2020-10-03 07:02:28 +08:00
Alex Lam S.L
35465d590e report immediate ufuzz failure from Pull Request (#4166) 2020-10-02 23:43:38 +08:00
Alex Lam S.L
ccd91b9952 retrofit catch as block-scoped (#4165) 2020-10-02 23:29:58 +08:00
Alex Lam S.L
47a5e6e17a enhance if_return (#4164) 2020-10-02 16:10:25 +08:00
Alex Lam S.L
090ee895e1 enhance inline (#4163) 2020-09-30 21:03:28 +08:00
Alex Lam S.L
1cd1a1e5ee improve resilience against GitHub API (#4161) 2020-09-30 01:13:29 +08:00
Alex Lam S.L
1d835ac17d fix corner case in inline (#4160)
fixes #4159
2020-09-29 07:01:38 +08:00
Alex Lam S.L
9e07ac4102 fix corner case in merge_vars (#4158)
fixes #4157
2020-09-28 14:09:55 +08:00
Alex Lam S.L
92d1391e5e v3.11.0 2020-09-27 20:36:27 +08:00
Alex Lam S.L
b4ff6d0f2d fix corner cases in functions & merge_vars (#4156)
fixes #4155
2020-09-26 15:31:33 +08:00
Alex Lam S.L
9882a9f4af fix corner case in ufuzz scheduling (#4154) 2020-09-26 11:23:56 +08:00
Alex Lam S.L
40f36b9e01 improve ufuzz duty cycle heuristic (#4153) 2020-09-26 07:56:00 +08:00
Alex Lam S.L
6e105c5ca6 enhance merge_vars (#4152) 2020-09-25 22:00:20 +08:00
Alex Lam S.L
af35cd32f2 fix corner case in merge_vars (#4151) 2020-09-25 08:04:51 +08:00
Alex Lam S.L
7de8daa4b1 minor clean up (#4149) 2020-09-23 23:06:12 +08:00
Alex Lam S.L
305a4bdcee minor clean up (#4148) 2020-09-23 16:34:22 +08:00
Alex Lam S.L
3472cf1a90 fix corner case in unused (#4147)
fixes #4146
2020-09-22 20:08:45 +08:00
Alex Lam S.L
6d4c0fa6fa fix corner case in unused (#4145)
fixes #4144
2020-09-22 14:03:27 +08:00
Alex Lam S.L
3cca0d6249 fix corner case in evaluate (#4143)
fixes #4142
2020-09-22 12:11:25 +08:00
Alex Lam S.L
12ac49b970 Merge pull request #4141 from alexlamsl/unused
enhance `unused`
2020-09-22 02:21:43 +01:00
alexlamsl
8c670cae93 enhance unused 2020-09-22 07:48:55 +08:00
Alex Lam S.L
0e3da27727 fix corner case in merge_vars (#4140)
fixes #4139
2020-09-21 23:49:41 +01:00
Alex Lam S.L
13cdc167a2 fix corner case in evaluate (#4138)
fixes #4137
2020-09-22 06:49:32 +08:00
alexlamsl
51803cdcb2 fix corner case in merge_vars
fixes #4139
2020-09-22 05:03:06 +08:00
Alex Lam S.L
8fa470c17c fix corner case in merge_vars (#4136)
fixes #4135
2020-09-20 23:54:14 +08:00
Alex Lam S.L
90410f9fc3 fix corner case in unused (#4134)
fixes #4133
2020-09-20 23:21:59 +08:00
Alex Lam S.L
ef3831437d improve ufuzz duty cycle heuristic (#4132) 2020-09-20 08:29:35 +08:00
Alex Lam S.L
171c544705 fix corner case in merge_vars (#4131)
fixes #4130
2020-09-20 05:36:16 +08:00
Alex Lam S.L
3c609e2f4a enhance unused (#4129) 2020-09-20 01:45:52 +08:00
Alex Lam S.L
f0ae03ed39 report immediate ufuzz failure from Pull Request (#4128) 2020-09-19 20:31:37 +08:00
Alex Lam S.L
31c6b45036 fix corner case in merge_vars (#4127)
fixes #4126
2020-09-19 19:56:21 +08:00
Alex Lam S.L
3ac533e644 enhance merge_vars (#4125) 2020-09-19 11:16:23 +08:00
Alex Lam S.L
38a46c86d7 enhance side_effects (#4124)
- add documentation for `merge_vars`
2020-09-18 21:35:29 +08:00
Alex Lam S.L
0f0759ec15 remove redundant transform (#4123) 2020-09-18 07:04:46 +08:00
Alex Lam S.L
7f501f9fed add tests (#4122) 2020-09-18 00:26:31 +08:00
Alex Lam S.L
72844eb5a4 improve fix for #4119 (#4121) 2020-09-17 23:08:36 +08:00
Alex Lam S.L
09d93cc6c8 fix corner case in evaluate (#4120)
fixes #4119
2020-09-17 21:20:31 +08:00
Alex Lam S.L
dd1374aa8a minor clean up (#4118) 2020-09-17 07:10:45 +08:00
Alex Lam S.L
fdf2e8c5b0 enhance collapse_vars (#4117) 2020-09-17 06:35:22 +08:00
Alex Lam S.L
a9d934ab4e improve handling of switch statements (#4114) 2020-09-17 03:12:08 +08:00
Alex Lam S.L
2a053710bd fix corner case in merge_vars (#4116)
fixes #4115
2020-09-17 03:11:57 +08:00
Alex Lam S.L
219aac6a84 fix corner case in merge_vars (#4113)
fixes #4112
2020-09-16 22:18:28 +08:00
Alex Lam S.L
2039185051 enhance conditionals (#4106) 2020-09-16 05:51:42 +08:00
Alex Lam S.L
ad27c14202 fix corner cases in merge_vars (#4108)
fixes #4107
fixes #4109
fixes #4110
fixes #4111
2020-09-16 04:43:01 +08:00
Alex Lam S.L
a62b086184 enhance merge_vars (#4105) 2020-09-15 22:59:10 +08:00
Alex Lam S.L
335456cf77 fix corner case in merge_vars (#4104)
fixes #4103
2020-09-15 19:47:12 +08:00
Alex Lam S.L
d64d0b0bec fix corner case in merge_vars (#4102)
fixes #4101
2020-09-15 19:18:12 +08:00
Alex Lam S.L
3ac575f2e8 introduce merge_vars (#4100) 2020-09-15 10:01:48 +08:00
Alex Lam S.L
d33a3a3253 enhance unused (#4098) 2020-09-13 01:05:43 +08:00
Alex Lam S.L
d7456a2dc2 enhance if_return (#4097) 2020-09-10 22:31:34 +08:00
Alex Lam S.L
d97672613d fix corner case in reduce_vars (#4095) 2020-09-08 22:12:27 +08:00
Alex Lam S.L
30761eede5 v3.10.4 2020-09-07 00:25:54 +08:00
Alex Lam S.L
fb30aeccaf relax ufuzz job timing constraint (#4094) 2020-09-05 19:29:50 +08:00
Alex Lam S.L
226aa1f76b enhance unsafe_math (#4093) 2020-09-04 10:14:39 +08:00
Alex Lam S.L
6e235602fb fix corner case in loops & unused (#4092)
fixes #4091
2020-09-04 01:51:26 +08:00
Alex Lam S.L
980fcbb56b enhance unused (#4090) 2020-09-03 17:41:33 +08:00
Alex Lam S.L
375ebe316d enhance join_vars (#4089) 2020-09-03 01:41:10 +08:00
Alex Lam S.L
2500930234 enhance reduce_vars (#4088) 2020-09-02 11:30:46 +08:00
Alex Lam S.L
2f0da2ff05 reduce AST_ForIn gracefully (#4087) 2020-09-02 08:51:43 +08:00
Alex Lam S.L
83a3cbf151 fix test case runtime accounting (#4086) 2020-09-02 03:23:08 +08:00
Alex Lam S.L
da8d154571 fix corner case in loops & unused (#4085)
fixes #4084
2020-09-02 03:20:58 +08:00
31 changed files with 5209 additions and 644 deletions

View File

@@ -6,6 +6,7 @@ on:
env:
BASE_URL: https://api.github.com/repos/${{ github.repository }}
CAUSE: ${{ github.event_name }}
RUN_NUM: ${{ github.run_number }}
TOKEN: ${{ github.token }}
jobs:
ufuzz:
@@ -36,12 +37,8 @@ jobs:
npm config set update-notifier false
npm --version
while !(npm install); do echo "'npm install' failed - retrying..."; done
PERIOD=1800000
if [[ $CAUSE == "schedule" ]]; then
PERIOD=`node test/ufuzz/actions $BASE_URL $TOKEN`
fi
if (( $PERIOD == 0 )); then
echo "too many jobs in queue - skipping..."
node test/ufuzz/job $BASE_URL $TOKEN $RUN_NUM
else
node test/ufuzz/job $PERIOD
node test/ufuzz/job 5000
fi

View File

@@ -688,6 +688,8 @@ to be `false` and all symbol names will be omitted.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.
- `merge_vars` (default: `true`) -- combine and reuse variables.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.

View File

@@ -342,7 +342,18 @@ function run() {
}
fatal(ex);
} else if (output == "ast") {
if (!options.compress && !options.mangle) result.ast.figure_out_scope({});
if (!options.compress && !options.mangle) {
var toplevel = result.ast;
if (!(toplevel instanceof UglifyJS.AST_Toplevel)) {
if (!(toplevel instanceof UglifyJS.AST_Statement)) toplevel = new UglifyJS.AST_SimpleStatement({
body: toplevel,
});
toplevel = new UglifyJS.AST_Toplevel({
body: [ toplevel ],
});
}
toplevel.figure_out_scope({});
}
print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) {
case "thedef":

View File

@@ -412,33 +412,46 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "cname enclosed uses_eval uses_with parent_scope functions variables make_def", {
var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", {
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
parent_scope: "[AST_Scope?/S] link to the parent scope",
functions: "[Object/S] like `variables`, but only lists function declarations",
parent_scope: "[AST_Scope?/S] link to the parent scope",
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
},
clone: function(deep) {
var node = this._clone(deep);
if (this.variables) node.variables = this.variables.clone();
if (this.functions) node.functions = this.functions.clone();
if (this.enclosed) node.enclosed = this.enclosed.slice();
if (this.functions) node.functions = this.functions.clone();
if (this.variables) node.variables = this.variables.clone();
return node;
},
pinned: function() {
return this.resolve().pinned();
},
resolve: function() {
return this.parent_scope.resolve();
},
_validate: function() {
if (this.parent_scope == null) return;
if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
},
}, AST_Block);
var AST_Scope = DEFNODE("Scope", "cname uses_eval uses_with", {
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
},
pinned: function() {
return this.uses_eval || this.uses_with;
},
_validate: function() {
if (this.parent_scope != null) {
if (!(this.parent_scope instanceof AST_Scope)) throw new Error("parent_scope must be AST_Scope");
}
},
}, AST_Block);
resolve: return_this,
}, AST_BlockScope);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$documentation: "The toplevel scope",
@@ -631,6 +644,9 @@ var AST_Switch = DEFNODE("Switch", "expression", {
},
_validate: function() {
must_be_expression(this, "expression");
this.body.forEach(function(node) {
if (!(node instanceof AST_SwitchBranch)) throw new Error("body must be AST_SwitchBranch[]");
});
},
}, AST_Block);
@@ -700,7 +716,7 @@ var AST_Catch = DEFNODE("Catch", "argname", {
_validate: function() {
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
},
}, AST_Block);
}, AST_BlockScope);
var AST_Finally = DEFNODE("Finally", null, {
$documentation: "A `finally` node; only makes sense as part of a `try` statement"

File diff suppressed because it is too large Load Diff

View File

@@ -44,11 +44,14 @@
"use strict";
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield'
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with";
var KEYWORDS_ATOM = "false null true";
var RESERVED_WORDS = [
"abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield",
KEYWORDS_ATOM,
KEYWORDS,
].join(" ");
var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case";
KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -438,7 +441,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() {
var regex_allowed = S.regex_allowed;
var i = find("*/", true);
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, "\n");
// update stream position
forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
S.comments_before.push(token("comment2", text, true));

View File

@@ -59,13 +59,9 @@ function SymbolDef(id, scope, orig, init) {
}
SymbolDef.prototype = {
unmangleable: function(options) {
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
forEach: function(fn) {
this.orig.forEach(fn);
this.references.forEach(fn);
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
@@ -85,7 +81,15 @@ SymbolDef.prototype = {
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
},
unmangleable: function(options) {
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
},
};
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
@@ -100,22 +104,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
if (node instanceof AST_Defun) {
node.name.walk(tw);
walk_scope(function() {
node.argnames.forEach(function(argname) {
argname.walk(tw);
});
walk_body(node, tw);
});
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
defun = scope = node;
descend();
scope = save_scope;
defun = save_defun;
if (node instanceof AST_BlockScope) {
walk_scope(descend);
return true;
}
if (node instanceof AST_With) {
@@ -129,25 +129,41 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolDefun) {
// This should be defined in the parent scope, as we encounter the
// AST_Defun node before getting to its AST_Symbol.
(node.scope = defun.parent_scope.resolve()).def_function(node, defun);
if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
} else if (node instanceof AST_SymbolDefun) {
defun.def_function(node, tw.parent());
entangle(defun, scope);
} else if (node instanceof AST_SymbolFunarg) {
defun.def_variable(node);
entangle(defun, scope);
} else if (node instanceof AST_SymbolLambda) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie8) def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolVar) {
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
}
node.reference(options);
}
} else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
defun.def_variable(node, null);
entangle(defun, scope);
}
function walk_scope(descend) {
node.init_scope_vars(scope);
var save_defun = defun;
var save_scope = scope;
if (node instanceof AST_Scope) defun = node;
scope = node;
descend();
scope = save_scope;
defun = save_defun;
}
function entangle(defun, scope) {
if (defun === scope) return;
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef === def) return;
node.thedef = def;
def.orig.push(node);
node.mark_enclosed(options);
}
});
self.make_def = function(orig, init) {
@@ -234,7 +250,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
new_def = scope.def_variable(node);
}
old_def.defun = new_def.scope;
old_def.orig.concat(old_def.references).forEach(function(node) {
old_def.forEach(function(node) {
node.redef = true;
node.thedef = new_def;
node.reference(options);
});
@@ -267,7 +284,7 @@ function init_scope_vars(scope, parent) {
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
}
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
AST_BlockScope.DEFMETHOD("init_scope_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
});
@@ -279,6 +296,7 @@ AST_Lambda.DEFMETHOD("init_scope_vars", function(parent_scope) {
start: this.start,
end: this.end,
}));
return this;
});
AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
@@ -299,20 +317,20 @@ AST_Symbol.DEFMETHOD("reference", function(options) {
this.mark_enclosed(options);
});
AST_Scope.DEFMETHOD("find_variable", function(name) {
AST_BlockScope.DEFMETHOD("find_variable", function(name) {
if (name instanceof AST_Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("def_function", function(symbol, init) {
AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
var def = this.def_variable(symbol, init);
if (!def.init || def.init instanceof AST_Defun) def.init = init;
this.functions.set(symbol.name, def);
return def;
});
AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
@@ -325,16 +343,10 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
return symbol.thedef = def;
});
AST_Lambda.DEFMETHOD("resolve", return_this);
AST_Scope.DEFMETHOD("resolve", function() {
return this.parent_scope.resolve();
});
AST_Toplevel.DEFMETHOD("resolve", return_this);
function names_in_use(scope, options) {
var names = scope.names_in_use;
if (!names) {
scope.names_in_use = names = Object.create(scope.mangled_names || null);
scope.names_in_use = names = Object.create(null);
scope.cname_holes = [];
var cache = options.cache && options.cache.props;
scope.enclosed.forEach(function(def) {
@@ -352,7 +364,7 @@ function next_mangled_name(scope, options, def) {
var holes = scope.cname_holes;
var names = Object.create(null);
var scopes = [ scope ];
def.references.forEach(function(sym) {
def.forEach(function(sym) {
var scope = sym.scope;
do {
if (scopes.indexOf(scope) < 0) {
@@ -368,7 +380,7 @@ function next_mangled_name(scope, options, def) {
name = base54(holes[i]);
if (names[name]) continue;
holes.splice(i, 1);
scope.names_in_use[name] = true;
in_use[name] = true;
return name;
}
while (true) {
@@ -377,7 +389,7 @@ function next_mangled_name(scope, options, def) {
if (!names[name]) break;
holes.push(scope.cname);
}
scope.names_in_use[name] = true;
in_use[name] = true;
return name;
}
@@ -418,7 +430,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var lname = -1;
if (options.cache && options.cache.props) {
var mangled_names = this.mangled_names = Object.create(null);
var mangled_names = names_in_use(this, options);
options.cache.props.each(function(mangled_name) {
mangled_names[mangled_name] = true;
});
@@ -490,12 +502,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props;
var avoid = Object.create(null);
var avoid = Object.create(RESERVED_WORDS);
options.reserved.forEach(to_avoid);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
if (node instanceof AST_BlockScope) node.variables.each(add_def);
}));
return avoid;
@@ -519,15 +530,14 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var cname = 0;
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
if (node instanceof AST_BlockScope) node.variables.each(rename);
}));
function next_name() {
var name;
do {
name = base54(cname++);
} while (avoid[name] || RESERVED_WORDS[name]);
} while (avoid[name]);
return name;
}
@@ -538,7 +548,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var redef = def.redefined();
var name = redef ? redef.rename || redef.name : next_name();
def.rename = name;
def.orig.concat(def.references).forEach(function(sym) {
def.forEach(function(sym) {
if (sym.definition() === def) sym.name = name;
});
}

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.10.3",
"version": "3.11.1",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -95,7 +95,7 @@ asm_mixed: {
return +sum;
}
function geometricMean(start, end) {
return start |= 0, end |= 0, +exp(logSum(start, end) / (end - start | 0));
return start |= 0, end |= 0, +exp(+logSum(start, end) / (end - start | 0));
}
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
return { geometricMean: geometricMean };

View File

@@ -62,18 +62,18 @@ collapse_vars_side_effects_1: {
expect: {
function f1() {
var s = "abcdef", i = 2;
console.log.bind(console)(s.charAt(i++), s.charAt(i++), s.charAt(4), 7);
console.log.bind(console)(s.charAt(i++), s.charAt(+i), s.charAt(4), 7);
}
function f2() {
var s = "abcdef", i = 2;
console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(i++), 7);
console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(+i), 7);
}
function f3() {
var s = "abcdef",
i = 2,
log = console.log.bind(console),
x = s.charAt(i++),
y = s.charAt(i++);
y = s.charAt(+i);
log(x, s.charAt(4), y, 7);
}
function f4() {
@@ -3073,7 +3073,6 @@ issue_2298: {
expect: {
!function() {
(function() {
0;
try {
!function(b) {
(void 0)[1] = "foo";

View File

@@ -238,6 +238,41 @@ concat_8: {
expect_stdout: true
}
concat_9: {
options = {
booleans: true,
evaluate: true,
reduce_vars: true,
strings: true,
toplevel: true,
}
input: {
var a = "foo";
console.log(
12 + (34 + a),
null + (34 + a),
12 + (null + a),
false + (34 + a),
12 + (false + a),
"bar" + (34 + a),
12 + ("bar" + a)
);
}
expect: {
var a = "foo";
console.log(
"1234" + a,
"null34" + a,
"12null" + a,
!1 + (34 + a),
12 + (!1 + a),
"bar34" + a,
"12bar" + a
);
}
expect_stdout: true
}
issue_3689: {
options = {
strings: true,

View File

@@ -783,6 +783,28 @@ cond_12: {
}
}
cond_13: {
options = {
conditionals: true,
}
input: {
x ? y(a) : z(a);
x ? y.f(a) : z.f(a);
x ? y.f(a) : z.g(a);
x ? y.f()(a) : z.g()(a);
x ? y.f.u(a) : z.g.u(a);
x ? y.f().u(a) : z.g().u(a);
}
expect: {
(x ? y : z)(a);
(x ? y : z).f(a);
x ? y.f(a) : z.g(a);
(x ? y.f() : z.g())(a);
(x ? y.f : z.g).u(a);
(x ? y.f() : z.g()).u(a);
}
}
ternary_boolean_consequent: {
options = {
booleans: true,

View File

@@ -1730,7 +1730,7 @@ chained_3: {
expect: {
console.log(function(a, b) {
var c = b;
b++;
+b;
return c;
}(0, 2));
}
@@ -1997,7 +1997,7 @@ issue_3146_4: {
expect_stdout: "PASS"
}
issue_3192: {
issue_3192_1: {
options = {
unused: true,
}
@@ -2025,6 +2025,26 @@ issue_3192: {
]
}
issue_3192_2: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
"use strict";
(function(a) {
console.log(a = "foo", arguments[0]);
})("bar");
}
expect: {
"use strict";
(function() {
console.log("foo", arguments[0]);
})("bar");
}
expect_stdout: "foo bar"
}
issue_3233: {
options = {
pure_getters: "strict",
@@ -2161,8 +2181,7 @@ issue_3515_1: {
expect: {
var c = 0;
(function() {
this[c++] = 0;
for (var key20 in !0);
for (var key20 in !(this[c++] = 0));
})();
console.log(c);
}
@@ -2718,7 +2737,7 @@ issue_3962_1: {
0..toString();
} while (0);
if (c) console.log("PASS");
}((a--, 1)), 0);
}(1), 0);
void 0;
}
expect_stdout: "PASS"
@@ -2751,7 +2770,7 @@ issue_3962_2: {
0..toString();
} while (0);
if (c) console.log("PASS");
}((a--, 1)), 0);
}(1), 0);
}
expect_stdout: "PASS"
}
@@ -2834,11 +2853,11 @@ issue_4025: {
console.log(a, b, d);
}
expect: {
var d, c = 0;
var c = 0;
try {
console.log(c);
} finally {
d = c + 1;
var d = c + 1;
c = 0;
}
console.log(1, 1, d);
@@ -2848,3 +2867,159 @@ issue_4025: {
"1 1 1",
]
}
forin_var_1: {
options = {
unused: true,
}
input: {
var k;
for (k in [ 1, 2 ])
console.log(k);
for (k in { PASS: 3 })
console.log(k);
console.log(k);
}
expect: {
for (var k in [ 1, 2 ])
console.log(k);
for (k in { PASS: 3 })
console.log(k);
console.log(k);
}
expect_stdout: [
"0",
"1",
"PASS",
"PASS",
]
}
forin_var_2: {
options = {
unused: true,
}
input: {
console.log(function() {
switch (0) {
case function() {
for (a in 0);
}:
var b = 0;
}
for (var c = 0; a;);
var a;
}());
}
expect: {
console.log(function() {
switch (0) {
case function() {
for (a in 0);
}:
}
for (; a;);
var a;
}());
}
expect_stdout: "undefined"
}
issue_4133: {
options = {
evaluate: true,
merge_vars: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
var b = [ a-- ], c = b && b[c];
console.log(a);
}
expect: {
var b = 1;
console.log(0);
}
expect_stdout: "0"
}
issue_4144: {
options = {
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
(function(a, b) {
var b = console, c = ++b;
})(console.log("PASS"), 0);
}
expect: {
(function(b) {
b = console,
++b;
})(console.log("PASS"));
}
expect_stdout: "PASS"
}
issue_4146: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
function g() {}
var a = g;
var c = b;
c.p;
console.log(typeof a);
}
f("FAIL", 42);
}
expect: {
(function(a, b) {
a = function () {};
var c = b;
c.p;
console.log(typeof a);
})(0, 42);
}
expect_stdout: "function"
}
single_use_catch_redefined: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}

View File

@@ -2908,3 +2908,109 @@ issue_4077: {
}
expect_stdout: "PASS"
}
issue_4119_1: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a, b;
b = a = [];
a[0] += 0;
if (+b + 1) {
console.log("FAIL");
} else {
console.log("PASS");
}
}
expect: {
var a, b;
b = a = [];
a[0] += 0;
+b + 1 ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4119_2: {
options = {
conditionals: true,
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
var a;
(function(b) {
a[0] += 0;
console.log(+b + 1 ? "FAIL" : "PASS");
})(a = []);
}
expect: {
var a;
(function(b) {
a[0] += 0;
console.log(+b + 1 ? "FAIL" : "PASS");
})(a = []);
}
expect_stdout: "PASS"
}
issue_4119_3: {
options = {
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log(b.p ? "FAIL" : "PASS");
}
expect: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log(b.p ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
issue_4119_4: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log(!b ? "FAIL" : "PASS");
}
expect: {
var a, b;
b = a = {
p: 42,
};
delete a.p;
console.log((b, 0, "PASS"));
}
expect_stdout: "PASS"
}

View File

@@ -1483,8 +1483,7 @@ issue_2663_2: {
}
expect: {
(function() {
var i;
for (i in { a: 1, b: 2, c: 3 })
for (var i in { a: 1, b: 2, c: 3 })
j = i, console.log(j);
var j;
})();
@@ -2677,7 +2676,7 @@ cross_references_3: {
};
return Math.square(n) + Math.cube(n);
};
}(Math)(2));
}()(2));
console.log(Math.square(3), Math.cube(3));
}
expect_stdout: [
@@ -4778,3 +4777,213 @@ issue_4006: {
}
expect_stdout: "-1"
}
issue_4155: {
options = {
functions: true,
inline: true,
merge_vars: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a;
(function() {
console.log(a);
})(a);
var b = function() {};
b && console.log(typeof b);
})();
}
expect: {
(function() {
void console.log(b);
var b = function() {};
b && console.log(typeof b);
})();
}
expect_stdout: [
"undefined",
"function",
]
}
issue_4159: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 42, c = function(b) {
(b = a) && console.log(a++, b);
}(c = a);
}
expect: {
var a = 42;
(b = a) && console.log(a++, b);
var b;
}
expect_stdout: "42 42"
}
direct_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f(a, b) {
function g(c) {
return c >> 1;
}
return g(a) + g(b);
}
console.log(f(13, 31));
}
expect: {
function f(a, b) {
return (a >> 1) + (b >> 1);
}
console.log(f(13, 31));
}
expect_stdout: "21"
}
direct_inline_catch_redefined: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, f(), g());
}
expect: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, a, g());
}
expect_stdout: true
}
issue_4171_1: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
try {
while (a)
var e = function() {};
} catch (e) {
return function() {
return e;
};
}
}(!console));
}
expect: {
console.log(function(a) {
try {
while (a)
var e = function() {};
} catch (e) {
return function() {
return e;
};
}
}(!console));
}
expect_stdout: "undefined"
}
issue_4171_2: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
try {
while (a);
} catch (e) {
return function() {
return e;
};
} finally {
var e = function() {};
}
}(!console));
}
expect: {
console.log(function(a) {
try {
while (a);
} catch (e) {
return function() {
return e;
};
} finally {
function e() {}
}
}(!console));
}
expect_stdout: "undefined"
}
catch_defun: {
mangle = {
toplevel: true,
}
input: {
try {
throw 42;
} catch (a) {
function f() {
return typeof a;
}
}
console.log(f());
}
expect: {
try {
throw 42;
} catch (o) {
function t() {
return typeof o;
}
}
console.log(t());
}
expect_stdout: true
}

View File

@@ -2714,3 +2714,108 @@ issue_2737: {
}
expect_stdout: "function"
}
single_use_catch_redefined: {
options = {
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
single_use_inline_catch_redefined: {
options = {
ie8: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
direct_inline_catch_redefined: {
options = {
ie8: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, f(), g());
}
expect: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, a, g());
}
expect_stdout: true
}

View File

@@ -594,3 +594,157 @@ iife_if_return_simple: {
}
expect_stdout: "PASS"
}
nested_if_break: {
options = {
if_return: true,
}
input: {
for (var i = 0; i < 3; i++)
L1: if ("number" == typeof i) {
if (0 === i) break L1;
console.log(i);
}
}
expect: {
for (var i = 0; i < 3; i++)
L1: if ("number" == typeof i)
if (0 !== i) console.log(i);
}
expect_stdout: [
"1",
"2",
]
}
nested_if_continue: {
options = {
conditionals: true,
if_return: true,
join_vars: true,
loops: true,
}
input: {
function f(n) {
var i = 0;
do {
if ("number" == typeof n) {
if (0 === n) {
console.log("even", i);
continue;
}
if (1 === n) {
console.log("odd", i);
continue;
}
i++;
}
} while (0 <= (n -= 2));
}
f(37);
f(42);
}
expect: {
function f(n) {
for (var i = 0;
"number" == typeof n
&& (0 !== n
? 1 !== n
? i++
: console.log("odd", i)
: console.log("even", i)),
0 <= (n -= 2););
}
f(37);
f(42);
}
expect_stdout: [
"odd 18",
"even 21",
]
}
nested_if_return: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f() {
if (A) {
if (B)
return B;
if (C)
return D;
if (E)
return F;
if (G)
return H;
if (I) {
if (J)
return K;
return;
}
if (L) {
if (M)
return;
return N;
}
}
}
}
expect: {
function f() {
if (A)
return B || (C ? D : E ? F : G ? H : I ? J ? K : void 0 : L && !M ? N : void 0);
}
}
}
issue_866_1: {
options = {
conditionals: true,
if_return: true,
sequences: false,
};
input: {
function f(a) {
if (a)
return "";
console.log(a);
}
}
expect: {
function f(a) {
if (a)
return "";
console.log(a);
}
}
}
issue_866_2: {
options = {
conditionals: true,
if_return: true,
sequences: true,
}
input: {
(function() {
if (a)
if (b)
c;
else
return d;
})();
}
expect: {
(function() {
if (a) {
if (!b)
return d;
c;
}
})();
}
}

View File

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

View File

@@ -277,8 +277,8 @@ join_object_assignments_forin: {
}
expect: {
console.log(function() {
var o = { a: "PASS" };
for (var a in o)
var o = { a: "PASS" }, a;
for (a in o)
return o[a];
}());
}

View File

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

View File

@@ -1009,3 +1009,82 @@ issue_4082: {
}
expect_stdout: "PASS"
}
issue_4084: {
options = {
keep_fargs: "strict",
loops: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
function f(a) {
var b = a++;
for (a in "foo");
}
f();
return typeof a;
}());
}
expect: {
console.log(function() {
(function() {
0;
})();
return typeof a;
}());
}
expect_stdout: "undefined"
}
issue_4091_1: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
try {
throw "FAIL";
} catch (e) {
for (var e in 42);
}
console.log(e && e);
}
expect: {
try {
throw "FAIL";
} catch (e) {
var e;
}
console.log(e && e);
}
expect_stdout: "undefined"
}
issue_4091_2: {
options = {
loops: true,
toplevel: true,
unused: true,
}
input: {
try {
throw "FAIL";
} catch (e) {
for (e in 42);
var e;
}
console.log(e && e);
}
expect: {
try {
throw "FAIL";
} catch (e) {
var e;
}
console.log(e && e);
}
expect_stdout: "undefined"
}

3010
test/compress/merge_vars.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -91,7 +91,7 @@ evaluate_1: {
expect: {
console.log(
x + 1 + 2,
2 * x,
2 * +x,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
@@ -130,7 +130,7 @@ evaluate_1_unsafe_math: {
expect: {
console.log(
x + 1 + 2,
2 * x,
2 * +x,
+x + 3,
1 + x + 2 + 3,
3 | x,
@@ -148,45 +148,52 @@ evaluate_1_unsafe_math: {
evaluate_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: false,
}
input: {
var x = "42", y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y = null;
[
x + 1 + 2,
2 * x,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
1 + (2 + x + 3),
2 + ~x + 3 + 1,
2 + ~x + 3 - y,
0 & x,
2 + (x |= 0) + 3 + 1,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + "12",
2 * x,
+x + 1 + 2,
1 + x + "23",
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
2 + x + 3 + 1,
2 + ~x + 3 + 1,
2 + ~x + 3,
0 & x,
2 + (x |= 0) + 3 + 1,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"string 4212",
@@ -207,45 +214,52 @@ evaluate_2: {
evaluate_2_unsafe_math: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: true,
}
input: {
var x = "42", y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y = null;
[
x + 1 + 2,
2 * x,
+x + 3,
1 + x + 2 + 3,
3 | x,
6 + x--,
x*y + 6,
1 + (2 + x + 3),
6 + ~x,
5 + ~x - y,
0 & x,
6 + (x |= 0),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var x = "" + num, y = null;
[
x + "12",
2 * x,
+x + 3,
1 + x + "23",
3 | x,
6 + x--,
x*y + 6,
6 + x,
6 + ~x,
5 + ~x,
0 & x,
6 + (x |= 0),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"string 4212",
@@ -310,45 +324,52 @@ evaluate_4: {
evaluate_5: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: false,
}
input: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
a - 2 + 3,
a - 2 - 3,
+a + 2 + 3,
+a + 2 - 3,
2 - a + 3,
2 - a - 3,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 2 + 3,
+a + 2 - 3,
a - 2 + 3,
a - 2 - 3,
+a + 2 + 3,
+a + 2 - 3,
2 - a + 3,
2 - a - 3,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect_stdout: [
"number 6",
@@ -369,45 +390,52 @@ evaluate_5: {
evaluate_5_unsafe_math: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: true,
}
input: {
var a = "1";
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 2 + 3,
+a + 2 - 3,
+a - 2 + 3,
+a - 2 - 3,
2 + +a + 3,
2 + +a - 3,
2 - +a + 3,
2 - +a - 3,
2 + 3 + +a,
2 + 3 - +a,
2 - 3 + +a,
2 - 3 - +a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect: {
var a = "1";
[
+a + 5,
+a + -1,
a - -1,
a - 5,
+a + 5,
+a + -1,
5 - a,
-1 - a,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num) {
var a = "" + num;
[
+a + 5,
+a + -1,
a - -1,
a - 5,
+a + 5,
+a + -1,
5 - a,
-1 - a,
+a + 5,
5 - a,
+a - 1,
-1 - a,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(1);
}
expect_stdout: [
"number 6",
@@ -546,37 +574,44 @@ evaluate_6_unsafe_math: {
evaluate_7: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: false,
}
input: {
var x = "42", y;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
+x - 2 + (3 + !y),
+x - 2 + (3 - !y),
+x - 2 - (3 + !y),
+x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num, y) {
var x = "" + num;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
+x - 2 + (3 + !y),
+x - 2 + (3 - !y),
+x - 2 - (3 + !y),
+x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
x - 2 + (3 + !y),
x - 2 + (3 - !y),
x - 2 - (3 + !y),
x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num, y) {
var x = "" + num;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
x - 2 + (3 + !y),
x - 2 + (3 - !y),
x - 2 - (3 + !y),
x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"number 48",
@@ -593,37 +628,44 @@ evaluate_7: {
evaluate_7_unsafe_math: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_math: true,
}
input: {
var x = "42", y;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
+x - 2 + (3 + !y),
+x - 2 + (3 - !y),
+x - 2 - (3 + !y),
+x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num, y) {
var x = "" + num;
[
+x + 2 + (3 + !y),
+x + 2 + (3 - !y),
+x + 2 - (3 + !y),
+x + 2 - (3 - !y),
+x - 2 + (3 + !y),
+x - 2 + (3 - !y),
+x - 2 - (3 + !y),
+x - 2 - (3 - !y),
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect: {
var x = "42", y;
[
+x + 5 + !y,
+x + 5 - !y,
+x + -1 - !y,
+x + -1 + !y,
x - -1 + !y,
x - -1 - !y,
x - 5 - !y,
x - 5 + !y,
].forEach(function(n) {
console.log(typeof n, n);
});
function f(num, y) {
var x = "" + num;
[
+x + 5 + !y,
+x + 5 - !y,
+x + -1 - !y,
+x + -1 + !y,
x - -1 + !y,
x - -1 - !y,
x - 5 - !y,
x - 5 + !y,
].forEach(function(n) {
console.log(typeof n, n);
});
}
f(42);
}
expect_stdout: [
"number 48",
@@ -637,6 +679,22 @@ evaluate_7_unsafe_math: {
]
}
evaluate_8_unsafe_math: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = [ "42" ];
console.log(a * (1 / 7));
}
expect: {
var a = [ "42" ];
console.log(+a / 7);
}
expect_stdout: "6"
}
NaN_redefined: {
options = {
evaluate: true,
@@ -1251,3 +1309,29 @@ issue_3695: {
}
expect_stdout: "NaN"
}
issue_4137: {
options = {
evaluate: true,
}
input: {
console.log(+(A = []) * (A[0] = 1));
}
expect: {
console.log(+(A = []) * (A[0] = 1));
}
expect_stdout: "0"
}
issue_4142: {
options = {
evaluate: true,
}
input: {
console.log("" + +(0 === console));
}
expect: {
console.log("" + +(0 === console));
}
expect_stdout: "0"
}

View File

@@ -120,7 +120,7 @@ modified: {
expect: {
function f0() {
var b = 2;
b++;
+b;
console.log(2);
console.log(4);
}
@@ -1624,7 +1624,7 @@ defun_label: {
expect_stdout: true
}
double_reference: {
double_reference_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
@@ -1638,6 +1638,32 @@ double_reference: {
g();
}
}
expect: {
function f() {
var g = function g() {
g();
};
g();
}
}
}
double_reference_2: {
options = {
functions: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
var g = function g() {
g();
};
g();
}
}
expect: {
function f() {
(function g() {
@@ -1647,6 +1673,60 @@ double_reference: {
}
}
double_reference_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var x = function f() {
return f;
};
function g() {
return x();
}
console.log(g() === g());
}
expect: {
var x = function f() {
return f;
};
function g() {
return x();
}
console.log(g() === g());
}
expect_stdout: "true"
}
double_reference_4: {
options = {
comparisons: true,
functions: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var x = function f() {
return f;
};
function g() {
return x();
}
console.log(g() === g());
}
expect: {
console.log(true);
}
expect_stdout: "true"
}
iife_arguments_1: {
options = {
reduce_funcs: true,
@@ -1686,8 +1766,35 @@ iife_arguments_2: {
}
expect: {
(function() {
console.log(function f() {
var x = function f() {
return f;
};
console.log(x() === arguments[0]);
})();
}
expect_stdout: true
}
iife_arguments_3: {
options = {
functions: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var x = function f() {
return f;
};
console.log(x() === arguments[0]);
})();
}
expect: {
(function() {
console.log(function x() {
return x;
}() === arguments[0]);
})();
}
@@ -2069,6 +2176,7 @@ issue_1670_6: {
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
switches: true,
unused: true,
@@ -2086,10 +2194,9 @@ issue_1670_6: {
})(1);
}
expect: {
(function(a) {
a = 1;
console.log(a);
})(1);
(function() {
console.log(1);
})();
}
expect_stdout: "1"
}
@@ -2308,7 +2415,7 @@ redefine_farg_2: {
console.log(typeof [], "number",function(a, b) {
a = b;
return typeof a;
}([]));
}());
}
expect_stdout: "object number undefined"
}
@@ -5267,11 +5374,11 @@ defun_catch_4: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect_stdout: "42"
node_version: "<=4"
expect_stdout: true
}
defun_catch_5: {
@@ -5293,10 +5400,10 @@ defun_catch_5: {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect_stdout: "42"
node_version: "<=4"
expect_stdout: true
}
defun_catch_6: {
@@ -5483,7 +5590,7 @@ lvalues_def_1: {
}
expect: {
var b = 1;
var a = b++, b = NaN;
var a = +b, b = NaN;
console.log(a, b);
}
expect_stdout: "1 NaN"

View File

@@ -245,6 +245,31 @@ unsafe_builtin_2: {
expect_stdout: "object PASS PASS"
}
unsafe_builtin_3: {
options = {
conditionals: true,
side_effects: true,
toplevel: true,
unsafe: true,
}
input: {
var o = {};
if (42 < Math.random())
o.p = "FAIL";
else
o.p = "PASS";
for (var k in o)
console.log(k, o[k]);
}
expect: {
var o = {};
o.p = 42 < Math.random() ? "FAIL" : "PASS";
for (var k in o)
console.log(k, o[k]);
}
expect_stdout: "p PASS"
}
unsafe_string_replace: {
options = {
side_effects: true,

View File

@@ -1,9 +1,3 @@
var o = this;
for (var k in o) L17060: {
a++;
UNUSED: {
console.log(0 - .1 - .1 - .1);
}
var a;
console.log(k);

View File

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

View File

@@ -24,6 +24,9 @@ describe("test/reduce.js", function() {
});
it("Should eliminate unreferenced labels", function() {
var result = reduce_test(read("test/input/reduce/label.js"), {
compress: {
unsafe_math: true,
},
mangle: false,
}, {
verbose: false,

View File

@@ -112,19 +112,18 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// no structural AST changes before this point.
if (node.start._permute >= REPLACEMENTS.length) return;
if (parent instanceof U.AST_Assign
&& parent.left === node
|| parent instanceof U.AST_Unary
&& parent.expression === node
&& ["++", "--", "delete"].indexOf(parent.operator) >= 0) {
// ignore lvalues
// ignore lvalues
if (parent instanceof U.AST_Assign && parent.left === node) return;
if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
case "++":
case "--":
case "delete":
return;
}
if ((parent instanceof U.AST_For || parent instanceof U.AST_ForIn)
&& parent.init === node && node instanceof U.AST_Var) {
// preserve for (var ...)
return node;
}
// preserve for (var xxx; ...)
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Var) return node;
// preserve for (xxx in ...)
if (parent instanceof U.AST_ForIn && parent.init === node) return node;
// node specific permutations with no parent logic
@@ -452,6 +451,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
node.start = JSON.parse(JSON.stringify(node.start));
node.start._permute = 0;
}));
var before_iterations = testcase;
for (var c = 0; c < max_iterations; ++c) {
if (verbose && pass == 1 && c % 25 == 0) {
log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
@@ -494,7 +494,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
}
}
if (c == 0) break;
if (before_iterations === testcase) break;
if (verbose) {
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
}
@@ -557,7 +557,7 @@ function try_beautify(testcase, minify_options, expected, result_cache, timeout)
code: testcase,
};
} else {
var actual = run_code(result.code, toplevel, result_cache, timeout);
var actual = run_code(result.code, toplevel, result_cache, timeout).result;
if (!sandbox.same_stdout(expected, actual)) return {
code: testcase,
};
@@ -650,7 +650,15 @@ function wrap_with_console_log(node) {
function run_code(code, toplevel, result_cache, timeout) {
var key = crypto.createHash("sha1").update(code).digest("base64");
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout));
var value = result_cache[key];
if (!value) {
var start = Date.now();
result_cache[key] = value = {
result: sandbox.run_code(code, toplevel, timeout),
elapsed: Date.now() - start,
};
}
return value;
}
function compare_run_code(code, minify_options, result_cache, max_timeout) {
@@ -658,21 +666,19 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
if (minified.error) return minified;
var toplevel = sandbox.has_toplevel(minify_options);
var elapsed = Date.now();
var unminified_result = run_code(code, toplevel, result_cache, max_timeout);
elapsed = Date.now() - elapsed;
var timeout = Math.min(100 * elapsed, max_timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout);
var unminified = run_code(code, toplevel, result_cache, max_timeout);
var timeout = Math.min(100 * unminified.elapsed, max_timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
if (sandbox.same_stdout(unminified_result, minified_result)) {
return is_timed_out(unminified_result) && is_timed_out(minified_result) && {
if (sandbox.same_stdout(unminified.result, minified_result)) {
return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
timed_out: true,
};
}
return {
unminified_result: unminified_result,
unminified_result: unminified.result,
minified_result: minified_result,
elapsed: elapsed,
elapsed: unminified.elapsed,
};
}

View File

@@ -1,11 +1,47 @@
require("../../tools/exit");
var get = require("https").get;
var parse = require("url").parse;
var base = process.argv[2];
var token = process.argv[3];
var base, token, run_number, eldest = true;
exports.init = function(url, auth, num) {
base = url;
token = auth;
run_number = num;
};
exports.should_stop = function(callback) {
read(base + "/actions/runs?per_page=100", function(reply) {
if (!reply || !Array.isArray(reply.workflow_runs)) return;
var runs = reply.workflow_runs.filter(function(workflow) {
return workflow.status != "completed";
}).sort(function(a, b) {
return b.run_number - a.run_number;
});
var found = false, remaining = 20;
(function next() {
if (!runs.length) return;
var workflow = runs.pop();
if (workflow.event == "schedule" && workflow.run_number == run_number) found = true;
read(workflow.jobs_url, function(reply) {
if (!reply || !Array.isArray(reply.jobs)) return;
if (!reply.jobs.every(function(job) {
if (job.status == "completed") return true;
remaining--;
return found || workflow.event != "schedule";
})) return;
if (remaining >= 0) {
next();
} else {
callback();
}
});
})();
});
};
function read(url, callback) {
var done = function(reply) {
done = function() {};
callback(reply);
};
var options = parse(url);
options.headers = {
"Authorization": "Token " + token,
@@ -17,33 +53,15 @@ function read(url, callback) {
response.on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
callback(JSON.parse(chunks.join("")));
var reply;
try {
reply = JSON.parse(chunks.join(""))
} catch (e) {}
done(reply);
}).on("error", function() {
done();
});
}).on("error", function() {
done();
});
}
var queued = 0, total = 0, earliest, now = Date.now();
process.on("beforeExit", function() {
if (queued > 3) {
process.stdout.write("0");
} else if (now - earliest > 0 && total > 1) {
process.stdout.write(Math.min(20 * (now - earliest) / (total - 1), 6300000).toFixed(0));
} else {
process.stdout.write("3600000");
}
});
read(base + "/actions/workflows/ufuzz.yml/runs?event=schedule", function(reply) {
reply.workflow_runs.filter(function(workflow) {
return /^(in_progress|queued|)$/.test(workflow.status);
}).forEach(function(workflow) {
read(workflow.jobs_url, function(reply) {
reply.jobs.forEach(function(job) {
if (job.status == "queued") queued++;
total++;
if (!job.started_at) return;
var start = new Date(job.started_at);
if (!(earliest < start)) earliest = start;
});
});
});
});

View File

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