improve Dictionary performance
This commit is contained in:
parent
860aa9531b
commit
e4ba7fad2a
161
lib/compress.js
161
lib/compress.js
|
|
@ -185,90 +185,89 @@ function Compressor(options, false_by_default) {
|
|||
};
|
||||
}
|
||||
|
||||
Compressor.prototype = new TreeTransformer;
|
||||
merge(Compressor.prototype, {
|
||||
option: function(key) { return this.options[key] },
|
||||
exposed: function(def) {
|
||||
if (def.exported) return true;
|
||||
if (def.undeclared) return true;
|
||||
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
|
||||
var toplevel = this.toplevel;
|
||||
return !all(def.orig, function(sym) {
|
||||
return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
|
||||
});
|
||||
},
|
||||
compress: function(node) {
|
||||
node = node.resolve_defines(this);
|
||||
node.hoist_exports(this);
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(true);
|
||||
}
|
||||
var merge_vars = this.options.merge_vars;
|
||||
var passes = +this.options.passes || 1;
|
||||
var min_count = 1 / 0;
|
||||
var stopping = false;
|
||||
var mangle = { ie: this.option("ie") };
|
||||
for (var pass = 0; pass < passes; pass++) {
|
||||
node.figure_out_scope(mangle);
|
||||
if (pass > 0 || this.option("reduce_vars"))
|
||||
node.reset_opt_flags(this);
|
||||
this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
|
||||
node = node.transform(this);
|
||||
if (passes > 1) {
|
||||
var count = 0;
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
|
||||
pass: pass,
|
||||
min_count: min_count,
|
||||
count: count,
|
||||
});
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
} else if (stopping) {
|
||||
break;
|
||||
} else {
|
||||
stopping = true;
|
||||
}
|
||||
Compressor.prototype = new TreeTransformer(function(node, descend, in_list) {
|
||||
if (node._squeezed) return node;
|
||||
var is_scope = node instanceof AST_Scope;
|
||||
if (is_scope) {
|
||||
node.hoist_properties(this);
|
||||
node.hoist_declarations(this);
|
||||
node.process_boolean_returns(this);
|
||||
}
|
||||
// Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
|
||||
// would call AST_Node.transform() if a different instance of AST_Node is
|
||||
// produced after OPT().
|
||||
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
|
||||
// Migrate and defer all children's AST_Node.transform() to below, which
|
||||
// will now happen after this parent AST_Node has been properly substituted
|
||||
// thus gives a consistent AST snapshot.
|
||||
descend(node, this);
|
||||
// Existing code relies on how AST_Node.optimize() worked, and omitting the
|
||||
// following replacement call would result in degraded efficiency of both
|
||||
// output and performance.
|
||||
descend(node, this);
|
||||
var opt = node.optimize(this);
|
||||
if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
|
||||
opt.drop_unused(this);
|
||||
if (opt.merge_variables(this)) opt.drop_unused(this);
|
||||
descend(opt, this);
|
||||
}
|
||||
if (opt === node) opt._squeezed = true;
|
||||
return opt;
|
||||
});
|
||||
Compressor.prototype.option = function(key) {
|
||||
return this.options[key];
|
||||
};
|
||||
Compressor.prototype.exposed = function(def) {
|
||||
if (def.exported) return true;
|
||||
if (def.undeclared) return true;
|
||||
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
|
||||
var toplevel = this.toplevel;
|
||||
return !all(def.orig, function(sym) {
|
||||
return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
|
||||
});
|
||||
};
|
||||
Compressor.prototype.compress = function(node) {
|
||||
node = node.resolve_defines(this);
|
||||
node.hoist_exports(this);
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(true);
|
||||
}
|
||||
var merge_vars = this.options.merge_vars;
|
||||
var passes = +this.options.passes || 1;
|
||||
var min_count = 1 / 0;
|
||||
var stopping = false;
|
||||
var mangle = { ie: this.option("ie") };
|
||||
for (var pass = 0; pass < passes; pass++) {
|
||||
node.figure_out_scope(mangle);
|
||||
if (pass > 0 || this.option("reduce_vars"))
|
||||
node.reset_opt_flags(this);
|
||||
this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
|
||||
node = node.transform(this);
|
||||
if (passes > 1) {
|
||||
var count = 0;
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
|
||||
pass: pass,
|
||||
min_count: min_count,
|
||||
count: count,
|
||||
});
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
} else if (stopping) {
|
||||
break;
|
||||
} else {
|
||||
stopping = true;
|
||||
}
|
||||
}
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(false);
|
||||
}
|
||||
return node;
|
||||
},
|
||||
before: function(node, descend, in_list) {
|
||||
if (node._squeezed) return node;
|
||||
var is_scope = node instanceof AST_Scope;
|
||||
if (is_scope) {
|
||||
node.hoist_properties(this);
|
||||
node.hoist_declarations(this);
|
||||
node.process_boolean_returns(this);
|
||||
}
|
||||
// Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
|
||||
// would call AST_Node.transform() if a different instance of AST_Node is
|
||||
// produced after OPT().
|
||||
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
|
||||
// Migrate and defer all children's AST_Node.transform() to below, which
|
||||
// will now happen after this parent AST_Node has been properly substituted
|
||||
// thus gives a consistent AST snapshot.
|
||||
descend(node, this);
|
||||
// Existing code relies on how AST_Node.optimize() worked, and omitting the
|
||||
// following replacement call would result in degraded efficiency of both
|
||||
// output and performance.
|
||||
descend(node, this);
|
||||
var opt = node.optimize(this);
|
||||
if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
|
||||
opt.drop_unused(this);
|
||||
if (opt.merge_variables(this)) opt.drop_unused(this);
|
||||
descend(opt, this);
|
||||
}
|
||||
if (opt === node) opt._squeezed = true;
|
||||
return opt;
|
||||
}
|
||||
});
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(false);
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
(function(OPT) {
|
||||
OPT(AST_Node, function(self, compressor) {
|
||||
|
|
|
|||
74
lib/utils.js
74
lib/utils.js
|
|
@ -96,15 +96,6 @@ function defaults(args, defs, croak) {
|
|||
return defs;
|
||||
}
|
||||
|
||||
function merge(obj, ext) {
|
||||
var count = 0;
|
||||
for (var i in ext) if (HOP(ext, i)) {
|
||||
obj[i] = ext[i];
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
function return_false() { return false; }
|
||||
function return_true() { return true; }
|
||||
|
|
@ -171,63 +162,80 @@ function all(array, predicate) {
|
|||
}
|
||||
|
||||
function Dictionary() {
|
||||
this._values = Object.create(null);
|
||||
this._size = 0;
|
||||
this.values = Object.create(null);
|
||||
}
|
||||
Dictionary.prototype = {
|
||||
set: function(key, val) {
|
||||
if (!this.has(key)) ++this._size;
|
||||
this._values["$" + key] = val;
|
||||
if (key == "__proto__") {
|
||||
this.proto_value = val;
|
||||
} else {
|
||||
this.values[key] = val;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
add: function(key, val) {
|
||||
if (this.has(key)) {
|
||||
this.get(key).push(val);
|
||||
var list = this.get(key);
|
||||
if (list) {
|
||||
list.push(val);
|
||||
} else {
|
||||
this.set(key, [ val ]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
get: function(key) { return this._values["$" + key] },
|
||||
get: function(key) {
|
||||
return key == "__proto__" ? this.proto_value : this.values[key];
|
||||
},
|
||||
del: function(key) {
|
||||
if (this.has(key)) {
|
||||
--this._size;
|
||||
delete this._values["$" + key];
|
||||
if (key == "__proto__") {
|
||||
delete this.proto_value;
|
||||
} else {
|
||||
delete this.values[key];
|
||||
}
|
||||
return this;
|
||||
},
|
||||
has: function(key) { return ("$" + key) in this._values },
|
||||
has: function(key) {
|
||||
return key == "__proto__" ? "proto_value" in this : key in this.values;
|
||||
},
|
||||
all: function(predicate) {
|
||||
for (var i in this._values)
|
||||
if (!predicate(this._values[i], i.substr(1)))
|
||||
return false;
|
||||
for (var i in this.values)
|
||||
if (!predicate(this.values[i], i)) return false;
|
||||
if ("proto_value" in this && !predicate(this.proto_value, "__proto__")) return false;
|
||||
return true;
|
||||
},
|
||||
each: function(f) {
|
||||
for (var i in this._values)
|
||||
f(this._values[i], i.substr(1));
|
||||
for (var i in this.values)
|
||||
f(this.values[i], i);
|
||||
if ("proto_value" in this) f(this.proto_value, "__proto__");
|
||||
},
|
||||
size: function() {
|
||||
return this._size;
|
||||
return Object.keys(this.values).length + ("proto_value" in this);
|
||||
},
|
||||
map: function(f) {
|
||||
var ret = [];
|
||||
for (var i in this._values)
|
||||
ret.push(f(this._values[i], i.substr(1)));
|
||||
for (var i in this.values)
|
||||
ret.push(f(this.values[i], i));
|
||||
if ("proto_value" in this) ret.push(f(this.proto_value, "__proto__"));
|
||||
return ret;
|
||||
},
|
||||
clone: function() {
|
||||
var ret = new Dictionary();
|
||||
for (var i in this._values)
|
||||
ret._values[i] = this._values[i];
|
||||
ret._size = this._size;
|
||||
this.each(function(value, i) {
|
||||
ret.set(i, value);
|
||||
});
|
||||
return ret;
|
||||
},
|
||||
toObject: function() { return this._values }
|
||||
toObject: function() {
|
||||
var obj = {};
|
||||
this.each(function(value, i) {
|
||||
obj["$" + i] = value;
|
||||
});
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
Dictionary.fromObject = function(obj) {
|
||||
var dict = new Dictionary();
|
||||
dict._size = merge(dict._values, obj);
|
||||
for (var i in obj)
|
||||
if (HOP(obj, i)) dict.set(i.slice(1), obj[i]);
|
||||
return dict;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -198,9 +198,9 @@ numeric_literal: {
|
|||
expect_exact: [
|
||||
'var obj = {',
|
||||
' 0: 0,',
|
||||
' "-0": 1,',
|
||||
' 42: 3,',
|
||||
' 37: 4,',
|
||||
' 42: 3,',
|
||||
' "-0": 1,',
|
||||
' o: 5,',
|
||||
' 1e42: 8,',
|
||||
' b: 7',
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user