From 4178289c382caf2eb3464390370dd1400a23468a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 25 Oct 2017 03:38:11 +0800 Subject: implement `hoist_props` (#2396) fixes #2377 --- lib/compress.js | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 5 deletions(-) (limited to 'lib/compress.js') 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){ -- cgit v1.2.3