aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/compress.js121
-rw-r--r--test/compress/comparing.js28
-rw-r--r--test/sandbox.js2
-rw-r--r--tools/node.js14
4 files changed, 124 insertions, 41 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 0268e07f..e347169e 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -2264,29 +2264,35 @@ merge(Compressor.prototype, {
// methods to determine whether an expression has a boolean result type
(function(def) {
- var unary_bool = makePredicate("! delete");
- var binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
def(AST_Node, return_false);
- def(AST_UnaryPrefix, function() {
- return unary_bool[this.operator];
+ def(AST_Assign, function(compressor) {
+ return this.operator == "=" && this.right.is_boolean(compressor);
});
- def(AST_Binary, function() {
- return binary_bool[this.operator]
- || lazy_op[this.operator]
- && this.left.is_boolean()
- && this.right.is_boolean();
+ 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_Conditional, function() {
- return this.consequent.is_boolean() && this.alternative.is_boolean();
+ 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_Assign, function() {
- return this.operator == "=" && this.right.is_boolean();
+ def(AST_Conditional, function(compressor) {
+ return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
});
- def(AST_Sequence, function() {
- return this.tail_node().is_boolean();
+ 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() {
+ return unary[this.operator];
});
- def(AST_True, return_true);
- def(AST_False, return_true);
})(function(node, func) {
node.DEFMETHOD("is_boolean", func);
});
@@ -2294,26 +2300,79 @@ merge(Compressor.prototype, {
// methods to determine if an expression has a numeric result type
(function(def) {
def(AST_Node, return_false);
- def(AST_Number, return_true);
- var unary = makePredicate("+ - ~ ++ --");
- def(AST_Unary, function() {
- return unary[this.operator];
- });
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) {
return binary[this.operator] || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
- def(AST_Assign, function(compressor) {
- return binary[this.operator.slice(0, -1)]
- || this.operator == "=" && this.right.is_number(compressor);
+ var fn = makePredicate([
+ "charCodeAt",
+ "getDate",
+ "getDay",
+ "getFullYear",
+ "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) {
+ 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);
});
- def(AST_Conditional, function(compressor) {
- return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
+ var unary = makePredicate("+ - ~ ++ --");
+ def(AST_Unary, function() {
+ return unary[this.operator];
});
})(function(node, func) {
node.DEFMETHOD("is_number", func);
@@ -2902,7 +2961,7 @@ merge(Compressor.prototype, {
var map;
if (expr instanceof AST_Array) {
map = native_fns.Array;
- } else if (expr.is_boolean()) {
+ } else if (expr.is_boolean(compressor)) {
map = native_fns.Boolean;
} else if (expr.is_number(compressor)) {
map = native_fns.Number;
@@ -5247,7 +5306,7 @@ merge(Compressor.prototype, {
var is_strict_comparison = true;
if ((self.left.is_string(compressor) && self.right.is_string(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.operator = self.operator.substr(0, 2);
}
@@ -5328,7 +5387,7 @@ merge(Compressor.prototype, {
]).optimize(compressor);
}
}
- if (compressor.option("comparisons") && self.is_boolean()) {
+ if (compressor.option("comparisons") && self.is_boolean(compressor)) {
if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, {
@@ -6102,7 +6161,7 @@ merge(Compressor.prototype, {
return self;
function booleanize(node) {
- if (node.is_boolean()) return node;
+ if (node.is_boolean(compressor)) return node;
// !!expression
return make_node(AST_UnaryPrefix, node, {
operator: "!",
diff --git a/test/compress/comparing.js b/test/compress/comparing.js
index 17b3ac47..1ff29565 100644
--- a/test/compress/comparing.js
+++ b/test/compress/comparing.js
@@ -295,3 +295,31 @@ issue_2857_6: {
}
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"
+}
diff --git a/test/sandbox.js b/test/sandbox.js
index ecf95d31..e85ebd79 100644
--- a/test/sandbox.js
+++ b/test/sandbox.js
@@ -79,7 +79,7 @@ exports.run_code = function(code, reuse) {
return ex;
} finally {
process.stdout.write = original_write;
- if (!reuse || /prototype/.test(code)) {
+ if (!reuse || code.indexOf(".prototype") >= 0) {
context = null;
} else for (var key in context) {
delete context[key];
diff --git a/tools/node.js b/tools/node.js
index 2e8f2d5c..3e37092c 100644
--- a/tools/node.js
+++ b/tools/node.js
@@ -1,7 +1,6 @@
var fs = require("fs");
-var UglifyJS = exports;
-var FILES = UglifyJS.FILES = [
+exports.FILES = [
"../lib/utils.js",
"../lib/ast.js",
"../lib/parse.js",
@@ -19,15 +18,12 @@ var FILES = UglifyJS.FILES = [
});
new Function("MOZ_SourceMap", "exports", function() {
- var code = FILES.map(function(file) {
+ var code = exports.FILES.map(function(file) {
return fs.readFileSync(file, "utf8");
});
code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n");
-}())(
- require("source-map"),
- UglifyJS
-);
+}())(require("source-map"), exports);
function describe_ast() {
var out = OutputStream({ beautify: true });
@@ -65,11 +61,11 @@ function describe_ast() {
}
function infer_options(options) {
- var result = UglifyJS.minify("", options);
+ var result = exports.minify("", options);
return result.error && result.error.defs;
}
-UglifyJS.default_options = function() {
+exports.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {};