refactor throw usage within compress (#2193)
Eliminate exceptional constructs from normal control flow.
This commit is contained in:
207
lib/compress.js
207
lib/compress.js
@@ -1198,21 +1198,21 @@ merge(Compressor.prototype, {
|
||||
for (var i = 0, len = statements.length; i < len; i++) {
|
||||
var stat = statements[i];
|
||||
if (prev) {
|
||||
if (stat instanceof AST_For) {
|
||||
try {
|
||||
prev.body.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_Binary && node.operator == "in")
|
||||
throw cons_seq;
|
||||
}));
|
||||
if (stat.init && !(stat.init instanceof AST_Definitions)) {
|
||||
stat.init = cons_seq(stat.init);
|
||||
if (stat instanceof AST_For && !(stat.init instanceof AST_Definitions)) {
|
||||
var abort = false;
|
||||
prev.body.walk(new TreeWalker(function(node) {
|
||||
if (abort) return true;
|
||||
if (node instanceof AST_Binary && node.operator == "in") {
|
||||
abort = true;
|
||||
return true;
|
||||
}
|
||||
else if (!stat.init) {
|
||||
}));
|
||||
if (!abort) {
|
||||
if (stat.init) stat.init = cons_seq(stat.init);
|
||||
else {
|
||||
stat.init = prev.body.drop_side_effect_free(compressor);
|
||||
n--;
|
||||
}
|
||||
} catch(ex) {
|
||||
if (ex !== cons_seq) throw ex;
|
||||
}
|
||||
}
|
||||
else if (stat instanceof AST_If) {
|
||||
@@ -1532,13 +1532,8 @@ merge(Compressor.prototype, {
|
||||
// descendant of AST_Node.
|
||||
AST_Node.DEFMETHOD("evaluate", function(compressor){
|
||||
if (!compressor.option("evaluate")) return this;
|
||||
try {
|
||||
var val = this._eval(compressor);
|
||||
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
|
||||
} catch(ex) {
|
||||
if (ex !== def) throw ex;
|
||||
return this;
|
||||
}
|
||||
var val = this._eval(compressor);
|
||||
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
|
||||
});
|
||||
var unaryPrefix = makePredicate("! ~ - + void");
|
||||
AST_Node.DEFMETHOD("is_constant", function(){
|
||||
@@ -1584,27 +1579,28 @@ merge(Compressor.prototype, {
|
||||
def(AST_Statement, function(){
|
||||
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
|
||||
});
|
||||
def(AST_Lambda, function(){
|
||||
throw def;
|
||||
});
|
||||
def(AST_Lambda, return_this);
|
||||
function ev(node, compressor) {
|
||||
if (!compressor) throw new Error("Compressor must be passed");
|
||||
|
||||
return node._eval(compressor);
|
||||
};
|
||||
def(AST_Node, function(){
|
||||
throw def; // not constant
|
||||
});
|
||||
def(AST_Node, return_this);
|
||||
def(AST_Constant, function(){
|
||||
return this.getValue();
|
||||
});
|
||||
def(AST_Array, function(compressor){
|
||||
if (compressor.option("unsafe")) {
|
||||
return this.elements.map(function(element) {
|
||||
return ev(element, compressor);
|
||||
});
|
||||
var elements = [];
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
var element = this.elements[i];
|
||||
var value = ev(element, compressor);
|
||||
if (element === value) return this;
|
||||
elements.push(value);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
throw def;
|
||||
return this;
|
||||
});
|
||||
def(AST_Object, function(compressor){
|
||||
if (compressor.option("unsafe")) {
|
||||
@@ -1616,105 +1612,121 @@ merge(Compressor.prototype, {
|
||||
key = key.name;
|
||||
} else if (key instanceof AST_Node) {
|
||||
key = ev(key, compressor);
|
||||
if (key === prop.key) return this;
|
||||
}
|
||||
if (typeof Object.prototype[key] === 'function') {
|
||||
throw def;
|
||||
return this;
|
||||
}
|
||||
val[key] = ev(prop.value, compressor);
|
||||
if (val[key] === prop.value) return this;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
throw def;
|
||||
return this;
|
||||
});
|
||||
def(AST_UnaryPrefix, function(compressor){
|
||||
var e = this.expression;
|
||||
// Function would be evaluated to an array and so typeof would
|
||||
// incorrectly return 'object'. Hence making is a special case.
|
||||
if (this.operator == "typeof" && this.expression instanceof AST_Function) {
|
||||
return typeof function(){};
|
||||
}
|
||||
var e = ev(this.expression, compressor);
|
||||
if (e === this.expression) return this;
|
||||
switch (this.operator) {
|
||||
case "!": return !ev(e, compressor);
|
||||
case "!": return !e;
|
||||
case "typeof":
|
||||
// Function would be evaluated to an array and so typeof would
|
||||
// incorrectly return 'object'. Hence making is a special case.
|
||||
if (e instanceof AST_Function) return typeof function(){};
|
||||
|
||||
e = ev(e, compressor);
|
||||
|
||||
// typeof <RegExp> returns "object" or "function" on different platforms
|
||||
// so cannot evaluate reliably
|
||||
if (e instanceof RegExp) throw def;
|
||||
|
||||
if (e instanceof RegExp) return this;
|
||||
return typeof e;
|
||||
case "void": return void ev(e, compressor);
|
||||
case "~": return ~ev(e, compressor);
|
||||
case "-": return -ev(e, compressor);
|
||||
case "+": return +ev(e, compressor);
|
||||
case "void": return void e;
|
||||
case "~": return ~e;
|
||||
case "-": return -e;
|
||||
case "+": return +e;
|
||||
}
|
||||
throw def;
|
||||
return this;
|
||||
});
|
||||
def(AST_Binary, function(c){
|
||||
var left = this.left, right = this.right, result;
|
||||
def(AST_Binary, function(compressor){
|
||||
var left = ev(this.left, compressor);
|
||||
if (left === this.left) return this;
|
||||
var right = ev(this.right, compressor);
|
||||
if (right === this.right) return this;
|
||||
var result;
|
||||
switch (this.operator) {
|
||||
case "&&" : result = ev(left, c) && ev(right, c); break;
|
||||
case "||" : result = ev(left, c) || ev(right, c); break;
|
||||
case "|" : result = ev(left, c) | ev(right, c); break;
|
||||
case "&" : result = ev(left, c) & ev(right, c); break;
|
||||
case "^" : result = ev(left, c) ^ ev(right, c); break;
|
||||
case "+" : result = ev(left, c) + ev(right, c); break;
|
||||
case "*" : result = ev(left, c) * ev(right, c); break;
|
||||
case "/" : result = ev(left, c) / ev(right, c); break;
|
||||
case "%" : result = ev(left, c) % ev(right, c); break;
|
||||
case "-" : result = ev(left, c) - ev(right, c); break;
|
||||
case "<<" : result = ev(left, c) << ev(right, c); break;
|
||||
case ">>" : result = ev(left, c) >> ev(right, c); break;
|
||||
case ">>>" : result = ev(left, c) >>> ev(right, c); break;
|
||||
case "==" : result = ev(left, c) == ev(right, c); break;
|
||||
case "===" : result = ev(left, c) === ev(right, c); break;
|
||||
case "!=" : result = ev(left, c) != ev(right, c); break;
|
||||
case "!==" : result = ev(left, c) !== ev(right, c); break;
|
||||
case "<" : result = ev(left, c) < ev(right, c); break;
|
||||
case "<=" : result = ev(left, c) <= ev(right, c); break;
|
||||
case ">" : result = ev(left, c) > ev(right, c); break;
|
||||
case ">=" : result = ev(left, c) >= ev(right, c); break;
|
||||
case "&&" : result = left && right; break;
|
||||
case "||" : result = left || right; break;
|
||||
case "|" : result = left | right; break;
|
||||
case "&" : result = left & right; break;
|
||||
case "^" : result = left ^ right; break;
|
||||
case "+" : result = left + right; break;
|
||||
case "*" : result = left * right; break;
|
||||
case "/" : result = left / right; break;
|
||||
case "%" : result = left % right; break;
|
||||
case "-" : result = left - right; break;
|
||||
case "<<" : result = left << right; break;
|
||||
case ">>" : result = left >> right; break;
|
||||
case ">>>" : result = left >>> right; break;
|
||||
case "==" : result = left == right; break;
|
||||
case "===" : result = left === right; break;
|
||||
case "!=" : result = left != right; break;
|
||||
case "!==" : result = left !== right; break;
|
||||
case "<" : result = left < right; break;
|
||||
case "<=" : result = left <= right; break;
|
||||
case ">" : result = left > right; break;
|
||||
case ">=" : result = left >= right; break;
|
||||
default:
|
||||
throw def;
|
||||
return this;
|
||||
}
|
||||
if (isNaN(result) && c.find_parent(AST_With)) {
|
||||
if (isNaN(result) && compressor.find_parent(AST_With)) {
|
||||
// leave original expression as is
|
||||
throw def;
|
||||
return this;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
def(AST_Conditional, function(compressor){
|
||||
return ev(this.condition, compressor)
|
||||
? ev(this.consequent, compressor)
|
||||
: ev(this.alternative, compressor);
|
||||
var condition = ev(this.condition, compressor);
|
||||
if (condition === this.condition) return this;
|
||||
var node = condition ? this.consequent : this.alternative;
|
||||
var value = ev(node, compressor);
|
||||
return value === node ? this : value;
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor){
|
||||
if (!compressor.option("reduce_vars") || this._evaluating) throw def;
|
||||
this._evaluating = true;
|
||||
try {
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) throw def;
|
||||
var value = ev(fixed, compressor);
|
||||
if (!HOP(fixed, "_eval")) fixed._eval = function() {
|
||||
return value;
|
||||
};
|
||||
if (value && typeof value == "object" && this.definition().escaped) throw def;
|
||||
return value;
|
||||
} finally {
|
||||
this._evaluating = false;
|
||||
if (!compressor.option("reduce_vars")) return this;
|
||||
this._eval = return_this;
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) {
|
||||
delete this._eval;
|
||||
return this;
|
||||
}
|
||||
var value = ev(fixed, compressor);
|
||||
if (value === fixed) {
|
||||
delete this._eval;
|
||||
return this;
|
||||
}
|
||||
if (!HOP(fixed, "_eval")) fixed._eval = function() {
|
||||
return value;
|
||||
};
|
||||
if (value && typeof value == "object" && this.definition().escaped) {
|
||||
delete this._eval;
|
||||
return this;
|
||||
}
|
||||
this._eval = fixed._eval;
|
||||
return value;
|
||||
});
|
||||
def(AST_PropAccess, function(compressor){
|
||||
if (compressor.option("unsafe")) {
|
||||
var key = this.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = ev(key, compressor);
|
||||
if (key === this.property) return this;
|
||||
}
|
||||
var val = ev(this.expression, compressor);
|
||||
if (val === this.expression) return this;
|
||||
if (val && HOP(val, key)) {
|
||||
return val[key];
|
||||
}
|
||||
}
|
||||
throw def;
|
||||
return this;
|
||||
});
|
||||
var object_fns = [
|
||||
'constructor',
|
||||
@@ -1760,19 +1772,24 @@ merge(Compressor.prototype, {
|
||||
var key = exp.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = ev(key, compressor);
|
||||
if (key === exp.property) return this;
|
||||
}
|
||||
var val = ev(exp.expression, compressor);
|
||||
if (val === exp.expression) return this;
|
||||
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
|
||||
return val[key].apply(val, this.args.map(function(arg) {
|
||||
return ev(arg, compressor);
|
||||
}));
|
||||
var args = [];
|
||||
for (var i = 0, len = this.args.length; i < len; i++) {
|
||||
var arg = this.args[i];
|
||||
var value = ev(arg, compressor);
|
||||
if (arg === value) return this;
|
||||
args.push(value);
|
||||
}
|
||||
return val[key].apply(val, args);
|
||||
}
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
def(AST_New, function(compressor){
|
||||
throw def;
|
||||
return this;
|
||||
});
|
||||
def(AST_New, return_this);
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("_eval", func);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user