Compare commits

..

22 Commits

Author SHA1 Message Date
Alex Lam S.L
af1b2f30c9 v3.10.2 2020-08-23 23:09:12 +08:00
Alex Lam S.L
37b4fc7e31 update domprops (#4065) 2020-08-23 23:06:15 +08:00
Alex Lam S.L
da85d102e3 enhance mangle.properties (#4064) 2020-08-23 08:45:39 +08:00
Alex Lam S.L
35fe1092d3 simplify traversal logic (#4063) 2020-08-23 05:45:35 +08:00
Alex Lam S.L
f2d486e771 enhance comparisons (#4062) 2020-08-23 01:03:48 +08:00
Alex Lam S.L
fee677786e fix corner case in collapse_vars (#4061) 2020-08-21 10:35:34 +08:00
Alex Lam S.L
aa83ecdb3b fix corner case in switches (#4060)
fixes #4059
2020-08-21 08:05:10 +08:00
Alex Lam S.L
a153176469 enhance conditionals & switches (#4058) 2020-08-21 00:35:39 +08:00
Alex Lam S.L
1c6384b6a5 improve ufuzz duty cycle heuristic (#4057) 2020-08-19 23:29:01 +08:00
Alex Lam S.L
e8db526f51 avoid setters during console.log() in sandbox (#4055)
fixes #4054
2020-08-19 06:14:41 +08:00
Alex Lam S.L
fa13ed4391 reject multiple defaults in switch (#4053)
fixes #4050
2020-08-17 10:09:12 +08:00
Alex Lam S.L
23f0dca992 fix corner cases in collapse_vars & dead_code (#4052)
fixes #4051
2020-08-17 05:54:27 +08:00
Alex Lam S.L
45ab3b51d8 clarify toplevel & global variable aliasing (#4046) 2020-08-10 06:39:28 +08:00
Alex Lam S.L
49670d216b fix corner case in collapse_vars (#4048)
fixes #4047
2020-08-10 05:48:56 +08:00
Alex Lam S.L
e2237d8cd2 improve ufuzz duty cycle heuristic (#4045) 2020-08-09 03:10:19 +08:00
Alex Lam S.L
91f078fe35 workaround incorrect workflow status (#4044) 2020-08-08 05:16:54 +08:00
Alex Lam S.L
a546cb881d improve ufuzz duty cycle on GitHub Actions (#4043) 2020-08-07 18:42:36 +08:00
Alex Lam S.L
84d5dffd9f tweak GitHub Actions (#4042) 2020-08-07 02:15:51 +08:00
Alex Lam S.L
a8e286f7e1 fix corner case in collapse_vars (#4041)
fixes #4040
2020-08-06 20:30:28 +08:00
Alex Lam S.L
9b05494ebc fix corner cases in aliasing of global variables (#4039)
fixes #4038
2020-08-06 09:39:50 +01:00
Alex Lam S.L
30ef20a208 tweak GitHub Actions (#4037) 2020-08-05 22:09:02 +08:00
Alex Lam S.L
a4002ef467 fix corner case in evaluate (#4036)
fixes #4035
2020-08-04 20:05:10 +08:00
27 changed files with 2892 additions and 754 deletions

View File

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

View File

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

View File

@@ -212,17 +212,16 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported: (comma-separated) options are supported:
- `toplevel` (default `false`) -- mangle names declared in the top level scope. - `eval` (default `false`) -- mangle names visible in scopes where `eval` or
`with` are used.
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used. - `reserved` (default: `[]`) -- when mangling is enabled but you want to
prevent certain names from being mangled, you can declare those names with
When mangling is enabled but you want to prevent certain names from being `--mangle reserved` — pass a comma-separated list of names. For example:
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=['$','require','exports'] uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed. to prevent the `require`, `exports` and `$` names from being changed.
### CLI mangling property names (`--mangle-props`) ### CLI mangling property names (`--mangle-props`)
@@ -657,8 +656,8 @@ to be `false` and all symbol names will be omitted.
- `hoist_props` (default: `true`) -- hoist properties from constant object and - `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example: array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props` `var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher, works best with `toplevel` and `mangle` enabled, alongside with `compress` option
and the `compress` option `toplevel` enabled. `passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false` - `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general) by default because it seems to increase the size of the output in general)
@@ -1157,3 +1156,19 @@ To allow for better optimizations, the compiler makes various assumptions:
- Object properties can be added, removed and modified (not prevented with - Object properties can be added, removed and modified (not prevented with
`Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`, `Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`Object.preventExtensions()` or `Object.seal()`). `Object.preventExtensions()` or `Object.seal()`).
- When `toplevel` is enabled, UglifyJS effectively assumes input code is wrapped
within `function(){ ... }`, thus forbids aliasing of declared global variables:
```js
A = "FAIL";
var B = "FAIL";
// can be `global`, `self`, `window` etc.
var top = function() {
return this;
}();
// "PASS"
top.A = "PASS";
console.log(A);
// "FAIL" after compress and/or mangle
top.B = "PASS";
console.log(B);
```

View File

@@ -167,7 +167,7 @@ merge(Compressor.prototype, {
if (def.global) for (var i = 0; i < def.orig.length; i++) if (def.global) for (var i = 0; i < def.orig.length; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"]) if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return true; return true;
return false; return def.undeclared;
}, },
compress: function(node) { compress: function(node) {
node = node.resolve_defines(this); node = node.resolve_defines(this);
@@ -1386,6 +1386,7 @@ merge(Compressor.prototype, {
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor); var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
var scan_rhs = foldable(candidate); var scan_rhs = foldable(candidate);
if (!scan_lhs && !scan_rhs) continue; if (!scan_lhs && !scan_rhs) continue;
var read_toplevel = false;
var modify_toplevel = false; var modify_toplevel = false;
// Locate symbols which may execute code outside of scanning range // Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate); var lvalues = get_lvalues(candidate);
@@ -1548,8 +1549,9 @@ merge(Compressor.prototype, {
return lvalues.has(node.name.name) || side_effects && may_modify(node.name); return lvalues.has(node.name.name) || side_effects && may_modify(node.name);
} }
var sym = is_lhs(node.left, node); var sym = is_lhs(node.left, node);
if (sym && lvalues.has(sym.name)) return true;
if (sym instanceof AST_PropAccess) return true; if (sym instanceof AST_PropAccess) return true;
if (!(sym instanceof AST_SymbolRef)) return false;
return lvalues.has(sym.name) || read_toplevel && compressor.exposed(sym.definition());
} }
function extract_args() { function extract_args() {
@@ -1832,7 +1834,9 @@ merge(Compressor.prototype, {
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_VarDef) return find_stop_unused(parent, level + 1); if (parent instanceof AST_VarDef) {
return may_throw(parent) ? node : find_stop_unused(parent, level + 1);
}
return null; return null;
} }
@@ -1936,19 +1940,21 @@ merge(Compressor.prototype, {
}; };
} }
function may_be_global(node) {
if (node instanceof AST_SymbolRef) {
node = node.fixed_value();
if (!node) return true;
}
if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
return node instanceof AST_PropAccess || node instanceof AST_This;
}
function get_lvalues(expr) { function get_lvalues(expr) {
var lvalues = new Dictionary(); var lvalues = new Dictionary();
if (candidate instanceof AST_VarDef) lvalues.add(candidate.name.name, lhs); if (expr instanceof AST_VarDef) lvalues.add(expr.name.name, lhs);
var scan_iife = scope instanceof AST_Toplevel; var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
var scan_toplevel = scope instanceof AST_Toplevel;
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
if (scan_iife && node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_PropAccess) return;
if (exp instanceof AST_Function && !exp.contains_this()) return;
modify_toplevel = true;
scan_iife = false;
return;
}
var value; var value;
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
value = node.fixed_value() || node; value = node.fixed_value() || node;
@@ -1956,6 +1962,28 @@ merge(Compressor.prototype, {
value = node; value = node;
} }
if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0)); if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
if (find_arguments && node instanceof AST_Sub) {
scope.argnames.forEach(function(argname) {
if (!compressor.option("reduce_vars") || argname.definition().assignments) {
lvalues.add(argname.name, true);
}
});
find_arguments = false;
}
if (!scan_toplevel) return;
if (node.TYPE == "Call") {
if (modify_toplevel) return;
var exp = node.expression;
if (exp instanceof AST_PropAccess) return;
if (exp instanceof AST_Function && !exp.contains_this()) return;
modify_toplevel = true;
} else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
if (node === lhs && !(expr instanceof AST_Unary)) {
modify_toplevel = true;
} else {
read_toplevel = true;
}
}
}); });
expr.walk(tw); expr.walk(tw);
return lvalues; return lvalues;
@@ -1965,9 +1993,12 @@ merge(Compressor.prototype, {
if (expr.name instanceof AST_SymbolFunarg) { if (expr.name instanceof AST_SymbolFunarg) {
var index = compressor.self().argnames.indexOf(expr.name); var index = compressor.self().argnames.indexOf(expr.name);
var args = compressor.parent().args; var args = compressor.parent().args;
if (args[index]) args[index] = make_node(AST_Number, args[index], { if (args[index]) {
args[index] = make_node(AST_Number, args[index], {
value: 0 value: 0
}); });
expr.name.definition().fixed = false;
}
return true; return true;
} }
var end = hit_stack.length - 1; var end = hit_stack.length - 1;
@@ -5892,28 +5923,57 @@ merge(Compressor.prototype, {
} }
body[0].body = decl.concat(body[0].body); body[0].body = decl.concat(body[0].body);
self.body = body; self.body = body;
if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) { if (compressor.option("conditionals")) switch (body.length) {
var has_break = false; case 1:
var tw = new TreeWalker(function(node) { if (!no_break(body[0])) break;
if (has_break
|| node instanceof AST_Lambda
|| node instanceof AST_SimpleStatement) return true;
if (is_break(node, tw)) has_break = true;
});
self.walk(tw);
if (!has_break) {
var statements = body[0].body.slice();
var exp = body[0].expression; var exp = body[0].expression;
var statements = body[0].body.slice();
if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
condition: make_node(AST_Binary, self, {
operator: "===",
left: self.expression,
right: exp,
}),
body: make_node(AST_BlockStatement, self, {
body: statements,
}),
alternative: null,
}).optimize(compressor);
if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, { if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
body: exp body: exp,
})); }));
statements.unshift(make_node(AST_SimpleStatement, self.expression, { statements.unshift(make_node(AST_SimpleStatement, self.expression, {
body:self.expression body:self.expression,
})); }));
return make_node(AST_BlockStatement, self, { return make_node(AST_BlockStatement, self, {
body: statements body: statements,
}).optimize(compressor); }).optimize(compressor);
} case 2:
if (!member(default_branch, body) || !no_break(body[1])) break;
var statements = body[0].body.slice();
var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
if (exclusive) statements.pop();
if (!all(statements, no_break)) break;
var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
var node = make_node(AST_If, self, {
condition: make_node(AST_Binary, self, body[0] === default_branch ? {
operator: "!==",
left: self.expression,
right: body[1].expression,
} : {
operator: "===",
left: self.expression,
right: body[0].expression,
}),
body: make_node(AST_BlockStatement, body[0], {
body: statements,
}),
alternative: exclusive && alternative || null,
});
if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
body: [ node, alternative ],
});
return node.optimize(compressor);
} }
return self; return self;
@@ -5921,6 +5981,19 @@ merge(Compressor.prototype, {
return node instanceof AST_Break && tw.loopcontrol_target(node) === self; return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
} }
function no_break(node) {
var found = false;
var tw = new TreeWalker(function(node) {
if (found
|| node instanceof AST_Lambda
|| node instanceof AST_SimpleStatement) return true;
if (is_break(node, tw)) found = true;
});
tw.push(self);
node.walk(tw);
return !found;
}
function eliminate_branch(branch, prev) { function eliminate_branch(branch, prev) {
if (prev && !aborts(prev)) { if (prev && !aborts(prev)) {
prev.body = prev.body.concat(branch.body); prev.body = prev.body.concat(branch.body);
@@ -7039,7 +7112,7 @@ merge(Compressor.prototype, {
&& self.right instanceof AST_SymbolRef && self.right instanceof AST_SymbolRef
&& self.left.definition() === self.right.definition() && self.left.definition() === self.right.definition()
&& is_object(self.left)) { && is_object(self.left)) {
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self); return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
} }
break; break;
case "&&": case "&&":
@@ -7436,6 +7509,7 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) { if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
// 0 + n => n // 0 + n => n
case "+": case "+":
@@ -7488,6 +7562,7 @@ merge(Compressor.prototype, {
break; break;
} }
} }
}
if (compressor.option("typeofs")) switch (self.operator) { if (compressor.option("typeofs")) switch (self.operator) {
case "&&": case "&&":
mark_locally_defined(self.left, self.right, null); mark_locally_defined(self.left, self.right, null);
@@ -7896,12 +7971,17 @@ merge(Compressor.prototype, {
} }
} }
} else if (self.left instanceof AST_SymbolRef) { } else if (self.left instanceof AST_SymbolRef) {
if (self.operator == "=" && self.left.equivalent_to(self.right)) return self.right; var parent;
if (self.operator == "=" && self.left.equivalent_to(self.right)
&& !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
return self.right;
}
if (self.left.is_immutable()) return strip_assignment(); if (self.left.is_immutable()) return strip_assignment();
var def = self.left.definition(); var def = self.left.definition();
var scope = def.scope.resolve(); var scope = def.scope.resolve();
var local = scope === compressor.find_parent(AST_Lambda); var local = scope === compressor.find_parent(AST_Lambda);
var level = 0, node, parent = self; var level = 0, node;
parent = self;
do { do {
node = parent; node = parent;
parent = compressor.parent(level++); parent = compressor.parent(level++);

View File

@@ -197,9 +197,7 @@ function minify(files, options) {
toplevel.mangle_names(options.mangle); toplevel.mangle_names(options.mangle);
} }
if (timings) timings.properties = Date.now(); if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) { if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now(); if (timings) timings.output = Date.now();
var result = {}; var result = {};
if (options.output.ast) { if (options.output.ast) {

View File

@@ -1174,10 +1174,7 @@ function OutputStream(options) {
}); });
/* -----[ other expressions ]----- */ /* -----[ other expressions ]----- */
DEFPRINT(AST_Call, function(self, output) { function print_call_args(self, output) {
self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) { if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start); output.add_mapping(self.start);
} }
@@ -1187,11 +1184,16 @@ function OutputStream(options) {
expr.print(output); expr.print(output);
}); });
}); });
}
DEFPRINT(AST_Call, function(self, output) {
self.expression.print(output);
print_call_args(self, output);
}); });
DEFPRINT(AST_New, function(self, output) { DEFPRINT(AST_New, function(self, output) {
output.print("new"); output.print("new");
output.space(); output.space();
AST_Call.prototype._codegen(self, output); self.expression.print(output);
if (need_constructor_parens(self, output)) print_call_args(self, output);
}); });
DEFPRINT(AST_Sequence, function(self, output) { DEFPRINT(AST_Sequence, function(self, output) {
self.expressions.forEach(function(node, index) { self.expressions.forEach(function(node, index) {

View File

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

View File

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

View File

@@ -559,21 +559,23 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
options = _default_mangler_options(options); options = _default_mangler_options(options);
base54.reset(); base54.reset();
try { try {
AST_Node.prototype.print = function(stream, force_parens) { var fn = AST_Symbol.prototype.add_source_map;
this._print(stream, force_parens); AST_Symbol.prototype.add_source_map = function() {
if (this instanceof AST_Symbol && !this.unmangleable(options)) { if (!this.unmangleable(options)) base54.consider(this.name, -1);
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
}; };
if (options.properties) {
AST_Dot.prototype.add_source_map = function() {
base54.consider(this.property, -1);
};
AST_Sub.prototype.add_source_map = function() {
skip_string(this.property);
};
}
base54.consider(this.print_to_string(), 1); base54.consider(this.print_to_string(), 1);
} finally { } finally {
AST_Node.prototype.print = AST_Node.prototype._print; AST_Symbol.prototype.add_source_map = fn;
delete AST_Dot.prototype.add_source_map;
delete AST_Sub.prototype.add_source_map;
} }
base54.sort(); base54.sort();

View File

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

View File

@@ -312,9 +312,7 @@ function test_case(test) {
if (test.mangle) { if (test.mangle) {
output.compute_char_frequency(test.mangle); output.compute_char_frequency(test.mangle);
output.mangle_names(test.mangle); output.mangle_names(test.mangle);
if (test.mangle.properties) { if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties);
output = U.mangle_properties(output, test.mangle.properties);
}
} }
var output_code = make_code(output, output_options); var output_code = make_code(output, output_options);
if (expect != output_code) { if (expect != output_code) {

View File

@@ -1599,7 +1599,7 @@ collapse_vars_constants: {
} }
} }
collapse_vars_arguments: { collapse_vars_arguments_1: {
options = { options = {
booleans: true, booleans: true,
collapse_vars: true, collapse_vars: true,
@@ -1636,6 +1636,78 @@ collapse_vars_arguments: {
expect_stdout: true expect_stdout: true
} }
collapse_vars_arguments_2: {
options = {
collapse_vars: true,
}
input: {
function log(a, b) {
console.log(b);
}
function f(c) {
var d = arguments[0];
c = "FAIL";
log(c, d);
}
f();
f("PASS");
}
expect: {
function log(a, b) {
console.log(b);
}
function f(c) {
var d = arguments[0];
log(c = "FAIL", d);
}
f();
f("PASS");
}
expect_stdout: [
"undefined",
"PASS",
]
}
collapse_vars_arguments_3: {
options = {
collapse_vars: true,
}
input: {
function log(a, b) {
console.log(b);
}
function f(c) {
var args = arguments;
console.log(c);
var d = args[0];
c = "FAIL";
log(c, d);
}
f();
f("PASS");
}
expect: {
function log(a, b) {
console.log(b);
}
function f(c) {
var args = arguments;
console.log(c);
var d = args[0];
log(c = "FAIL", d);
}
f();
f("PASS");
}
expect_stdout: [
"undefined",
"undefined",
"PASS",
"PASS",
]
}
collapse_vars_short_circuit: { collapse_vars_short_circuit: {
options = { options = {
booleans: true, booleans: true,
@@ -8317,3 +8389,152 @@ issue_4012: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
global_assign: {
options = {
collapse_vars: true,
}
input: {
this.A = "FAIL";
A = "PASS";
B = "FAIL";
console.log(A);
}
expect: {
this.A = "FAIL";
A = "PASS";
B = "FAIL";
console.log(A);
}
expect_stdout: "PASS"
}
global_read: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
a = this.A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect: {
var a = 0;
a = this.A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4038: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
a = this;
a = a.A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect: {
var a = 0;
a = (a = this).A;
A = 1;
a ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4040: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var a = console.log("PASS") && a.p;
delete NaN;
}
expect: {
var a = console.log("PASS") && a.p;
delete NaN;
}
expect_stdout: "PASS"
}
issue_4047_1: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
console.log(+function(a) {
b = a;
(a >>= 0) && console.log("PASS");
}(--b + (0 !== typeof A)));
}
expect: {
var b = 1;
var a;
console.log((a = --b + ((a = 0) !== typeof A), +void ((a >>= 0) && console.log("PASS"))));
}
expect_stdout: [
"PASS",
"NaN",
]
}
issue_4047_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var b = 1;
console.log(+function(a) {
b = a;
(a >>= 0) && console.log("PASS");
}(--b + (0 !== typeof A)));
}
expect: {
var a;
console.log((a = +(0 !== typeof A), +void ((a >>= 0) && console.log("PASS"))));
}
expect_stdout: [
"PASS",
"NaN",
]
}
issue_4051: {
options = {
collapse_vars: true,
}
input: {
try {
var a = (b = b.p, "FAIL"), b = b;
} catch (e) {}
console.log(a);
}
expect: {
try {
var a = (b = b.p, "FAIL"), b = b;
} catch (e) {}
console.log(a);
}
expect_stdout: "undefined"
}

View File

@@ -123,6 +123,29 @@ self_comparison_3: {
] ]
} }
self_comparison_4: {
options = {
booleans: true,
comparisons: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {};
console.log(o == o, o != o);
console.log(o === o, o !== o);
}
expect: {
console.log(!0, !1);
console.log(!0, !1);
}
expect_stdout: [
"true false",
"true false",
]
}
issue_2857_1: { issue_2857_1: {
options = { options = {
comparisons: true, comparisons: true,

View File

@@ -1341,3 +1341,24 @@ issue_3967: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_4051: {
options = {
dead_code: true,
}
input: {
try {
delete (A = A);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
delete (A = A);
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -2833,3 +2833,39 @@ issue_3997: {
} }
expect_stdout: "string" expect_stdout: "string"
} }
issue_4035: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var a = 0;
(function() {
var b = --a;
console.log(delete (0 + b));
console.log(delete (1 * b));
console.log(delete (b + 0));
console.log(delete (b - 0));
console.log(delete (b / 1));
})();
}
expect: {
var a = 0;
(function() {
var b = --a;
console.log((0 + b, true));
console.log((1 * b, true));
console.log((0 + b, true));
console.log((b - 0, true));
console.log((b / 1, true));
})();
}
expect_stdout: [
"true",
"true",
"true",
"true",
"true",
]
}

View File

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

View File

@@ -2031,6 +2031,7 @@ issue_1670_4: {
issue_1670_5: { issue_1670_5: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
keep_fargs: false, keep_fargs: false,
@@ -2062,6 +2063,7 @@ issue_1670_5: {
issue_1670_6: { issue_1670_6: {
options = { options = {
conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
keep_fargs: false, keep_fargs: false,
@@ -7402,7 +7404,27 @@ issue_4030: {
} }
expect: { expect: {
A = "PASS"; A = "PASS";
console.log("PASS"); console.log(A);
}
expect_stdout: "PASS"
}
global_assign: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
A = "FAIL";
this.A = "PASS";
console.log(A);
}
expect: {
A = "FAIL";
this.A = "PASS";
console.log(A);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }

View File

@@ -80,3 +80,21 @@ log_global: {
} }
expect_stdout: "[object global]" expect_stdout: "[object global]"
} }
issue_4054: {
input: {
console.log({
set p(v) {
throw "FAIL";
},
});
}
expect: {
console.log({
set p(v) {
throw "FAIL";
},
});
}
expect_stdout: "{ p: [Setter] }"
}

View File

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

View File

@@ -0,0 +1,4 @@
switch (0) {
default:
default:
}

View File

@@ -330,7 +330,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with invalid syntax", function(done) { it("Should fail with invalid syntax", function(done) {
var command = uglifyjscmd + ' test/input/invalid/simple.js'; var command = uglifyjscmd + " test/input/invalid/simple.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -342,7 +342,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with correct marking of tabs", function(done) { it("Should fail with correct marking of tabs", function(done) {
var command = uglifyjscmd + ' test/input/invalid/tab.js'; var command = uglifyjscmd + " test/input/invalid/tab.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -354,7 +354,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with correct marking at start of line", function(done) { it("Should fail with correct marking at start of line", function(done) {
var command = uglifyjscmd + ' test/input/invalid/eof.js'; var command = uglifyjscmd + " test/input/invalid/eof.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -366,7 +366,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should fail with a missing loop body", function(done) { it("Should fail with a missing loop body", function(done) {
var command = uglifyjscmd + ' test/input/invalid/loop-no-body.js'; var command = uglifyjscmd + " test/input/invalid/loop-no-body.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); var lines = stderr.split(/\n/);
@@ -378,7 +378,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (5--)", function(done) { it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js'; var command = uglifyjscmd + " test/input/invalid/assign_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -392,7 +392,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (Math.random() /= 2)", function(done) { it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js'; var command = uglifyjscmd + " test/input/invalid/assign_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -406,7 +406,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (++this)", function(done) { it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js'; var command = uglifyjscmd + " test/input/invalid/assign_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -420,7 +420,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (++null)", function(done) { it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js'; var command = uglifyjscmd + " test/input/invalid/assign_4.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -434,7 +434,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (a.=)", function(done) { it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js'; var command = uglifyjscmd + " test/input/invalid/dot_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -448,7 +448,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (%.a)", function(done) { it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js'; var command = uglifyjscmd + " test/input/invalid/dot_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -462,7 +462,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (a./();)", function(done) { it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js'; var command = uglifyjscmd + " test/input/invalid/dot_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -476,7 +476,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error ({%: 1})", function(done) { it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js'; var command = uglifyjscmd + " test/input/invalid/object.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -490,7 +490,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (delete x)", function(done) { it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js'; var command = uglifyjscmd + " test/input/invalid/delete.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -504,7 +504,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (function g(arguments))", function(done) { it("Should throw syntax error (function g(arguments))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_1.js'; var command = uglifyjscmd + " test/input/invalid/function_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -518,7 +518,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (function eval())", function(done) { it("Should throw syntax error (function eval())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_2.js'; var command = uglifyjscmd + " test/input/invalid/function_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -532,7 +532,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (iife arguments())", function(done) { it("Should throw syntax error (iife arguments())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_3.js'; var command = uglifyjscmd + " test/input/invalid/function_3.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -546,7 +546,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (catch (eval))", function(done) { it("Should throw syntax error (catch (eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js'; var command = uglifyjscmd + " test/input/invalid/try.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -560,7 +560,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (var eval)", function(done) { it("Should throw syntax error (var eval)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/var.js'; var command = uglifyjscmd + " test/input/invalid/var.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -574,7 +574,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (else)", function(done) { it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js'; var command = uglifyjscmd + " test/input/invalid/else.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -588,7 +588,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (return)", function(done) { it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js'; var command = uglifyjscmd + " test/input/invalid/return.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -602,7 +602,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (for-in init)", function(done) { it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js'; var command = uglifyjscmd + " test/input/invalid/for-in_1.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -616,7 +616,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should throw syntax error (for-in var)", function(done) { it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js'; var command = uglifyjscmd + " test/input/invalid/for-in_2.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
@@ -629,6 +629,18 @@ describe("bin/uglifyjs", function() {
done(); done();
}); });
}); });
it("Should throw syntax error (switch defaults)", function(done) {
var command = uglifyjscmd + " test/input/invalid/switch.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/switch.js:3,2");
assert.strictEqual(lines[1], " default:");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: More than one default clause in switch statement");
done();
});
});
it("Should handle literal string as source map input", function(done) { it("Should handle literal string as source map input", function(done) {
var command = [ var command = [
uglifyjscmd, uglifyjscmd,

View File

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

49
test/ufuzz/actions.js Normal file
View File

@@ -0,0 +1,49 @@
require("../../tools/exit");
var get = require("https").get;
var parse = require("url").parse;
var base = process.argv[2];
var token = process.argv[3];
function read(url, callback) {
var options = parse(url);
options.headers = {
"Authorization": "Token " + token,
"User-Agent": "UglifyJS",
};
get(options, function(response) {
var chunks = [];
response.setEncoding("utf8");
response.on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
callback(JSON.parse(chunks.join("")));
});
});
}
var queued = 0, total = 0, earliest, now = Date.now();
process.on("beforeExit", function() {
if (queued > 3) {
process.stdout.write("0");
} else if (now - earliest > 0 && total > 1) {
process.stdout.write(Math.min(20 * (now - earliest) / (total - 1), 6300000).toFixed(0));
} else {
process.stdout.write("3600000");
}
});
read(base + "/actions/workflows/ufuzz.yml/runs?event=schedule", function(reply) {
reply.workflow_runs.filter(function(workflow) {
return /^(in_progress|queued|)$/.test(workflow.status);
}).forEach(function(workflow) {
read(workflow.jobs_url, function(reply) {
reply.jobs.forEach(function(job) {
if (job.status == "queued") queued++;
total++;
if (!job.started_at) return;
var start = new Date(job.started_at);
if (!(earliest < start)) earliest = start;
});
});
});
});

View File

@@ -1175,7 +1175,8 @@ function log(options) {
} }
function sort_globals(code) { function sort_globals(code) {
return "var " + sandbox.run_code("throw Object.keys(this).sort();" + code).join(",") + ";" + code; var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
return globals.length ? "var " + globals.join(",") + ";" + code : code;
} }
function fuzzy_match(original, uglified) { function fuzzy_match(original, uglified) {

456
tools/domprops.html Normal file
View File

@@ -0,0 +1,456 @@
<!doctype html>
<html>
<body>
<script>
!function(G) {
var domprops = [];
var objs = [ G ];
var tagNames = [
"a",
"abbr",
"acronym",
"address",
"applet",
"area",
"article",
"aside",
"audio",
"b",
"base",
"basefont",
"bdi",
"bdo",
"bgsound",
"big",
"blink",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"center",
"checked",
"cite",
"code",
"col",
"colgroup",
"command",
"comment",
"compact",
"content",
"data",
"datalist",
"dd",
"declare",
"defer",
"del",
"details",
"dfn",
"dialog",
"dir",
"disabled",
"div",
"dl",
"dt",
"element",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"font",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"image",
"img",
"input",
"ins",
"isindex",
"ismap",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"listing",
"main",
"map",
"mark",
"marquee",
"math",
"menu",
"menuitem",
"meta",
"meter",
"multicol",
"multiple",
"nav",
"nextid",
"nobr",
"noembed",
"noframes",
"nohref",
"noresize",
"noscript",
"noshade",
"nowrap",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"plaintext",
"pre",
"progress",
"q",
"rb",
"readonly",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"selected",
"shadow",
"slot",
"small",
"source",
"spacer",
"span",
"strike",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"tt",
"u",
"ul",
"var",
"video",
"wbr",
"xmp",
"XXX",
];
for (var n = 0; n < tagNames.length; n++) {
add(document.createElement(tagNames[n]));
}
var nsNames = {
"http://www.w3.org/1998/Math/MathML": [
"annotation",
"annotation-xml",
"maction",
"maligngroup",
"malignmark",
"math",
"menclose",
"merror",
"mfenced",
"mfrac",
"mglyph",
"mi",
"mlabeledtr",
"mlongdiv",
"mmultiscripts",
"mn",
"mo",
"mover",
"mpadded",
"mphantom",
"mprescripts",
"mroot",
"mrow",
"ms",
"mscarries",
"mscarry",
"msgroup",
"msline",
"mspace",
"msqrt",
"msrow",
"mstack",
"mstyle",
"msub",
"msubsup",
"msup",
"mtable",
"mtd",
"mtext",
"mtr",
"munder",
"munderover",
"none",
"semantics",
],
"http://www.w3.org/2000/svg": [
"a",
"altGlyph",
"altGlyphDef",
"altGlyphItem",
"animate",
"animateColor",
"animateMotion",
"animateTransform",
"circle",
"clipPath",
"color-profile",
"cursor",
"defs",
"desc",
"discard",
"ellipse",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"filter",
"font",
"font-face",
"font-face-format",
"font-face-name",
"font-face-src",
"font-face-uri",
"foreignObject",
"g",
"glyph",
"glyphRef",
"hatch",
"hatchpath",
"hkern",
"image",
"line",
"linearGradient",
"marker",
"mask",
"mesh",
"meshgradient",
"meshpatch",
"meshrow",
"metadata",
"missing-glyph",
"mpath",
"path",
"pattern",
"polygon",
"polyline",
"radialGradient",
"rect",
"script",
"set",
"solidcolor",
"stop",
"style",
"svg",
"switch",
"symbol",
"text",
"textPath",
"title",
"tref",
"tspan",
"unknown",
"use",
"view",
"vkern",
],
};
if (document.createElementNS) for (var ns in nsNames) {
for (var n = 0; n < nsNames[ns].length; n++) {
add(document.createElementNS(ns, nsNames[ns][n]));
}
}
var skips = [
G.alert,
G.back,
G.blur,
G.captureEvents,
G.clearImmediate,
G.clearInterval,
G.clearTimeout,
G.close,
G.confirm,
G.console,
G.dump,
G.fetch,
G.find,
G.focus,
G.forward,
G.getAttention,
G.history,
G.home,
G.location,
G.moveBy,
G.moveTo,
G.navigator,
G.open,
G.openDialog,
G.print,
G.process,
G.prompt,
G.resizeBy,
G.resizeTo,
G.setImmediate,
G.setInterval,
G.setTimeout,
G.showModalDialog,
G.sizeToContent,
G.stop,
];
var types = [];
var interfaces = [
"beforeunloadevent",
"compositionevent",
"customevent",
"devicemotionevent",
"deviceorientationevent",
"dragevent",
"event",
"events",
"focusevent",
"hashchangeevent",
"htmlevents",
"keyboardevent",
"messageevent",
"mouseevent",
"mouseevents",
"storageevent",
"svgevents",
"textevent",
"touchevent",
"uievent",
"uievents",
];
var i = 0, full = false;
var addEvent = document.createEvent ? function(type) {
if (~indexOf(types, type)) return;
types.push(type);
for (var j = 0; j < interfaces.length; j++) try {
var event = document.createEvent(interfaces[j]);
event.initEvent(type, true, true);
add(event);
} catch (e) {}
} : function() {};
var scanProperties = Object.getOwnPropertyNames ? function(o, fn) {
var names = Object.getOwnPropertyNames(o);
names.forEach(fn);
for (var k in o) if (!~indexOf(names, k)) fn(k);
} : function(o, fn) {
for (var k in o) fn(k);
};
setTimeout(function next() {
for (var j = 10; --j >= 0 && i < objs.length; i++) {
var o = objs[i];
var skip = ~indexOf(skips, o);
try {
scanProperties(o, function(k) {
if (!~indexOf(domprops, k)) domprops.push(k);
if (/^on/.test(k)) addEvent(k.slice(2));
if (!full) try {
add(o[k]);
} catch (e) {}
});
} catch (e) {}
if (skip || full) continue;
try {
add(o.__proto__);
} catch (e) {}
try {
add(o.prototype);
} catch (e) {}
try {
add(new o());
} catch (e) {}
try {
add(o());
} catch (e) {}
}
if (!full && objs.length > 20000) {
alert(objs.length);
full = true;
}
if (i < objs.length) {
setTimeout(next, 0);
} else {
document.write('<pre>[\n "' + domprops.sort().join('",\n "').replace(/&/g, "&amp;").replace(/</g, "&lt;") + '"\n]</pre>');
}
}, 0);
function add(o) {
if (o) switch (typeof o) {
case "function":
case "object":
if (!~indexOf(objs, o)) objs.push(o);
}
}
function indexOf(list, value) {
var j = list.length;
while (--j >= 0) {
if (list[j] === value) break;
}
return j;
}
}(function() {
return this;
}());
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

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