refactor throw usage within compress (#2193)

Eliminate exceptional constructs from normal control flow.
This commit is contained in:
Alex Lam S.L
2017-07-03 02:10:56 +08:00
committed by GitHub
parent f3a487a368
commit 20e4f8277f

View File

@@ -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);
});