improve unsafe comparisons (#3200)

This commit is contained in:
Alex Lam S.L
2018-06-28 03:46:19 +08:00
committed by GitHub
parent 88c8f4e363
commit 957d5537a8
4 changed files with 128 additions and 45 deletions

View File

@@ -2264,29 +2264,35 @@ merge(Compressor.prototype, {
// methods to determine whether an expression has a boolean result type // methods to determine whether an expression has a boolean result type
(function(def) { (function(def) {
var unary_bool = makePredicate("! delete");
var binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
def(AST_Node, return_false); def(AST_Node, return_false);
def(AST_Assign, function(compressor) {
return this.operator == "=" && this.right.is_boolean(compressor);
});
var binary = makePredicate("in instanceof == != === !== < <= >= >");
def(AST_Binary, function(compressor) {
return binary[this.operator] || lazy_op[this.operator]
&& this.left.is_boolean(compressor)
&& this.right.is_boolean(compressor);
});
def(AST_Boolean, return_true);
var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
def(AST_Call, function(compressor) {
if (!compressor.option("unsafe")) return false;
var exp = this.expression;
return exp instanceof AST_Dot && (fn[exp.property]
|| exp.property == "test" && exp.expression instanceof AST_RegExp);
});
def(AST_Conditional, function(compressor) {
return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
});
def(AST_New, return_false);
def(AST_Sequence, function(compressor) {
return this.tail_node().is_boolean(compressor);
});
var unary = makePredicate("! delete");
def(AST_UnaryPrefix, function() { def(AST_UnaryPrefix, function() {
return unary_bool[this.operator]; return unary[this.operator];
}); });
def(AST_Binary, function() {
return binary_bool[this.operator]
|| lazy_op[this.operator]
&& this.left.is_boolean()
&& this.right.is_boolean();
});
def(AST_Conditional, function() {
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function() {
return this.operator == "=" && this.right.is_boolean();
});
def(AST_Sequence, function() {
return this.tail_node().is_boolean();
});
def(AST_True, return_true);
def(AST_False, return_true);
})(function(node, func) { })(function(node, func) {
node.DEFMETHOD("is_boolean", func); node.DEFMETHOD("is_boolean", func);
}); });
@@ -2294,27 +2300,80 @@ merge(Compressor.prototype, {
// methods to determine if an expression has a numeric result type // methods to determine if an expression has a numeric result type
(function(def) { (function(def) {
def(AST_Node, return_false); def(AST_Node, return_false);
def(AST_Number, return_true);
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function() {
return unary[this.operator];
});
var binary = makePredicate("- * / % & | ^ << >> >>>"); var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Assign, function(compressor) {
return binary[this.operator.slice(0, -1)]
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
return binary[this.operator] || this.operator == "+" return binary[this.operator] || this.operator == "+"
&& this.left.is_number(compressor) && this.left.is_number(compressor)
&& this.right.is_number(compressor); && this.right.is_number(compressor);
}); });
def(AST_Assign, function(compressor) { var fn = makePredicate([
return binary[this.operator.slice(0, -1)] "charCodeAt",
|| this.operator == "=" && this.right.is_number(compressor); "getDate",
}); "getDay",
def(AST_Sequence, function(compressor) { "getFullYear",
return this.tail_node().is_number(compressor); "getHours",
"getMilliseconds",
"getMinutes",
"getMonth",
"getSeconds",
"getTime",
"getTimezoneOffset",
"getUTCDate",
"getUTCDay",
"getUTCFullYear",
"getUTCHours",
"getUTCMilliseconds",
"getUTCMinutes",
"getUTCMonth",
"getUTCSeconds",
"getYear",
"indexOf",
"lastIndexOf",
"localeCompare",
"push",
"search",
"setDate",
"setFullYear",
"setHours",
"setMilliseconds",
"setMinutes",
"setMonth",
"setSeconds",
"setTime",
"setUTCDate",
"setUTCFullYear",
"setUTCHours",
"setUTCMilliseconds",
"setUTCMinutes",
"setUTCMonth",
"setUTCSeconds",
"setYear",
"toExponential",
"toFixed",
"toPrecision",
]);
def(AST_Call, function(compressor) {
if (!compressor.option("unsafe")) return false;
var exp = this.expression;
return exp instanceof AST_Dot && (fn[exp.property]
|| is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
}); });
def(AST_Conditional, function(compressor) { def(AST_Conditional, function(compressor) {
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor); return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
}); });
def(AST_New, return_false);
def(AST_Number, return_true);
def(AST_Sequence, function(compressor) {
return this.tail_node().is_number(compressor);
});
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function() {
return unary[this.operator];
});
})(function(node, func) { })(function(node, func) {
node.DEFMETHOD("is_number", func); node.DEFMETHOD("is_number", func);
}); });
@@ -2902,7 +2961,7 @@ merge(Compressor.prototype, {
var map; var map;
if (expr instanceof AST_Array) { if (expr instanceof AST_Array) {
map = native_fns.Array; map = native_fns.Array;
} else if (expr.is_boolean()) { } else if (expr.is_boolean(compressor)) {
map = native_fns.Boolean; map = native_fns.Boolean;
} else if (expr.is_number(compressor)) { } else if (expr.is_number(compressor)) {
map = native_fns.Number; map = native_fns.Number;
@@ -5247,7 +5306,7 @@ merge(Compressor.prototype, {
var is_strict_comparison = true; var is_strict_comparison = true;
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) || if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
(self.left.is_number(compressor) && self.right.is_number(compressor)) || (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
(self.left.is_boolean() && self.right.is_boolean()) || (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
self.left.equivalent_to(self.right)) { self.left.equivalent_to(self.right)) {
self.operator = self.operator.substr(0, 2); self.operator = self.operator.substr(0, 2);
} }
@@ -5328,7 +5387,7 @@ merge(Compressor.prototype, {
]).optimize(compressor); ]).optimize(compressor);
} }
} }
if (compressor.option("comparisons") && self.is_boolean()) { if (compressor.option("comparisons") && self.is_boolean(compressor)) {
if (!(compressor.parent() instanceof AST_Binary) if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) { || compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, { var negated = make_node(AST_UnaryPrefix, self, {
@@ -6102,7 +6161,7 @@ merge(Compressor.prototype, {
return self; return self;
function booleanize(node) { function booleanize(node) {
if (node.is_boolean()) return node; if (node.is_boolean(compressor)) return node;
// !!expression // !!expression
return make_node(AST_UnaryPrefix, node, { return make_node(AST_UnaryPrefix, node, {
operator: "!", operator: "!",

View File

@@ -295,3 +295,31 @@ issue_2857_6: {
} }
expect_stdout: "true" expect_stdout: "true"
} }
is_boolean_unsafe: {
options = {
comparisons: true,
unsafe: true,
}
input: {
console.log(/foo/.test("bar") === [].isPrototypeOf({}));
}
expect: {
console.log(/foo/.test("bar") == [].isPrototypeOf({}));
}
expect_stdout: "true"
}
is_number_unsafe: {
options = {
comparisons: true,
unsafe: true,
}
input: {
console.log(Math.acos(42) !== "foo".charCodeAt(4));
}
expect: {
console.log(Math.acos(42) != "foo".charCodeAt(4));
}
expect_stdout: "true"
}

View File

@@ -79,7 +79,7 @@ exports.run_code = function(code, reuse) {
return ex; return ex;
} finally { } finally {
process.stdout.write = original_write; process.stdout.write = original_write;
if (!reuse || /prototype/.test(code)) { if (!reuse || code.indexOf(".prototype") >= 0) {
context = null; context = null;
} else for (var key in context) { } else for (var key in context) {
delete context[key]; delete context[key];

View File

@@ -1,7 +1,6 @@
var fs = require("fs"); var fs = require("fs");
var UglifyJS = exports; exports.FILES = [
var FILES = UglifyJS.FILES = [
"../lib/utils.js", "../lib/utils.js",
"../lib/ast.js", "../lib/ast.js",
"../lib/parse.js", "../lib/parse.js",
@@ -19,15 +18,12 @@ var FILES = UglifyJS.FILES = [
}); });
new Function("MOZ_SourceMap", "exports", function() { new Function("MOZ_SourceMap", "exports", function() {
var code = FILES.map(function(file) { var code = exports.FILES.map(function(file) {
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}); });
code.push("exports.describe_ast = " + describe_ast.toString()); code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n"); return code.join("\n\n");
}())( }())(require("source-map"), exports);
require("source-map"),
UglifyJS
);
function describe_ast() { function describe_ast() {
var out = OutputStream({ beautify: true }); var out = OutputStream({ beautify: true });
@@ -65,11 +61,11 @@ function describe_ast() {
} }
function infer_options(options) { function infer_options(options) {
var result = UglifyJS.minify("", options); var result = exports.minify("", options);
return result.error && result.error.defs; return result.error && result.error.defs;
} }
UglifyJS.default_options = function() { exports.default_options = function() {
var defs = {}; var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) { Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {}; var options = {};