Compare commits

..

28 Commits

Author SHA1 Message Date
Alex Lam S.L
338dd144b8 v3.5.6 2019-04-21 07:19:29 +08:00
Alex Lam S.L
c719552317 fix corner cases in functions (#3372)
fixes #3371
2019-04-21 02:16:05 +08:00
Alex Lam S.L
855964a87a enhance unsafe evaluate (#3370) 2019-04-20 19:42:41 +08:00
Alex Lam S.L
a438e2fca9 update domprops (#3369)
fixes #2343
fixes #3037
2019-04-20 07:16:14 +08:00
Alex Lam S.L
00833e893a enhance functions (#3368) 2019-04-19 19:01:47 +08:00
Alex Lam S.L
f1a77e4fc0 v3.5.5 2019-04-19 15:22:46 +08:00
Alex Lam S.L
b55a2fd531 fix corner case in functions (#3367)
fixes #3366
2019-04-19 02:55:43 +08:00
Alex Lam S.L
e8a2c0b5bf fix corner case in functions (#3365)
fixes #3364
2019-04-18 17:03:52 +08:00
Alex Lam S.L
21cd7e3f57 reduce test exports (#3361) 2019-04-17 16:19:08 +08:00
Alex Lam S.L
5172ba5f2a introduce functions (#3360)
`var f = function() {};` => `function f() {}`
2019-04-15 22:23:11 +08:00
Alex Lam S.L
a57b069409 v3.5.4 2019-04-10 02:40:42 +08:00
Alex Lam S.L
4454656c3b update dependencies (#3358)
- commander@2.20.0
- semver@6.0.0
2019-04-10 02:39:56 +08:00
Alex Lam S.L
fa43768ce0 v3.5.3 2019-04-01 18:12:03 +08:00
Alex Lam S.L
a74e600fa0 mangle shadowed lambda under ie8 correctly (#3356)
fixes #3355
2019-04-01 15:22:00 +08:00
Ruben Bridgewater
4b21526310 Fix test expectation (#3357)
The test expects a specific precision value that is not met on all V8 versions anymore due to a recent consolidation of different algorithms across the V8 code base.

This makes sure the preceision is tested against one digit less to keep the test working on all V8 versions.

Refs: 98453126c1
Refs: https://github.com/nodejs/node/issues/25060#issuecomment-477953457
2019-03-30 02:08:27 +08:00
Alex Lam S.L
a7a7b1daed v3.5.2 2019-03-23 14:25:14 +08:00
Alex Lam S.L
7436977aa5 fix infinite loop triggered by #3347 (#3354)
fixes #3353
2019-03-23 14:21:54 +08:00
Alex Lam S.L
e3c565b46f v3.5.1 2019-03-21 13:54:14 +08:00
Alex Lam S.L
54b0b49b68 enhance inline (#3352) 2019-03-21 02:58:33 +08:00
Alex Lam S.L
65648d84a5 enhance collapse_vars (#3351) 2019-03-20 23:31:21 +08:00
Alex Lam S.L
fd788590f6 v3.5.0 2019-03-20 18:43:54 +08:00
Alex Lam S.L
143f9054da fix corner case in sequences (#3350) 2019-03-20 14:54:26 +08:00
Alex Lam S.L
f2286c33f1 enhance unsafe for Array (#3349) 2019-03-20 06:37:51 +08:00
Alex Lam S.L
b9615f7a62 improve compress performance (#3348)
fixes #3174
2019-03-20 02:53:04 +08:00
Alex Lam S.L
c520e99eda enhance comparisons (#3347) 2019-03-19 01:34:25 +08:00
Alex Lam S.L
615ae37ca3 introduce assignments (#3345) 2019-03-18 21:28:41 +08:00
Alex Lam S.L
7aa7f21872 fix corner case in evaluate (#3344) 2019-03-18 21:24:42 +08:00
Alex Lam S.L
4430a436eb fix corner case in inline (#3343) 2019-03-17 05:31:40 +08:00
24 changed files with 3142 additions and 252 deletions

View File

@@ -605,6 +605,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `arguments` (default: `true`) -- replace `arguments[index]` with function
parameter name whenever possible.
- `assignments` (default: `true`) -- apply optimizations to assignment expressions.
- `booleans` (default: `true`) -- various optimizations for boolean context,
for example `!!a ? b : c → a ? b : c`
@@ -634,6 +636,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `functions` (default: `true`) -- convert declarations from `var`to `function`
whenever possible.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_funs` (default: `false`) -- hoist function declarations

View File

@@ -49,6 +49,7 @@ function Compressor(options, false_by_default) {
TreeTransformer.call(this, this.before, this.after);
this.options = defaults(options, {
arguments : !false_by_default,
assignments : !false_by_default,
booleans : !false_by_default,
collapse_vars : !false_by_default,
comparisons : !false_by_default,
@@ -59,6 +60,7 @@ function Compressor(options, false_by_default) {
drop_debugger : !false_by_default,
evaluate : !false_by_default,
expression : false,
functions : !false_by_default,
global_defs : false,
hoist_funs : false,
hoist_props : !false_by_default,
@@ -112,7 +114,7 @@ function Compressor(options, false_by_default) {
};
} else if (Array.isArray(pure_funcs)) {
this.pure_funcs = function(node) {
return pure_funcs.indexOf(node.expression.print_to_string()) < 0;
return !member(node.expression.print_to_string(), pure_funcs);
};
} else {
this.pure_funcs = return_true;
@@ -129,7 +131,7 @@ function Compressor(options, false_by_default) {
top_retain = top_retain.split(/,/);
}
this.top_retain = function(def) {
return top_retain.indexOf(def.name) >= 0;
return member(def.name, top_retain);
};
}
var toplevel = this.options["toplevel"];
@@ -149,7 +151,7 @@ Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
exposed: function(def) {
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++)
if (def.global) for (var i = 0; i < def.orig.length; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return true;
return false;
@@ -310,8 +312,22 @@ merge(Compressor.prototype, {
return value instanceof AST_SymbolRef && value.fixed_value() || value;
}
function is_read_only_fn(value, name) {
if (value instanceof AST_Boolean) return native_fns.Boolean[name];
if (value instanceof AST_Number) return native_fns.Number[name];
if (value instanceof AST_String) return native_fns.String[name];
if (name == "valueOf") return false;
if (value instanceof AST_Array) return native_fns.Array[name];
if (value instanceof AST_Function) return native_fns.Function[name];
if (value instanceof AST_Object) return native_fns.Object[name];
if (value instanceof AST_RegExp) return native_fns.RegExp[name];
}
function is_modified(compressor, tw, node, value, level, immutable) {
var parent = tw.parent(level);
if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
return;
}
var lhs = is_lhs(node, parent);
if (lhs) return lhs;
if (!immutable
@@ -342,7 +358,7 @@ merge(Compressor.prototype, {
def.assignments = 0;
def.chained = false;
def.direct_access = false;
def.escaped = false;
def.escaped = [];
def.fixed = !def.scope.pinned()
&& !compressor.exposed(def)
&& !(def.init instanceof AST_Function && def.init !== def.scope)
@@ -481,8 +497,9 @@ merge(Compressor.prototype, {
|| parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
|| parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped.push(parent);
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
if (!d.escaped || d.escaped > depth) d.escaped = depth;
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
return;
} else if (parent instanceof AST_Array
|| parent instanceof AST_Binary && lazy_op[parent.operator]
@@ -537,13 +554,11 @@ merge(Compressor.prototype, {
var d = sym.definition();
var safe = safe_to_assign(tw, d, sym.scope, node.right);
d.assignments++;
if (!safe) return;
var fixed = d.fixed;
if (!fixed && node.operator != "=") return;
var eq = node.operator == "=";
var value = eq ? node.right : node;
if (is_modified(compressor, tw, node, value, 0)) return;
d.references.push(sym);
if (!eq) d.chained = true;
d.fixed = eq ? function() {
return node.right;
@@ -554,6 +569,8 @@ merge(Compressor.prototype, {
right: node.right
});
};
if (!safe) return;
d.references.push(sym);
mark(tw, d, false);
node.right.walk(tw);
mark(tw, d, true);
@@ -740,8 +757,8 @@ merge(Compressor.prototype, {
d.fixed = false;
}
}
mark_escaped(tw, d, this.scope, this, value, 0, 1);
}
mark_escaped(tw, d, this.scope, this, value, 0, 1);
var parent;
if (d.fixed instanceof AST_Defun
&& !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
@@ -783,10 +800,8 @@ merge(Compressor.prototype, {
var d = exp.definition();
var safe = safe_to_assign(tw, d, exp.scope, true);
d.assignments++;
if (!safe) return;
var fixed = d.fixed;
if (!fixed) return;
d.references.push(exp);
d.chained = true;
d.fixed = function() {
return make_node(AST_Binary, node, {
@@ -800,6 +815,8 @@ merge(Compressor.prototype, {
})
});
};
if (!safe) return;
d.references.push(exp);
mark(tw, d, true);
return true;
});
@@ -1100,10 +1117,10 @@ merge(Compressor.prototype, {
stop_if_hit = parent;
}
// Replace variable with assignment when found
var hit_lhs, hit_rhs;
var hit_rhs;
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
&& (scan_lhs && lhs.equivalent_to(node)
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true;
@@ -1193,10 +1210,9 @@ merge(Compressor.prototype, {
var stop_after = null;
var stop_if_hit = null;
var lhs = get_lhs(candidate);
var rhs = get_rhs(candidate);
var side_effects = lhs && lhs.has_side_effects(compressor);
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
var scan_rhs = rhs && foldable(rhs);
var scan_rhs = foldable(get_rhs(candidate));
if (!scan_lhs && !scan_rhs) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
@@ -1241,7 +1257,7 @@ merge(Compressor.prototype, {
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
for (var i = 0; !abort && i < node.body.length; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
@@ -1361,6 +1377,7 @@ merge(Compressor.prototype, {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
candidates.push(hit_stack.slice());
extract_candidates(expr.left);
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
@@ -1376,6 +1393,8 @@ merge(Compressor.prototype, {
extract_candidates(expr.alternative);
} else if (expr instanceof AST_Definitions) {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_Dot) {
extract_candidates(expr.expression);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
@@ -1407,6 +1426,9 @@ merge(Compressor.prototype, {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
extract_candidates(expr.body);
} else if (expr instanceof AST_Sub) {
extract_candidates(expr.expression);
extract_candidates(expr.property);
} else if (expr instanceof AST_Switch) {
extract_candidates(expr.expression);
expr.body.forEach(extract_candidates);
@@ -1465,6 +1487,7 @@ merge(Compressor.prototype, {
return node;
}
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_PropAccess) return node;
if (parent instanceof AST_Sequence) {
return find_stop(parent, level + 1, parent.tail_node() !== node);
}
@@ -1523,6 +1546,7 @@ merge(Compressor.prototype, {
}
function foldable(expr) {
if (!expr) return false;
if (expr instanceof AST_SymbolRef) {
var value = expr.evaluate(compressor);
if (value === expr) return rhs_exact_match;
@@ -1544,10 +1568,10 @@ merge(Compressor.prototype, {
}
}));
return !circular && rhs_exact_match;
}
function rhs_exact_match(node) {
return rhs.equivalent_to(node);
function rhs_exact_match(node) {
return expr.equivalent_to(node);
}
}
function rhs_fuzzy_match(value, fallback) {
@@ -1570,9 +1594,14 @@ merge(Compressor.prototype, {
lvalues[candidate.name.name] = lhs;
}
var tw = new TreeWalker(function(node) {
var sym = root_expr(node);
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_modified(compressor, tw, node, node, 0);
var value;
if (node instanceof AST_SymbolRef) {
value = node.fixed_value() || node;
} else if (node instanceof AST_This) {
value = node;
}
if (value && !lvalues[node.name]) {
lvalues[node.name] = is_modified(compressor, tw, node, value, 0);
}
});
expr.walk(tw);
@@ -1682,7 +1711,7 @@ merge(Compressor.prototype, {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
if (!member(stat.value, seen_dirs)) {
i++;
seen_dirs.push(stat.value);
} else {
@@ -1879,7 +1908,7 @@ merge(Compressor.prototype, {
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
for (var j = i + 1; j < statements.length; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
@@ -1970,7 +1999,7 @@ merge(Compressor.prototype, {
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
for (var i = 0; i < block.body.length; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
@@ -2110,7 +2139,7 @@ merge(Compressor.prototype, {
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
for (var i = 0, j = -1; i < statements.length; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
@@ -2238,7 +2267,11 @@ merge(Compressor.prototype, {
});
def(AST_SymbolRef, function() {
var fixed = this.fixed_value();
return fixed && fixed.is_truthy();
if (!fixed) return false;
this.is_truthy = return_false;
var result = fixed.is_truthy();
delete this.is_truthy;
return result;
});
})(function(node, func) {
node.DEFMETHOD("is_truthy", func);
@@ -2251,45 +2284,38 @@ merge(Compressor.prototype, {
return !compressor.option("pure_getters")
|| this._dot_throw(compressor);
});
function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters"));
}
def(AST_Node, is_strict);
def(AST_Null, return_true);
def(AST_Undefined, return_true);
def(AST_Constant, return_false);
def(AST_Array, return_false);
def(AST_Object, function(compressor) {
if (!is_strict(compressor)) return false;
for (var i = this.properties.length; --i >=0;)
if (this.properties[i].value instanceof AST_Accessor) return true;
return false;
});
def(AST_Lambda, return_false);
def(AST_UnaryPostfix, return_false);
def(AST_UnaryPrefix, function() {
return this.operator == "void";
});
def(AST_Binary, function(compressor) {
return (this.operator == "&&" || this.operator == "||")
&& (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
})
def(AST_Assign, function(compressor) {
return this.operator == "="
&& this.right._dot_throw(compressor);
})
def(AST_Binary, function(compressor) {
return (this.operator == "&&" || this.operator == "||")
&& (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
})
def(AST_Conditional, function(compressor) {
return this.consequent._dot_throw(compressor)
|| this.alternative._dot_throw(compressor);
})
def(AST_Constant, return_false);
def(AST_Dot, function(compressor) {
if (!is_strict(compressor)) return false;
var exp = this.expression;
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
return !(exp instanceof AST_Lambda && this.property == "prototype");
});
def(AST_Lambda, return_false);
def(AST_Null, return_true);
def(AST_Object, function(compressor) {
if (!is_strict(compressor)) return false;
for (var i = this.properties.length; --i >=0;)
if (this.properties[i].value instanceof AST_Accessor) return true;
return false;
});
def(AST_Sequence, function(compressor) {
return this.tail_node()._dot_throw(compressor);
});
@@ -2299,8 +2325,17 @@ merge(Compressor.prototype, {
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
if (this.is_immutable()) return false;
var fixed = this.fixed_value();
return !fixed || fixed._dot_throw(compressor);
if (!fixed) return true;
this._dot_throw = return_true;
var result = fixed._dot_throw(compressor);
delete this._dot_throw;
return result;
});
def(AST_UnaryPrefix, function() {
return this.operator == "void";
});
def(AST_UnaryPostfix, return_false);
def(AST_Undefined, return_true);
})(function(node, func) {
node.DEFMETHOD("_dot_throw", func);
});
@@ -2334,6 +2369,14 @@ merge(Compressor.prototype, {
def(AST_Sequence, function(compressor) {
return this.tail_node().is_boolean(compressor);
});
def(AST_SymbolRef, function(compressor) {
var fixed = this.fixed_value();
if (!fixed) return false;
this.is_boolean = return_false;
var result = fixed.is_boolean(compressor);
delete this.is_boolean;
return result;
});
var unary = makePredicate("! delete");
def(AST_UnaryPrefix, function() {
return unary[this.operator];
@@ -2415,6 +2458,14 @@ merge(Compressor.prototype, {
def(AST_Sequence, function(compressor) {
return this.tail_node().is_number(compressor);
});
def(AST_SymbolRef, function(compressor) {
var fixed = this.fixed_value();
if (!fixed) return false;
this.is_number = return_false;
var result = fixed.is_number(compressor);
delete this.is_number;
return result;
});
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function() {
return unary[this.operator];
@@ -2426,22 +2477,30 @@ merge(Compressor.prototype, {
// methods to determine if an expression has a string result type
(function(def) {
def(AST_Node, return_false);
def(AST_String, return_true);
def(AST_UnaryPrefix, function() {
return this.operator == "typeof";
def(AST_Assign, function(compressor) {
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Binary, function(compressor) {
return this.operator == "+" &&
(this.left.is_string(compressor) || this.right.is_string(compressor));
});
def(AST_Assign, function(compressor) {
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
def(AST_Conditional, function(compressor) {
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
});
def(AST_Sequence, function(compressor) {
return this.tail_node().is_string(compressor);
});
def(AST_Conditional, function(compressor) {
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
def(AST_String, return_true);
def(AST_SymbolRef, function(compressor) {
var fixed = this.fixed_value();
if (!fixed) return false;
this.is_string = return_false;
var result = fixed.is_string(compressor);
delete this.is_string;
return result;
});
def(AST_UnaryPrefix, function() {
return this.operator == "typeof";
});
})(function(node, func) {
node.DEFMETHOD("is_string", func);
@@ -2681,7 +2740,7 @@ merge(Compressor.prototype, {
def(AST_Array, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) {
for (var i = 0; i < this.elements.length; i++) {
var element = this.elements[i];
var value = element._eval(compressor, cached, depth);
if (element === value) return this;
@@ -2694,7 +2753,7 @@ merge(Compressor.prototype, {
def(AST_Object, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var val = {};
for (var i = 0, len = this.properties.length; i < len; i++) {
for (var i = 0; i < this.properties.length; i++) {
var prop = this.properties[i];
var key = prop.key;
if (key instanceof AST_Symbol) {
@@ -2794,7 +2853,7 @@ merge(Compressor.prototype, {
var fixed = this.fixed_value();
if (!fixed) return this;
var value;
if (cached.indexOf(fixed) >= 0) {
if (member(fixed, cached)) {
value = fixed._eval();
} else {
this._eval = return_this;
@@ -2808,9 +2867,25 @@ merge(Compressor.prototype, {
}
if (value && typeof value == "object") {
var escaped = this.definition().escaped;
if (escaped && depth > escaped) return this;
switch (escaped.length) {
case 0:
break;
case 1:
if (contains_ref(escaped[0], this)) break;
default:
if (depth > escaped.depth) return this;
}
}
return value;
function contains_ref(expr, ref) {
var found = false;
expr.walk(new TreeWalker(function(node) {
if (found) return true;
if (node === ref) return found = true;
}));
return found;
}
});
var global_objs = {
Array: Array,
@@ -2889,7 +2964,7 @@ merge(Compressor.prototype, {
if (!native_fn || !native_fn[key]) return this;
}
var args = [];
for (var i = 0, len = this.args.length; i < len; i++) {
for (var i = 0; i < this.args.length; i++) {
var arg = this.args[i];
var value = arg._eval(compressor, cached, depth);
if (arg === value) return this;
@@ -3240,6 +3315,12 @@ merge(Compressor.prototype, {
return true;
}
def(AST_Node, return_false);
def(AST_Array, function() {
return all(this.elements);
});
def(AST_Binary, function() {
return this.left.is_constant_expression() && this.right.is_constant_expression();
});
def(AST_Constant, return_true);
def(AST_Lambda, function(scope) {
var self = this;
@@ -3268,21 +3349,15 @@ merge(Compressor.prototype, {
}));
return result;
});
def(AST_Unary, function() {
return this.expression.is_constant_expression();
});
def(AST_Binary, function() {
return this.left.is_constant_expression() && this.right.is_constant_expression();
});
def(AST_Array, function() {
return all(this.elements);
});
def(AST_Object, function() {
return all(this.properties);
});
def(AST_ObjectProperty, function() {
return this.value.is_constant_expression();
});
def(AST_Unary, function() {
return this.expression.is_constant_expression();
});
})(function(node, func) {
node.DEFMETHOD("is_constant_expression", func);
});
@@ -3425,9 +3500,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def) {
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
}
var_defs_by_id.add(node_def.id, def);
if (!drop_vars) {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
@@ -3534,29 +3607,49 @@ merge(Compressor.prototype, {
if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) {
def.value = def.value.drop_side_effect_free(compressor);
}
if (def.name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
var ref = make_node(AST_SymbolRef, def.name, def.name);
sym.references.push(ref);
var assign = make_node(AST_Assign, def, {
operator: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[sym.id] = assign;
}
side_effects.push(assign.transform(tt));
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
var ref = make_node(AST_SymbolRef, def.name, def.name);
sym.references.push(ref);
var assign = make_node(AST_Assign, def, {
operator: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[sym.id] = assign;
}
remove(var_defs, def);
sym.eliminated++;
return;
side_effects.push(assign.transform(tt));
}
remove(var_defs, def);
sym.eliminated++;
return;
}
if (def.value) {
if (!def.value) {
head.push(def);
} else if (compressor.option("functions")
&& def.value === def.name.fixed_value()
&& def.value instanceof AST_Function
&& can_rename(def.value, def.name.name)
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
compressor.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
var defun = make_node(AST_Defun, def, def.value);
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
var name_def = def.name.scope.resolve().def_function(defun.name);
if (def.value.name) {
var old_def = def.value.name.definition();
def.value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef && node.definition() === old_def) {
node.name = name_def.name;
node.thedef = name_def;
node.reference({});
}
}));
}
body.push(defun);
} else {
if (side_effects.length > 0) {
if (tail.length > 0) {
side_effects.push(def.value);
@@ -3569,8 +3662,6 @@ merge(Compressor.prototype, {
side_effects = [];
}
tail.push(def);
} else {
head.push(def);
}
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
var value = def.value && def.value.drop_side_effect_free(compressor);
@@ -3587,6 +3678,11 @@ merge(Compressor.prototype, {
}
sym.eliminated++;
}
function can_rename(fn, name) {
var def = fn.variables.get(name);
return !def || fn.name && def === fn.name.definition();
}
});
if (head.length > 0 || tail.length > 0) {
node.definitions = head.concat(tail);
@@ -3612,6 +3708,7 @@ merge(Compressor.prototype, {
// https://github.com/mishoo/UglifyJS2/issues/44
// https://github.com/mishoo/UglifyJS2/issues/1830
// https://github.com/mishoo/UglifyJS2/issues/1838
// https://github.com/mishoo/UglifyJS2/issues/3371
// that's an invalid AST.
// We fix it at this stage by moving the `var` outside the `for`.
if (node instanceof AST_For) {
@@ -3622,7 +3719,15 @@ merge(Compressor.prototype, {
node.init = block.body.pop();
block.body.push(node);
}
if (node.init instanceof AST_SimpleStatement) {
if (node.init instanceof AST_Defun) {
if (!block) {
block = make_node(AST_BlockStatement, node, {
body: [ node ]
});
}
block.body.splice(-1, 0, node.init);
node.init = null;
} else if (node.init instanceof AST_SimpleStatement) {
node.init = node.init.body;
} else if (is_empty(node.init)) {
node.init = null;
@@ -3928,7 +4033,7 @@ merge(Compressor.prototype, {
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped == 1) return;
if (def.escaped.depth == 1) return;
if (def.references.length == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
@@ -3992,17 +4097,17 @@ merge(Compressor.prototype, {
return this;
});
def(AST_Binary, function(compressor, first_in_statement) {
var right = this.right.drop_side_effect_free(compressor);
var right = this.right.drop_side_effect_free(compressor, first_in_statement);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
if (lazy_op[this.operator]) {
if (right === this.right) return this;
var node = this.clone();
node.right = right;
node.right = right.drop_side_effect_free(compressor);
return node;
} else {
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
return make_sequence(this, [ left, right ]);
if (!left) return right;
return make_sequence(this, [ left, right.drop_side_effect_free(compressor) ]);
}
});
def(AST_Call, function(compressor, first_in_statement) {
@@ -4062,19 +4167,6 @@ merge(Compressor.prototype, {
def(AST_Function, function(compressor) {
return this.name && compressor.option("ie8") ? this : null;
});
def(AST_Unary, function(compressor, first_in_statement) {
if (unary_side_effects[this.operator]) {
this.write_only = !this.expression.has_side_effects(compressor);
return this;
}
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null;
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
if (first_in_statement && expression && is_iife_call(expression)) {
if (expression === this.expression && this.operator == "!") return this;
return expression.negate(compressor, first_in_statement);
}
return expression;
});
def(AST_Object, function(compressor, first_in_statement) {
var values = trim(this.properties, compressor, first_in_statement);
return values && make_sequence(this, values);
@@ -4082,12 +4174,10 @@ merge(Compressor.prototype, {
def(AST_ObjectProperty, function(compressor, first_in_statement) {
return this.value.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Sequence, function(compressor) {
var last = this.tail_node();
var expr = last.drop_side_effect_free(compressor);
if (expr === last) return this;
var expressions = this.expressions.slice(0, -1);
if (expr) expressions.push(expr);
def(AST_Sequence, function(compressor, first_in_statement) {
var expressions = trim(this.expressions, compressor, first_in_statement);
if (expressions === this.expressions) return this;
if (!expressions) return null;
return make_sequence(this, expressions);
});
def(AST_Sub, function(compressor, first_in_statement) {
@@ -4099,9 +4189,27 @@ merge(Compressor.prototype, {
return make_sequence(this, [ expression, property ]);
});
def(AST_SymbolRef, function(compressor) {
return this.is_declared(compressor) ? null : this;
if (!this.is_declared(compressor)) return this;
this.definition().replaced++;
return null;
});
def(AST_This, return_null);
def(AST_Unary, function(compressor, first_in_statement) {
if (unary_side_effects[this.operator]) {
this.write_only = !this.expression.has_side_effects(compressor);
return this;
}
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) {
this.expression.definition().replaced++;
return null;
}
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
if (first_in_statement && expression && is_iife_call(expression)) {
if (expression === this.expression && this.operator == "!") return this;
return expression.negate(compressor, first_in_statement);
}
return expression;
});
})(function(node, func) {
node.DEFMETHOD("drop_side_effect_free", func);
});
@@ -4596,9 +4704,7 @@ merge(Compressor.prototype, {
});
OPT(AST_Definitions, function(self, compressor) {
if (self.definitions.length == 0)
return make_node(AST_EmptyStatement, self);
return self;
return self.definitions.length ? self : make_node(AST_EmptyStatement, self);
});
AST_Call.DEFMETHOD("lift_sequences", function(compressor) {
@@ -4630,7 +4736,7 @@ merge(Compressor.prototype, {
&& !fn.uses_arguments
&& !fn.pinned()) {
var pos = 0, last = 0;
for (var i = 0, len = self.args.length; i < len; i++) {
for (var i = 0; i < self.args.length; i++) {
var trim = i >= fn.argnames.length;
if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor);
@@ -4652,12 +4758,30 @@ merge(Compressor.prototype, {
if (compressor.option("unsafe")) {
if (is_undeclared_ref(exp)) switch (exp.name) {
case "Array":
if (self.args.length != 1) {
return make_node(AST_Array, self, {
elements: self.args
}).optimize(compressor);
if (self.args.length == 1) {
var first = self.args[0];
if (first instanceof AST_Number) try {
var length = first.getValue();
if (length > 6) break;
var elements = Array(length);
for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
return make_node(AST_Array, self, {
elements: elements
});
} catch (ex) {
compressor.warn("Invalid array length: {length} [{file}:{line},{col}]", {
length: length,
file: self.start.file,
line: self.start.line,
col: self.start.col
});
break;
}
if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
}
break;
return make_node(AST_Array, self, {
elements: self.args
});
case "Object":
if (self.args.length == 0) {
return make_node(AST_Object, self, {
@@ -4990,7 +5114,7 @@ merge(Compressor.prototype, {
}
function can_inject_args(catches, safe_to_inject) {
for (var i = 0, len = fn.argnames.length; i < len; i++) {
for (var i = 0; i < fn.argnames.length; i++) {
var arg = fn.argnames[i];
if (arg.__unused) continue;
if (!safe_to_inject
@@ -5005,8 +5129,7 @@ merge(Compressor.prototype, {
}
function can_inject_vars(catches, safe_to_inject) {
var len = fn.body.length;
for (var i = 0; i < len; i++) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (!(stat instanceof AST_Var)) continue;
if (!safe_to_inject) return false;
@@ -5035,7 +5158,8 @@ merge(Compressor.prototype, {
if (scope.fixed_value() instanceof AST_Scope) return false;
}
} while (!(scope instanceof AST_Scope));
var safe_to_inject = !(scope instanceof AST_Toplevel) || compressor.toplevel.vars;
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
&& (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope));
var inline = compressor.option("inline");
if (!can_inject_vars(catches, inline >= 3 && safe_to_inject)) return false;
if (!can_inject_args(catches, inline >= 2 && safe_to_inject)) return false;
@@ -5085,10 +5209,10 @@ merge(Compressor.prototype, {
function flatten_vars(decls, expressions) {
var pos = expressions.length;
for (var i = 0, lines = fn.body.length; i < lines; i++) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (!(stat instanceof AST_Var)) continue;
for (var j = 0, defs = stat.definitions.length; j < defs; j++) {
for (var j = 0; j < stat.definitions.length; j++) {
var var_def = stat.definitions[j];
var name = var_def.name;
var redef = name.definition().redefined();
@@ -5789,7 +5913,7 @@ merge(Compressor.prototype, {
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
if (single_use && fixed instanceof AST_Lambda) {
if (def.scope !== self.scope
&& (!compressor.option("reduce_funcs") || def.escaped == 1 || fixed.inlined)) {
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
single_use = false;
} else if (recursive_ref(compressor, def)) {
single_use = false;
@@ -5844,6 +5968,7 @@ merge(Compressor.prototype, {
value = fixed.optimize(compressor);
if (value === fixed) value = fixed.clone(true);
}
def.replaced++;
return value;
}
if (fixed && def.should_replace === undefined) {
@@ -5886,7 +6011,9 @@ merge(Compressor.prototype, {
}
}
if (def.should_replace) {
return def.should_replace();
var value = def.should_replace();
def.replaced++;
return value;
}
}
return self;
@@ -6010,6 +6137,7 @@ merge(Compressor.prototype, {
|| parent instanceof AST_Sequence && parent.tail_node() === node);
}
self = self.lift_sequences(compressor);
if (!compressor.option("assignments")) return self;
if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
// x = expr1 OP expr2
if (self.right.left instanceof AST_SymbolRef
@@ -6028,6 +6156,16 @@ merge(Compressor.prototype, {
self.right = self.right.left;
}
}
if ((self.operator == "+=" || self.operator == "-=")
&& self.left.is_number(compressor)
&& self.right instanceof AST_Number
&& self.right.getValue() === 1) {
var op = self.operator.slice(0, -1);
return make_node(AST_UnaryPrefix, self, {
operator: op + op,
expression: self.left
});
}
return self;
function in_try(level, node) {

View File

@@ -123,7 +123,7 @@ function OutputStream(options) {
});
} : function(str) {
var s = "";
for (var i = 0, len = str.length; i < len; i++) {
for (var i = 0; i < str.length; i++) {
if (is_surrogate_pair_head(str[i]) && !is_surrogate_pair_tail(str[i + 1])
|| is_surrogate_pair_tail(str[i]) && !is_surrogate_pair_head(str[i - 1])) {
s += "\\u" + str.charCodeAt(i).toString(16);

View File

@@ -164,10 +164,6 @@ function is_unicode_connector_punctuation(ch) {
return UNICODE.connector_punctuation.test(ch);
}
function is_identifier(name) {
return !RESERVED_WORDS[name] && /^[a-z_$][a-z0-9_$]*$/i.test(name);
}
function is_identifier_start(code) {
return code == 36 || code == 95 || is_letter(code);
}
@@ -272,10 +268,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function find_eol() {
var text = S.text;
for (var i = S.pos, n = S.text.length; i < n; ++i) {
var ch = text[i];
if (NEWLINE_CHARS[ch])
return i;
for (var i = S.pos; i < S.text.length; ++i) {
if (NEWLINE_CHARS[text[i]]) return i;
}
return -1;
}

View File

@@ -61,8 +61,6 @@ SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
if (!options) options = {};
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
@@ -198,24 +196,20 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
if (node instanceof AST_SymbolLambda) {
var def = node.thedef;
if (def.orig.length == 1) {
redefine(node, node.scope.parent_scope);
node.thedef.init = def.init;
}
redefine(node, node.scope.parent_scope);
node.thedef.init = def.init;
return true;
}
}));
function redefine(node, scope) {
var name = node.name;
var refs = node.thedef.references;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
var old_def = node.thedef;
var new_def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
old_def.orig.concat(old_def.references).forEach(function(node) {
node.thedef = new_def;
node.reference(options);
});
node.thedef = def;
node.reference(options);
}
});
@@ -300,7 +294,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
AST_Lambda.DEFMETHOD("resolve", return_this);
AST_Scope.DEFMETHOD("resolve", function() {
return this.parent_scope;
return this.parent_scope.resolve();
});
AST_Toplevel.DEFMETHOD("resolve", return_this);
@@ -337,7 +331,7 @@ function next_mangled_name(scope, options, def) {
} while (scope = scope.parent_scope);
});
var name;
for (var i = 0, len = holes.length; i < len; i++) {
for (var i = 0; i < holes.length; i++) {
name = base54(holes[i]);
if (names[name]) continue;
holes.splice(i, 1);
@@ -346,7 +340,7 @@ function next_mangled_name(scope, options, def) {
}
while (true) {
name = base54(++scope.cname);
if (in_use[name] || !is_identifier(name) || options.reserved.has[name]) continue;
if (in_use[name] || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
if (!names[name]) break;
holes.push(scope.cname);
}
@@ -426,7 +420,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var name;
do {
name = base54(++lname);
} while (!is_identifier(name));
} while (RESERVED_WORDS[name]);
node.mangled_name = name;
return true;
}
@@ -497,7 +491,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var name;
do {
name = base54(cname++);
} while (avoid[name] || !is_identifier(name));
} while (avoid[name] || RESERVED_WORDS[name]);
return name;
}
@@ -559,7 +553,7 @@ var base54 = (function() {
var freq = Object.create(null);
function init(chars) {
var array = [];
for (var i = 0, len = chars.length; i < len; i++) {
for (var i = 0; i < chars.length; i++) {
var ch = chars[i];
array.push(ch);
freq[ch] = -1e-2 * i;

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.4.10",
"version": "3.5.6",
"engines": {
"node": ">=0.8.0"
},
@@ -23,12 +23,12 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.19.0",
"commander": "~2.20.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~6.1.1",
"semver": "~5.6.0"
"semver": "~6.0.0"
},
"scripts": {
"test": "node test/run-tests.js"

View File

@@ -239,3 +239,113 @@ index_length: {
}
expect_stdout: "1 2"
}
constructor_bad: {
options = {
unsafe: true
}
input: {
try {
Array(NaN);
console.log("FAIL1");
} catch (ex) {
try {
new Array(NaN);
console.log("FAIL2");
} catch (ex) {
console.log("PASS");
}
}
try {
Array(3.14);
console.log("FAIL1");
} catch (ex) {
try {
new Array(3.14);
console.log("FAIL2");
} catch (ex) {
console.log("PASS");
}
}
}
expect: {
try {
Array(NaN);
console.log("FAIL1");
} catch (ex) {
try {
Array(NaN);
console.log("FAIL2");
} catch (ex) {
console.log("PASS");
}
}
try {
Array(3.14);
console.log("FAIL1");
} catch (ex) {
try {
Array(3.14);
console.log("FAIL2");
} catch (ex) {
console.log("PASS");
}
}
}
expect_stdout: [
"PASS",
"PASS",
]
expect_warnings: [
"WARN: Invalid array length: 3.14 [test/compress/arrays.js:13,12]",
"WARN: Invalid array length: 3.14 [test/compress/arrays.js:17,16]",
]
}
constructor_good: {
options = {
unsafe: true
}
input: {
console.log(Array());
console.log(Array(0));
console.log(Array(1));
console.log(Array(6));
console.log(Array(7));
console.log(Array(1, 2));
console.log(Array(false));
console.log(Array("foo"));
console.log(Array(Array));
console.log(new Array());
console.log(new Array(0));
console.log(new Array(1));
console.log(new Array(6));
console.log(new Array(7));
console.log(new Array(1, 2));
console.log(new Array(false));
console.log(new Array("foo"));
console.log(new Array(Array));
}
expect: {
console.log([]);
console.log([]);
console.log([,]);
console.log([,,,,,,]);
console.log(Array(7));
console.log([ 1, 2 ]);
console.log([ false ]);
console.log([ "foo" ]);
console.log(Array(Array));
console.log([]);
console.log([]);
console.log([,]);
console.log([,,,,,,]);
console.log(Array(7));
console.log([ 1, 2 ]);
console.log([ false ]);
console.log([ "foo" ]);
console.log(Array(Array));
}
expect_stdout: true
expect_warnings: []
}

View File

@@ -1,5 +1,6 @@
asm_mixed: {
options = {
assignments: true,
booleans: true,
comparisons: true,
conditionals: true,

View File

@@ -1,5 +1,6 @@
op_equals_left_local_var: {
options = {
assignments: true,
evaluate: true,
}
input: {
@@ -60,6 +61,7 @@ op_equals_left_local_var: {
op_equals_right_local_var: {
options = {
assignments: true,
evaluate: true,
}
input: {
@@ -123,6 +125,7 @@ op_equals_right_local_var: {
}
op_equals_left_global_var: {
options = {
assignments: true,
evaluate: true,
}
input: {
@@ -179,6 +182,7 @@ op_equals_left_global_var: {
op_equals_right_global_var: {
options = {
assignments: true,
evaluate: true,
}
input: {
@@ -236,3 +240,52 @@ op_equals_right_global_var: {
x = g() & x;
}
}
increment_decrement_1: {
options = {
assignments: true,
reduce_vars: true,
}
input: {
console.log(function(a) {
a += 1;
a -= 1;
return a;
}(42));
}
expect: {
console.log(function(a){
++a;
--a;
return a;
}(42));
}
expect_stdout: "42"
}
increment_decrement_2: {
options = {
assignments: true,
passes: 2,
reduce_vars: true,
}
input: {
console.log(function(a) {
a = a + 1;
a = a - 1;
a += 1;
a -= 1;
return a;
}(42));
}
expect: {
console.log(function(a){
++a;
--a;
++a;
--a;
return a;
}(42));
}
expect_stdout: "42"
}

View File

@@ -944,7 +944,7 @@ collapse_vars_misc1: {
function f5(x) { var z = foo(); return (5 - window.x) / z }
function f6() { return window.a * window.z && zap() }
function f7() { var b = window.a * window.z; return b + b }
function f8() { var b = window.a * window.z; return b + (b + 5) }
function f8() { var b = window.a * window.z; return b + (5 + b) }
function f9() { var b = window.a * window.z; return bar() || b }
function f10(x) { var a = 5; return a += 3; }
function f11(x) { var a = 5; return a += 2; }
@@ -3895,11 +3895,11 @@ issue_2436_10: {
o = { b: 3 };
return n;
}
console.log((c = o, [
c.a,
console.log([
(c = o).a,
f(c.b),
c.b,
]).join(" "));
].join(" "));
var c;
}
expect_stdout: "1 2 2"
@@ -6121,3 +6121,39 @@ issue_3327: {
}
expect_stdout: "PASS 42"
}
assign_left: {
options = {
collapse_vars: true,
}
input: {
console.log(function(a, b) {
(b = a, b.p).q = "PASS";
return a.p.q;
}({p: {}}));
}
expect: {
console.log(function(a, b) {
(b = a).p.q = "PASS";
return a.p.q;
}({p: {}}));
}
expect_stdout: "PASS"
}
sub_property: {
options = {
collapse_vars: true,
}
input: {
console.log(function(a, b) {
return a[(b = a, b.length - 1)];
}([ "FAIL", "PASS" ]));
}
expect: {
console.log(function(a, b) {
return a[(b = a).length - 1];
}([ "FAIL", "PASS" ]));
}
expect_stdout: "PASS"
}

View File

@@ -323,3 +323,25 @@ is_number_unsafe: {
}
expect_stdout: "true"
}
is_boolean_var: {
options = {
comparisons: true,
reduce_vars: true,
}
input: {
console.log(function(a, b) {
for (var i = 0, c = !b; i < a.length; i++)
if (!a[i] === c)
return i;
}([ false, true ], 42));
}
expect: {
console.log(function(a, b) {
for (var i = 0, c = !b; i < a.length; i++)
if (!a[i] == c)
return i;
}([ false, true ], 42));
}
expect_stdout: "1"
}

View File

@@ -1367,6 +1367,7 @@ cond_seq_assign_2: {
cond_seq_assign_3: {
options = {
assignments: true,
conditionals: true,
}
input: {

View File

@@ -1124,14 +1124,14 @@ issue_2207_1: {
console.log(Math.max(3, 6, 2, 7, 3, 4));
console.log(Math.cos(1.2345));
console.log(Math.cos(1.2345) - Math.sin(4.321));
console.log(Math.pow(Math.PI, Math.E - Math.LN10));
console.log(Math.pow(Math.PI, Math.E - Math.LN10).toFixed(15));
}
expect: {
console.log("A");
console.log(7);
console.log(Math.cos(1.2345));
console.log(1.2543732512566947);
console.log(1.6093984514472044);
console.log("1.609398451447204");
}
expect_stdout: true
}
@@ -1540,7 +1540,7 @@ issue_2926_2: {
expect_stdout: "function"
}
issue_2968: {
issue_2968_1: {
options = {
collapse_vars: true,
evaluate: true,
@@ -1571,6 +1571,39 @@ issue_2968: {
expect_stdout: "PASS"
}
issue_2968_2: {
options = {
assignments: true,
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var c = "FAIL";
(function() {
(function(a, b) {
a <<= 0;
a && (a[(c = "PASS", 0 >>> (b += 1))] = 0);
})(42, -42);
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
a = 42,
((a <<= 0) && (a[(c = "PASS", 0)] = 0));
var a;
})();
console.log(c);
}
expect_stdout: "PASS"
}
truthy_conditionals: {
options = {
conditionals: true,
@@ -1610,3 +1643,72 @@ truthy_loops: {
}
}
}
if_increment: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
if (console)
return ++a;
}(0));
}
expect: {
console.log(function(a) {
if (console)
return 1;
}());
}
expect_stdout: "1"
}
try_increment: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
try {
return ++a;
} catch (e) {}
}(0));
}
expect: {
console.log(function(a) {
try {
return 1;
} catch (e) {}
}());
}
expect_stdout: "1"
}
unsafe_escaped: {
options = {
evaluate: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
(function(a) {
console.log(function(index) {
return a[index];
}(function(term) {
return a.indexOf(term);
}("PASS")));
})([ "PASS" ]);
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -358,6 +358,7 @@ inner_ref: {
issue_2107: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
passes: 3,
@@ -387,6 +388,7 @@ issue_2107: {
issue_2114_1: {
options = {
assignments: true,
collapse_vars: true,
if_return: true,
inline: true,
@@ -419,6 +421,7 @@ issue_2114_1: {
issue_2114_2: {
options = {
assignments: true,
collapse_vars: true,
if_return: true,
inline: true,
@@ -1223,6 +1226,7 @@ issue_2630_1: {
issue_2630_2: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
passes: 2,
@@ -1320,6 +1324,7 @@ issue_2630_4: {
issue_2630_5: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
reduce_vars: true,
@@ -1398,6 +1403,8 @@ recursive_inline_2: {
issue_2657: {
options = {
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
unused: true,
@@ -2467,6 +2474,7 @@ issue_3297_3: {
inline: true,
join_vars: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@@ -2505,18 +2513,18 @@ issue_3297_3: {
}).processBulk([1, 2, 3]);
}
expect: {
function function1(c) {
function function1(u) {
return {
processBulk: function n(o) {
var r, t, u = c();
o && 0 < o.length && (r = {
param1: o.shift(),
processBulk: function n(r) {
var o, t = u();
r && 0 < r.length && (o = {
param1: r.shift(),
param2: {
subparam1: u
subparam1: t
}
}, t = function() {
n(o);
}, console.log(JSON.stringify(r)), t());
},
console.log(JSON.stringify(o)),
n(r));
}
};
}
@@ -2530,3 +2538,506 @@ issue_3297_3: {
'{"param1":3,"param2":{"subparam1":42}}',
]
}
cross_references_1: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
var Math = {
square: function(n) {
return n * n;
}
};
console.log((function(factory) {
return factory();
})(function() {
return function(Math) {
return function(n) {
return Math.square(n);
};
}(Math);
})(3));
}
expect: {
var Math = {
square: function(n) {
return n * n;
}
};
console.log(function(Math) {
return function(n) {
return Math.square(n);
};
}(Math)(3));
}
expect_stdout: "9"
}
cross_references_2: {
options = {
collapse_vars: true,
evaluate: true,
hoist_props: true,
inline: true,
passes: 4,
pure_getters: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var Math = {
square: function(n) {
return n * n;
}
};
console.log((function(factory) {
return factory();
})(function() {
return function(Math) {
return function(n) {
return Math.square(n);
};
}(Math);
})(3));
}
expect: {
console.log(9);
}
expect_stdout: "9"
}
cross_references_3: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
var Math = {
square: function(n) {
return n * n;
},
cube: function(n) {
return n * n * n;
}
};
console.log(function(factory) {
return factory();
}(function() {
return function(Math) {
return function(n) {
Math = {
square: function(x) {
return "(SQUARE" + x + ")";
},
cube: function(x) {
return "(CUBE" + x + ")";
}
};
return Math.square(n) + Math.cube(n);
};
}(Math);
})(2));
console.log(Math.square(3), Math.cube(3));
}
expect: {
var Math = {
square: function(n) {
return n * n;
},
cube: function(n) {
return n * n * n;
}
};
console.log(function(Math) {
return function(n) {
Math = {
square: function(x) {
return "(SQUARE" + x + ")";
},
cube: function(x) {
return "(CUBE" + x + ")";
}
};
return Math.square(n) + Math.cube(n);
};
}(Math)(2));
console.log(Math.square(3), Math.cube(3));
}
expect_stdout: [
"(SQUARE2)(CUBE2)",
"9 27",
]
}
loop_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(o) {
function g(p) {
return o[p];
}
function h(q) {
while (g(q));
}
return h;
}([ 1, "foo", 0 ])(2));
}
expect: {
console.log(function(o) {
return function(q) {
while (p = q, o[p]);
var p;
};
}([ 1, "foo", 0 ])(2));
}
expect_stdout: "undefined"
}
functions: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = function a() {
return a && "a";
};
var b = function x() {
return !!x;
};
var c = function(c) {
return c;
};
if (c(b(a()))) {
var d = function() {};
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect: {
!function() {
function a() {
return a && "a";
}
function b() {
return !!b;
}
var c = function(c) {
return c;
};
if (c(b(a()))) {
function d() {}
function e() {
return typeof e;
}
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect_stdout: "a true 42 function function function"
}
functions_use_strict: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
!function() {
var a = function a() {
return a && "a";
};
var b = function x() {
return !!x;
};
var c = function(c) {
return c;
};
if (c(b(a()))) {
var d = function() {};
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect: {
"use strict";
!function() {
function a() {
return a && "a";
}
function b() {
return !!b;
}
var c = function(c) {
return c;
};
if (c(b(a()))) {
var d = function() {};
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect_stdout: "a true 42 function function function"
}
issue_2437: {
options = {
collapse_vars: true,
conditionals: true,
functions: true,
inline: true,
join_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
function bar() {
if (xhrDesc) {
var req = new XMLHttpRequest();
var result = !!req.onreadystatechange;
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
return result;
} else {
var req = new XMLHttpRequest();
var detectFunc = function(){};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
return result;
}
}
console.log(foo());
}
expect: {
console.log(function() {
if (xhrDesc) {
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
}
function detectFunc() {}
var req;
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
return req.onreadystatechange = null, result;
}());
}
}
issue_2485: {
options = {
functions: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
var foo = function(bar) {
var n = function(a, b) {
return a + b;
};
var sumAll = function(arg) {
return arg.reduce(n, 0);
};
var runSumAll = function(arg) {
return sumAll(arg);
};
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect: {
var foo = function(bar) {
function n(a, b) {
return a + b;
}
function runSumAll(arg) {
return function(arg) {
return arg.reduce(n, 0);
}(arg);
}
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect_stdout: "6"
}
issue_3364: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {}
input: {
var s = 2, a = 100, b = 10, c = 0;
function f(p, e, r) {
try {
for (var i = 1; i-- > 0;)
var a = function(x) {
function g(y) {
y && y[a++];
}
var x = g(--s >= 0 && f(c++));
for (var j = 1; --j > 0;);
}();
} catch (e) {
try {
return;
} catch (z) {
for (var k = 1; --k > 0;) {
for (var l = 1; l > 0; --l) {
var n = function() {};
for (var k in n)
var o = (n, k);
}
}
}
}
}
var r = f();
console.log(c);
}
expect: {
var s = 2, c = 0;
(function n(r, o, a) {
try {
for (var f = 1; f-- >0;)
var t = function(r) {
(function(r) {
r && r[t++];
})(--s >= 0 && n(c++));
for (var o = 1; --o > 0;);
}();
} catch (o) {
try {
return;
} catch (r) {
for (var v = 1; --v > 0;)
for (var i = 1; i > 0;--i) {
function u() {}
for (var v in u);
}
}
}
})();
console.log(c);
}
expect_stdout: "2"
}
issue_3366: {
options = {
functions: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {
return function() {};
}
var a = g();
(function() {
this && a && console.log("PASS");
})();
}
f();
}
expect: {
(function() {
function a() {}
(function() {
this && a && console.log("PASS");
})();
})();
}
expect_stdout: "PASS"
}
issue_3371: {
options = {
functions: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a = function f() {
(function() {
console.log(typeof f);
})();
};
while (a());
})();
}
expect: {
(function() {
function a() {
console.log(typeof a);
}
while (a());
})();
}
expect_stdout: "function"
}

View File

@@ -861,3 +861,111 @@ issue_3215_4: {
}
expect_stdout: "PASS"
}
issue_3355_1: {
mangle = {
ie8: false,
}
input: {
(function f() {
var f;
})();
(function g() {
})();
console.log(typeof f === typeof g);
}
expect: {
(function o() {
var o;
})();
(function o() {
})();
console.log(typeof f === typeof g);
}
expect_stdout: "true"
}
issue_3355_2: {
mangle = {
ie8: true,
}
input: {
(function f() {
var f;
})();
(function g() {
})();
console.log(typeof f === typeof g);
}
expect: {
(function f() {
var f;
})();
(function g() {
})();
console.log(typeof f === typeof g);
}
expect_stdout: "true"
}
issue_3355_3: {
mangle = {
ie8: false,
}
input: {
!function(a) {
"aaaaaaaaaa";
a();
var b = function c() {
var c = 42;
console.log("FAIL");
};
}(function() {
console.log("PASS");
});
}
expect: {
!function(a) {
"aaaaaaaaaa";
a();
var o = function a() {
var a = 42;
console.log("FAIL");
};
}(function() {
console.log("PASS");
});
}
expect_stdout: "PASS"
}
issue_3355_4: {
mangle = {
ie8: true,
}
input: {
!function(a) {
"aaaaaaaaaa";
a();
var b = function c() {
var c = 42;
console.log("FAIL");
};
}(function() {
console.log("PASS");
});
}
expect: {
!function(a) {
"aaaaaaaaaa";
a();
var o = function n() {
var n = 42;
console.log("FAIL");
};
}(function() {
console.log("PASS");
});
}
expect_stdout: "PASS"
}

View File

@@ -646,3 +646,30 @@ issue_2904: {
}
expect_stdout: "1"
}
issue_3371: {
options = {
functions: true,
join_vars: true,
loops: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = function() {
console.log("PASS");
};
while (a());
})();
}
expect: {
(function() {
function a() {
console.log("PASS");
}
for (; a(); );
})();
}
expect_stdout: "PASS"
}

View File

@@ -281,8 +281,8 @@ unsafe_evaluate_escaped: {
console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; console.log(o, o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p, o); return o.p; }());
console.log(function(){ var o={p:1}; console.log(o, 1); return o.p; }());
console.log(function(){ var o={p:2}; console.log(2, o); return o.p; }());
console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }());
}
expect_stdout: true
@@ -6716,3 +6716,24 @@ issue_3297: {
}
expect_stdout: "true"
}
drop_side_effect_free: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 123;
"" + (a && (a.b = 0) || a);
console.log(a);
}
expect: {
var a = 123;
a.b = 0;
console.log(a);
}
expect_stdout: "123"
}

View File

@@ -924,14 +924,14 @@ call: {
b.c = function() {
console.log(this === b ? "bar" : "baz");
},
a, b(),
b(),
(a, b.c)(),
a, function() {
function() {
console.log(this === a);
}(),
a, new b(),
a, new b.c(),
a, new function() {
new b(),
new b.c(),
new function() {
console.log(this === a);
}();
}
@@ -944,3 +944,23 @@ call: {
"false",
]
}
missing_link: {
options = {
conditionals: true,
evaluate: true,
sequences: true,
}
input: {
var a = 100;
a;
a++ + (0 ? 2 : 1);
console.log(a);
}
expect: {
var a = 100;
a,
a++,
console.log(a);
}
}

View File

@@ -1,7 +1,5 @@
exports["base54"] = base54;
exports["Compressor"] = Compressor;
exports["defaults"] = defaults;
exports["is_identifier"] = is_identifier;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
@@ -9,7 +7,6 @@ exports["OutputStream"] = OutputStream;
exports["parse"] = parse;
exports["push_uniq"] = push_uniq;
exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["SourceMap"] = SourceMap;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["TreeTransformer"] = TreeTransformer;

View File

@@ -3,7 +3,7 @@
"use strict";
var site = "https://browserbench.org/JetStream";
var site = "https://browserbench.org/JetStream1.1";
if (typeof phantom == "undefined") {
require("../tools/exit");
var args = process.argv.slice(2);

View File

@@ -1,6 +1,7 @@
var assert = require("assert");
var exec = require("child_process").exec;
var fs = require("fs");
var run_code = require("../sandbox").run_code;
function read(path) {
return fs.readFileSync(path, "utf8");
@@ -714,4 +715,32 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should compress swarm of unused variables with reasonable performance", function(done) {
var code = [
"console.log(function() {",
];
for (var i = 0; i < 10000; i++) {
code.push("var obj" + i + " = {p: " + i + "};");
}
code.push("var map = {");
for (var i = 0; i < 10000; i++) {
code.push("obj" + i + ": obj" + i + ",");
}
code = code.concat([
"};",
"return obj25.p + obj121.p + obj1024.p;",
"}());",
]).join("\n");
exec(uglifyjscmd + " -mc", function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, [
"console.log(function(){",
"var p={p:25},n={p:121},o={p:1024};",
"return p.p+n.p+o.p",
"}());\n",
].join(""));
assert.strictEqual(run_code(stdout), run_code(code));
done();
}).stdin.end(code);
});
});

View File

@@ -73,8 +73,10 @@ exports.run_code = function(code, reuse) {
process.stdout.write = original_write;
if (!reuse || code.indexOf(".prototype") >= 0) {
context = null;
} else for (var key in context) {
delete context[key];
} else {
vm.runInContext(Object.keys(context).map(function(name) {
return "delete " + name;
}).join("\n"), context);
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +1,540 @@
<!doctype html>
<html>
<head>
</head>
<body>
<script>(function(){
var props = {};
<body>
<script>
!function() {
var names = [];
var scanned = [];
var to_scan = [];
function addObject(obj) {
if (obj == null) return;
try {
Object.getOwnPropertyNames(obj).forEach(add);
} catch(ex) {}
if (obj.prototype) {
Object.getOwnPropertyNames(obj.prototype).forEach(add);
}
if (typeof obj == "function") {
try {
Object.getOwnPropertyNames(new obj).forEach(add);
} catch(ex) {}
}
}
function scan(obj) {
if (obj && typeof obj == "object" && !~scanned.indexOf(obj)) {
scanned.push(obj);
to_scan.push(obj);
}
}
function add(name) {
props[name] = true;
}
scan(self);
[
"a",
"abbr",
"acronym",
"address",
"applet",
"area",
"article",
"aside",
"audio",
"b",
"base",
"basefont",
"bdi",
"bdo",
"bgsound",
"big",
"blink",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"center",
"checked",
"cite",
"code",
"col",
"colgroup",
"command",
"comment",
"compact",
"content",
"data",
"datalist",
"dd",
"declare",
"defer",
"del",
"details",
"dfn",
"dialog",
"dir",
"disabled",
"div",
"dl",
"dt",
"element",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"font",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"image",
"img",
"input",
"ins",
"isindex",
"ismap",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"listing",
"main",
"map",
"mark",
"marquee",
"math",
"menu",
"menuitem",
"meta",
"meter",
"multicol",
"multiple",
"nav",
"nobr",
"noembed",
"noframes",
"nohref",
"noresize",
"noscript",
"noshade",
"nowrap",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"plaintext",
"pre",
"progress",
"q",
"rb",
"readonly",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"selected",
"shadow",
"small",
"source",
"spacer",
"span",
"strike",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"tt",
"u",
"ul",
"var",
"video",
"wbr",
"xmp",
"XXX",
].forEach(function(tag) {
scan(document.createElement(tag));
});
[
"abort",
"absolutedeviceorientation",
"activate",
"active",
"addsourcebuffer",
"addstream",
"addtrack",
"afterprint",
"afterscriptexecute",
"afterupdate",
"animationcancel",
"animationend",
"animationiteration",
"animationstart",
"appinstalled",
"audioend",
"audioprocess",
"audiostart",
"autocomplete",
"autocompleteerror",
"auxclick",
"beforeactivate",
"beforecopy",
"beforecut",
"beforedeactivate",
"beforeeditfocus",
"beforeinstallprompt",
"beforepaste",
"beforeprint",
"beforescriptexecute",
"beforeunload",
"beforeupdate",
"blocked",
"blur",
"bounce",
"boundary",
"cached",
"cancel",
"candidatewindowhide",
"candidatewindowshow",
"candidatewindowupdate",
"canplay",
"canplaythrough",
"cellchange",
"change",
"chargingchange",
"chargingtimechange",
"checking",
"click",
"close",
"compassneedscalibration",
"complete",
"connect",
"connecting",
"connectionstatechange",
"contextmenu",
"controllerchange",
"controlselect",
"copy",
"cuechange",
"cut",
"dataavailable",
"datachannel",
"datasetchanged",
"datasetcomplete",
"dblclick",
"deactivate",
"devicechange",
"devicelight",
"devicemotion",
"deviceorientation",
"deviceorientationabsolute",
"deviceproximity",
"dischargingtimechange",
"disconnect",
"display",
"downloading",
"drag",
"dragend",
"dragenter",
"dragexit",
"dragleave",
"dragover",
"dragstart",
"drop",
"durationchange",
"emptied",
"encrypted",
"end",
"ended",
"enter",
"enterpictureinpicture",
"error",
"errorupdate",
"exit",
"filterchange",
"finish",
"focus",
"focusin",
"focusout",
"freeze",
"fullscreenchange",
"fullscreenerror",
"gesturechange",
"gestureend",
"gesturestart",
"gotpointercapture",
"hashchange",
"help",
"icecandidate",
"iceconnectionstatechange",
"icegatheringstatechange",
"inactive",
"input",
"invalid",
"keydown",
"keypress",
"keyup",
"languagechange",
"layoutcomplete",
"leavepictureinpicture",
"levelchange",
"load",
"loadeddata",
"loadedmetadata",
"loadend",
"loading",
"loadingdone",
"loadingerror",
"loadstart",
"losecapture",
"lostpointercapture",
"mark",
"message",
"messageerror",
"mousedown",
"mouseenter",
"mouseleave",
"mousemove",
"mouseout",
"mouseover",
"mouseup",
"mousewheel",
"move",
"moveend",
"movestart",
"mozfullscreenchange",
"mozfullscreenerror",
"mozorientationchange",
"mozpointerlockchange",
"mozpointerlockerror",
"mscontentzoom",
"msfullscreenchange",
"msfullscreenerror",
"msgesturechange",
"msgesturedoubletap",
"msgestureend",
"msgesturehold",
"msgesturestart",
"msgesturetap",
"msgotpointercapture",
"msinertiastart",
"mslostpointercapture",
"msmanipulationstatechanged",
"msneedkey",
"msorientationchange",
"mspointercancel",
"mspointerdown",
"mspointerenter",
"mspointerhover",
"mspointerleave",
"mspointermove",
"mspointerout",
"mspointerover",
"mspointerup",
"mssitemodejumplistitemremoved",
"msthumbnailclick",
"negotiationneeded",
"nomatch",
"noupdate",
"obsolete",
"offline",
"online",
"open",
"orientationchange",
"pagechange",
"pagehide",
"pageshow",
"paste",
"pause",
"play",
"playing",
"pluginstreamstart",
"pointercancel",
"pointerdown",
"pointerenter",
"pointerleave",
"pointerlockchange",
"pointerlockerror",
"pointermove",
"pointerout",
"pointerover",
"pointerup",
"popstate",
"progress",
"propertychange",
"ratechange",
"reading",
"readystatechange",
"rejectionhandled",
"removesourcebuffer",
"removestream",
"removetrack",
"reset",
"resize",
"resizeend",
"resizestart",
"resourcetimingbufferfull",
"result",
"resume",
"rowenter",
"rowexit",
"rowsdelete",
"rowsinserted",
"scroll",
"search",
"seeked",
"seeking",
"select",
"selectionchange",
"selectstart",
"show",
"signalingstatechange",
"soundend",
"soundstart",
"sourceclose",
"sourceclosed",
"sourceended",
"sourceopen",
"speechend",
"speechstart",
"stalled",
"start",
"statechange",
"stop",
"storage",
"storagecommit",
"submit",
"success",
"suspend",
"textinput",
"timeout",
"timeupdate",
"toggle",
"touchcancel",
"touchend",
"touchmove",
"touchstart",
"track",
"transitioncancel",
"transitionend",
"transitionrun",
"transitionstart",
"unhandledrejection",
"unload",
"updateready",
"upgradeneeded",
"userproximity",
"versionchange",
"visibilitychange",
"voiceschanged",
"volumechange",
"vrdisplayactivate",
"vrdisplayconnect",
"vrdisplaydeactivate",
"vrdisplaydisconnect",
"vrdisplaypresentchange",
"waiting",
"waitingforkey",
"warning",
"webkitanimationend",
"webkitanimationiteration",
"webkitanimationstart",
"webkitcurrentplaybacktargetiswirelesschanged",
"webkitfullscreenchange",
"webkitfullscreenerror",
"webkitkeyadded",
"webkitkeyerror",
"webkitkeymessage",
"webkitneedkey",
"webkitorientationchange",
"webkitplaybacktargetavailabilitychanged",
"webkitpointerlockchange",
"webkitpointerlockerror",
"webkitresourcetimingbufferfull",
"webkittransitionend",
"wheel",
"zoom",
].forEach(function(type) {
[
"beforeunloadevent",
"compositionevent",
"customevent",
"devicemotionevent",
"deviceorientationevent",
"dragevent",
"event",
"events",
"focusevent",
"hashchangeevent",
"htmlevents",
"keyboardevent",
"messageevent",
"mouseevent",
"mouseevents",
"storageevent",
"svgevents",
"textevent",
"touchevent",
"uievent",
"uievents",
].forEach(function(interface) {
try {
var event = document.createEvent(interface);
event.initEvent(type, true, true);
scan(event);
} catch (e) {}
});
});
Object.getOwnPropertyNames(window).forEach(function(thing){
addObject(window[thing]);
});
try {
addObject(new Event("click"));
addObject(new Event("contextmenu"));
addObject(new Event("mouseup"));
addObject(new Event("mousedown"));
addObject(new Event("keydown"));
addObject(new Event("keypress"));
addObject(new Event("keyup"));
} catch(ex) {}
var ta = document.createElement("textarea");
ta.style.width = "100%";
ta.style.height = "20em";
ta.style.boxSizing = "border-box";
<!-- ta.value = Object.keys(props).sort(cmp).map(function(name){ -->
<!-- return JSON.stringify(name); -->
<!-- }).join(",\n"); -->
ta.value = JSON.stringify({
vars: [],
props: Object.keys(props).sort(cmp)
}, null, 2);
document.body.appendChild(ta);
function cmp(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
return a < b ? -1 : a > b ? 1 : 0;
}
})();</script>
</body>
var obj;
while (obj = to_scan.shift()) {
var proto = obj;
do {
Object.getOwnPropertyNames(proto).forEach(function(name) {
var visited = ~names.indexOf(name);
if (!visited) names.push(name);
try {
scan(obj[name]);
if (visited) return;
if (/^create/.test(name)) {
scan(obj[name]());
}
if (/^[A-Z]/.test(name)) {
scan(new obj[name]());
}
} catch (e) {}
});
} while (proto = Object.getPrototypeOf(proto));
}
names.sort();
document.write('<pre>[\n "');
document.write(names.join('",\n "'));
document.write('"\n]</pre>');
}();
</script>
</body>
</html>