augment evaluate to extract within objects (#1425)

- gated by `unsafe`
- replaces previous optimisation specific to String.length
- "123"[0] => 1
- [1, 2, 3][0] => 1
- [1, 2, 3].length => 3
- does not apply to objects with overridden prototype functions
This commit is contained in:
Alex Lam S.L
2017-01-26 19:14:18 +08:00
committed by Richard van Velzen
parent 48284844a4
commit 0d7d4918eb
6 changed files with 729 additions and 14 deletions

View File

@@ -152,6 +152,12 @@ merge(Compressor.prototype, {
AST_Node.DEFMETHOD("clear_opt_flags", function(){
this.walk(new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
var d = node.definition();
if (d && d.init) {
delete d.init._evaluated;
}
}
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
node._squeezed = false;
node._optimized = false;
@@ -992,13 +998,20 @@ merge(Compressor.prototype, {
// constant; otherwise it's the original or a replacement node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return [ this ];
var val;
try {
var val = this._eval(compressor);
return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
val = this._eval(compressor);
} catch(ex) {
if (ex !== def) throw ex;
return [ this ];
}
var node;
try {
node = make_node_from_constant(compressor, val, this);
} catch(ex) {
return [ this ];
}
return [ best_of(node, this), val ];
});
AST_Node.DEFMETHOD("is_constant", function(compressor){
// Accomodate when compress option evaluate=false
@@ -1047,6 +1060,32 @@ merge(Compressor.prototype, {
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);
});
}
throw def;
});
def(AST_Object, function(compressor){
if (compressor.option("unsafe")) {
var val = {};
for (var i = 0, len = this.properties.length; i < len; i++) {
var prop = this.properties[i];
var key = prop.key;
if (key instanceof AST_Node) {
key = ev(key, compressor);
}
if (typeof Object.prototype[key] === 'function') {
throw def;
}
val[key] = ev(prop.value, compressor);
}
return val;
}
throw def;
});
def(AST_UnaryPrefix, function(compressor){
var e = this.expression;
switch (this.operator) {
@@ -1114,6 +1153,12 @@ merge(Compressor.prototype, {
try {
var d = this.definition();
if (d && (d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) {
if (compressor.option("unsafe")) {
if (!HOP(d.init, '_evaluated')) {
d.init._evaluated = ev(d.init, compressor);
}
return d.init._evaluated;
}
return ev(d.init, compressor);
}
} finally {
@@ -1121,11 +1166,16 @@ merge(Compressor.prototype, {
}
throw def;
});
def(AST_Dot, function(compressor){
if (compressor.option("unsafe") && this.property == "length") {
var str = ev(this.expression, compressor);
if (typeof str == "string")
return str.length;
def(AST_PropAccess, function(compressor){
if (compressor.option("unsafe")) {
var key = this.property;
if (key instanceof AST_Node) {
key = ev(key, compressor);
}
var val = ev(this.expression, compressor);
if (val && HOP(val, key)) {
return val[key];
}
}
throw def;
});
@@ -2890,7 +2940,7 @@ merge(Compressor.prototype, {
});
}
}
return self;
return self.evaluate(compressor)[0];
});
OPT(AST_Dot, function(self, compressor){