aboutsummaryrefslogtreecommitdiff
path: root/lib/compress.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compress.js')
-rw-r--r--lib/compress.js82
1 files changed, 77 insertions, 5 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 670a3b0d..9f410718 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -60,6 +60,7 @@ function Compressor(options, false_by_default) {
expression : false,
global_defs : {},
hoist_funs : !false_by_default,
+ hoist_props : false,
hoist_vars : false,
ie8 : false,
if_return : !false_by_default,
@@ -190,6 +191,7 @@ merge(Compressor.prototype, {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
+ node = node.hoist_properties(this);
node = node.hoist_declarations(this);
was_scope = true;
}
@@ -547,6 +549,7 @@ merge(Compressor.prototype, {
}
function reset_def(def) {
+ def.direct_access = false;
def.escaped = false;
if (def.scope.uses_eval) {
def.fixed = false;
@@ -604,15 +607,19 @@ merge(Compressor.prototype, {
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
+ return;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
mark_escaped(d, parent, parent, level + 1);
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
- mark_escaped(d, parent, read_property(value, parent.property), level + 1);
+ value = read_property(value, parent.property);
+ mark_escaped(d, parent, value, level + 1);
+ if (value) return;
}
+ if (level == 0) d.direct_access = true;
}
});
- AST_SymbolRef.DEFMETHOD("fixed_value", function() {
+ AST_Symbol.DEFMETHOD("fixed_value", function() {
var fixed = this.definition().fixed;
if (!fixed || fixed instanceof AST_Node) return fixed;
return fixed();
@@ -2478,11 +2485,11 @@ merge(Compressor.prototype, {
}));
}
switch (body.length) {
- case 0:
+ case 0:
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
- case 1:
+ case 1:
return body[0];
- default:
+ default:
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
body: body
});
@@ -2678,6 +2685,71 @@ merge(Compressor.prototype, {
return self;
});
+ AST_Scope.DEFMETHOD("hoist_properties", function(compressor){
+ var self = this;
+ if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self;
+ var defs_by_id = Object.create(null);
+ var var_names = Object.create(null);
+ self.enclosed.forEach(function(def) {
+ var_names[def.name] = true;
+ });
+ self.variables.each(function(def, name) {
+ var_names[name] = true;
+ });
+ return self.transform(new TreeTransformer(function(node) {
+ if (node instanceof AST_VarDef) {
+ var sym = node.name, def, value;
+ if (sym.scope === self
+ && !(def = sym.definition()).escaped
+ && !def.single_use
+ && !def.direct_access
+ && (value = sym.fixed_value()) === node.value
+ && value instanceof AST_Object) {
+ var defs = new Dictionary();
+ var assignments = [];
+ value.properties.forEach(function(prop) {
+ assignments.push(make_node(AST_VarDef, node, {
+ name: make_sym(prop.key),
+ value: prop.value
+ }));
+ });
+ defs_by_id[def.id] = defs;
+ return MAP.splice(assignments);
+ }
+ }
+ if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
+ var defs = defs_by_id[node.expression.definition().id];
+ if (defs) {
+ var key = node.property;
+ if (key instanceof AST_Node) key = key.getValue();
+ var def = defs.get(key);
+ var sym = make_node(AST_SymbolRef, node, {
+ name: def.name,
+ scope: node.expression.scope,
+ thedef: def
+ });
+ sym.reference({});
+ return sym;
+ }
+ }
+
+ function make_sym(key) {
+ var prefix = sym.name + "_" + key.toString().replace(/[^a-z_$]+/ig, "_");
+ var name = prefix;
+ for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
+ var new_var = make_node(sym.CTOR, sym, {
+ name: name,
+ scope: self
+ });
+ var def = self.def_variable(new_var);
+ defs.set(key, def);
+ self.enclosed.push(def);
+ var_names[name] = true;
+ return new_var;
+ }
+ }));
+ });
+
// drop_side_effect_free()
// remove side-effect-free parts which only affects return value
(function(def){