Compare commits

..

15 Commits

Author SHA1 Message Date
Alex Lam S.L
604caa09e7 v3.3.12 2018-02-22 08:14:29 +00:00
Alex Lam S.L
29a71d3aae more tests for #2938 (#2940) 2018-02-21 04:19:42 +08:00
Alex Lam S.L
39a907bde3 workaround pure_getters=true when dropping unused assignments (#2939)
fixes #2938
2018-02-20 17:38:40 +08:00
Alex Lam S.L
70474310f3 improve unsafe evaluate of function (#2936)
Miscellaneous
- simplify `run_code()` hack
2018-02-19 18:47:02 +08:00
Alex Lam S.L
b5f0f4f3a1 reduce false positives from object literals (#2935) 2018-02-19 06:21:07 +08:00
Alex Lam S.L
2905fd625a reduce false positives from labels (#2934) 2018-02-19 03:55:33 +08:00
Alex Lam S.L
4facd94029 reduce false positives from noop (#2933) 2018-02-19 01:15:05 +08:00
Alex Lam S.L
4b5993ff15 fix crash in may_throw() (#2932)
fixes #2931
2018-02-18 21:51:27 +08:00
Alex Lam S.L
2351a672ea fix dead_code on exceptional return (#2930)
fixes #2929
2018-02-18 04:36:00 +08:00
Alex Lam S.L
4a528c469c reduce false positives from function.toString() (#2928) 2018-02-18 02:13:26 +08:00
Alex Lam S.L
82d1ef0242 fix unsafe evaluate of function property (#2927)
fixes #2926
2018-02-17 21:33:36 +08:00
Alex Lam S.L
7fdd2082a6 drop unused "class" definition IIFEs (#2923)
fixes #805
2018-02-17 05:11:31 +08:00
Alex Lam S.L
e529f54e90 reduce function-related false positives (#2925) 2018-02-17 04:35:03 +08:00
Alex Lam S.L
d626e9bf19 improve inline efficiency (#2924) 2018-02-17 02:37:13 +08:00
Alex Lam S.L
a2a9459684 fix unsafe evaluate of AST_Function (#2920)
fixes #2919
2018-02-16 17:21:46 +08:00
11 changed files with 413 additions and 64 deletions

View File

@@ -2004,7 +2004,7 @@ merge(Compressor.prototype, {
if (this.properties[i].value instanceof AST_Accessor) return true;
return false;
});
def(AST_Function, return_false);
def(AST_Lambda, return_false);
def(AST_UnaryPostfix, return_false);
def(AST_UnaryPrefix, function() {
return this.operator == "void";
@@ -2023,8 +2023,9 @@ merge(Compressor.prototype, {
})
def(AST_Dot, function(compressor) {
if (!is_strict(compressor)) return false;
if (this.expression instanceof AST_Function && this.property == "prototype") return false;
return true;
var exp = this.expression;
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
return !(exp instanceof AST_Lambda && this.property == "prototype");
});
def(AST_Sequence, function(compressor) {
return this.tail_node()._dot_throw(compressor);
@@ -2231,6 +2232,7 @@ merge(Compressor.prototype, {
"slice",
].concat(object_fns),
Boolean: object_fns,
Function: object_fns,
Number: [
"toExponential",
"toFixed",
@@ -2313,7 +2315,9 @@ merge(Compressor.prototype, {
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this;
var val = this._eval(compressor, 1);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
if (!val || val instanceof RegExp) return val;
if (typeof val == "function" || typeof val == "object") return this;
return val;
});
var unaryPrefix = makePredicate("! ~ - + void");
AST_Node.DEFMETHOD("is_constant", function(){
@@ -2335,15 +2339,22 @@ merge(Compressor.prototype, {
def(AST_Constant, function(){
return this.getValue();
});
def(AST_Function, function(compressor) {
if (compressor.option("unsafe")) {
var fn = function() {};
fn.node = this;
fn.toString = function() {
return "function(){}";
};
return fn;
}
return this;
});
def(AST_Array, function(compressor, depth) {
if (compressor.option("unsafe")) {
var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) {
var element = this.elements[i];
if (element instanceof AST_Function) {
elements.push(element);
continue;
}
var value = element._eval(compressor, depth);
if (element === value) return this;
elements.push(value);
@@ -2513,6 +2524,14 @@ merge(Compressor.prototype, {
} else {
val = exp._eval(compressor, depth + 1);
if (!val || val === exp || !HOP(val, key)) return this;
if (typeof val == "function") switch (key) {
case "name":
return val.node.name ? val.node.name.name : "";
case "length":
return val.node.argnames.length;
default:
return this;
}
}
return val[key];
}
@@ -2844,6 +2863,9 @@ merge(Compressor.prototype, {
def(AST_ObjectProperty, function(compressor){
return this.value.may_throw(compressor);
});
def(AST_Return, function(compressor){
return this.value && this.value.may_throw(compressor);
});
def(AST_Sequence, function(compressor){
return any(this.expressions, compressor);
});
@@ -2863,8 +2885,7 @@ merge(Compressor.prototype, {
return !this.is_declared(compressor);
});
def(AST_Try, function(compressor){
return any(this.body, compressor)
|| this.bcatch && this.bcatch.may_throw(compressor)
return this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)
|| this.bfinally && this.bfinally.may_throw(compressor);
});
def(AST_Unary, function(compressor){
@@ -3000,11 +3021,20 @@ merge(Compressor.prototype, {
if (self.uses_eval || self.uses_with) return;
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) {
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
var sym;
if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) {
return node.left;
sym = node.left;
} else if (node instanceof AST_Unary && node.write_only) {
sym = node.expression;
}
if (node instanceof AST_Unary && node.write_only) return node.expression;
if (/strict/.test(compressor.option("pure_getters"))) {
while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
if (sym instanceof AST_Sub) props.unshift(sym.property);
sym = sym.expression;
}
}
return sym;
};
var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
@@ -3080,17 +3110,26 @@ merge(Compressor.prototype, {
function before(node, descend, in_list) {
var parent = tt.parent();
if (drop_vars) {
var sym = assign_as_unused(node);
var props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef) {
var def = sym.definition();
var in_use = def.id in in_use_ids;
var value = null;
if (node instanceof AST_Assign) {
if (!in_use || def.id in fixed_ids && fixed_ids[def.id] !== node) {
return maintain_this_binding(parent, node, node.right.transform(tt));
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
value = node.right;
}
} else if (!in_use) return make_node(AST_Number, node, {
value: 0
});
} else if (!in_use) {
value = make_node(AST_Number, node, {
value: 0
});
}
if (value) {
props.push(value);
return maintain_this_binding(parent, node, make_sequence(node, props.map(function(prop) {
return prop.transform(tt);
})));
}
}
}
if (scope !== self) return;
@@ -3268,12 +3307,15 @@ merge(Compressor.prototype, {
self.transform(tt);
function scan_ref_scoped(node, descend) {
var node_def, sym = assign_as_unused(node);
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef
&& self.variables.get(sym.name) === (node_def = sym.definition())) {
props.forEach(function(prop) {
prop.walk(tw);
});
if (node instanceof AST_Assign) {
node.right.walk(tw);
if (!node_def.chained && node.left.fixed_value() === node.right) {
if (node.left === sym && !node_def.chained && sym.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
@@ -4478,6 +4520,8 @@ merge(Compressor.prototype, {
})) {
return false;
}
} else if (line instanceof AST_EmptyStatement) {
continue;
} else if (stat) {
return false;
} else {
@@ -5471,7 +5515,7 @@ merge(Compressor.prototype, {
node = parent;
parent = compressor.parent(level++);
if (parent instanceof AST_Exit) {
if (in_try(level, parent instanceof AST_Throw)) break;
if (in_try(level, parent)) break;
if (is_reachable(def.scope, [ def ])) break;
if (self.operator == "=") return self.right;
def.fixed = false;
@@ -5505,13 +5549,17 @@ merge(Compressor.prototype, {
}
return self;
function in_try(level, no_catch) {
function in_try(level, node) {
var right = self.right;
self.right = make_node(AST_Null, right);
var may_throw = node.may_throw(compressor);
self.right = right;
var scope = self.left.definition().scope;
var parent;
while ((parent = compressor.parent(level++)) !== scope) {
if (parent instanceof AST_Try) {
if (parent.bfinally) return true;
if (no_catch && parent.bcatch) return true;
if (may_throw && parent.bcatch) return true;
}
}
}

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.3.11",
"version": "3.3.12",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -4619,3 +4619,49 @@ issue_2914_2: {
}
expect_stdout: "0"
}
issue_805: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
function f() {
function Foo(){}
Foo.prototype = {};
Foo.prototype.bar = 42;
return Foo;
}
}
expect: {
function f() {
function Foo(){}
(Foo.prototype = {}).bar = 42;
return Foo;
}
}
}
issue_2931: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function() {
var a = function() {
return;
}();
return a;
}());
}
expect: {
console.log(function() {
return function() {
return;
}();
}());
}
expect_stdout: "undefined"
}

View File

@@ -917,3 +917,28 @@ issue_2860_2: {
}
expect_stdout: "1"
}
issue_2929: {
options = {
dead_code: true,
}
input: {
console.log(function(a) {
try {
return null.p = a = 1;
} catch (e) {
return a ? "PASS" : "FAIL";
}
}());
}
expect: {
console.log(function(a) {
try {
return null.p = a = 1;
} catch (e) {
return a ? "PASS" : "FAIL";
}
}());
}
expect_stdout: "PASS"
}

View File

@@ -1719,3 +1719,69 @@ issue_2846: {
}
expect_stdout: "0"
}
issue_805_1: {
options = {
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(a) {
var unused = function() {};
unused.prototype[a()] = 42;
(unused.prototype.bar = function() {
console.log("bar");
})();
return unused;
})(function() {
console.log("foo");
return "foo";
});
}
expect: {
console.log("foo"),
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
}
issue_805_2: {
options = {
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(a) {
function unused() {}
unused.prototype[a()] = 42;
(unused.prototype.bar = function() {
console.log("bar");
})();
return unused;
})(function() {
console.log("foo");
return "foo";
});
}
expect: {
console.log("foo"),
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
}

View File

@@ -1489,3 +1489,49 @@ issue_2916_2: {
}
expect_stdout: "PASS"
}
issue_2919: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([ function() {} ].toString());
}
expect: {
console.log("function(){}");
}
}
issue_2926_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function f(a) {
console.log(f.name.length, f.length);
})();
}
expect: {
(function f(a) {
console.log(1, 1);
})();
}
expect_stdout: "1 1"
}
issue_2926_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(typeof function() {}.valueOf());
}
expect: {
console.log("function");
}
expect_stdout: "function"
}

View File

@@ -292,11 +292,12 @@ issue_2084: {
}
expect: {
var c = 0;
!function(c) {
c = 1 + c,
!function() {
var c;
c = 1 + (c = -1),
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(-1),
}(),
console.log(c);
}
expect_stdout: "0"
@@ -1051,11 +1052,9 @@ issue_2616: {
}
expect: {
var c = "FAIL";
(function() {
!function(NaN) {
(true << NaN) - 0/0 || (c = "PASS");
}([]);
})();
!function(NaN) {
(true << NaN) - 0/0 || (c = "PASS");
}([]);
console.log(c);
}
expect_stdout: "PASS"
@@ -1086,13 +1085,11 @@ issue_2620_1: {
}
expect: {
var c = "FAIL";
(function() {
(function(a) {
if (function(a) {
a && a();
}(), a) c = "PASS";
})(1);
})(),
!function(a) {
if (function(a) {
a && a();
}(), a) c = "PASS";
}(1),
console.log(c);
}
expect_stdout: "PASS"
@@ -1160,18 +1157,16 @@ issue_2620_3: {
}
expect: {
var c = "FAIL";
(function() {
(function(a, NaN) {
(function() {
switch (a) {
case a:
break;
case c = "PASS", NaN:
break;
}
})();
})(NaN);
})();
!function(a, NaN) {
(function() {
switch (a) {
case a:
break;
case c = "PASS", NaN:
break;
}
})();
}(NaN);
console.log(c);
}
expect_stdout: "PASS"
@@ -1341,11 +1336,9 @@ issue_2630_4: {
}
expect: {
var x = 3, a = 1, b = 2;
(function() {
(function() {
while (--x >= 0 && void (b += ++a));
})();
})();
!function() {
while (--x >= 0 && void (b += ++a));
}();
console.log(a);
}
expect_stdout: "2"

View File

@@ -721,3 +721,112 @@ issue_2838: {
}
expect_stdout: "PASS"
}
issue_2938_1: {
options = {
pure_getters: true,
unused: true,
}
input: {
function f(a) {
a.b = "PASS";
}
var o = {};
f(o);
console.log(o.b);
}
expect: {
function f(a) {
a.b = "PASS";
}
var o = {};
f(o);
console.log(o.b);
}
expect_stdout: "PASS"
}
issue_2938_2: {
options = {
pure_getters: true,
toplevel: true,
unused: true,
}
input: {
var Parser = function Parser() {};
var p = Parser.prototype;
p.initialContext = function initialContext() {
console.log("PASS");
};
p.braceIsBlock = function() {};
(new Parser).initialContext();
}
expect: {
var Parser = function() {};
var p = Parser.prototype;
p.initialContext = function() {
console.log("PASS");
};
p.braceIsBlock = function() {};
(new Parser).initialContext();
}
expect_stdout: "PASS"
}
issue_2938_3: {
options = {
pure_getters: true,
side_effects: true,
unused: true,
}
input: {
function f(a) {
var unused = a.a;
a.b = "PASS";
a.c;
}
var o = {};
o.d;
f(o);
console.log(o.b);
}
expect: {
function f(a) {
a.b = "PASS";
}
var o = {};
f(o);
console.log(o.b);
}
expect_stdout: "PASS"
}
issue_2938_4: {
options = {
pure_getters: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var Parser = function Parser() {};
var p = Parser.prototype;
var unused = p.x;
p.initialContext = function initialContext() {
p.y;
console.log("PASS");
};
p.braceIsBlock = function() {};
(new Parser).initialContext();
}
expect: {
var Parser = function() {};
var p = Parser.prototype;
p.initialContext = function() {
console.log("PASS");
};
p.braceIsBlock = function() {};
(new Parser).initialContext();
}
expect_stdout: "PASS"
}

View File

@@ -5527,3 +5527,21 @@ issue_2869: {
}
expect_stdout: "PASS"
}
issue_2919: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var arr = [ function() {} ];
console.log(typeof arr[0]);
}
expect: {
console.log("function");
}
expect_stdout: "function"
}

View File

@@ -226,7 +226,7 @@ describe("minify", function() {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {

View File

@@ -24,13 +24,12 @@ function strip_func_ids(text) {
var FUNC_TOSTRING = [
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
" f.valueOf = Function.prototype.valueOf;",
"});",
"Function.prototype.toString = Function.prototype.valueOf = function() {",
"Function.prototype.toString = function() {",
" var id = 100000;",
" return function() {",
" var n = this.name;",
' if (!/^F[0-9]{6}N$/.test(n)) {',
" if (!/^F[0-9]{6}N$/.test(n)) {",
' n = "F" + ++id + "N";',
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
@@ -40,10 +39,9 @@ var FUNC_TOSTRING = [
" });",
] : [], [
" }",
' return "[Function: " + n + "]";',
" }",
' return "function(){}";',
" };",
"}();",
'Object.defineProperty(Function.prototype, "valueOf", { enumerable: false });',
]).join("\n");
exports.run_code = function(code) {
var stdout = "";