diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2017-01-26 19:14:18 +0800 |
---|---|---|
committer | Richard van Velzen <rvanvelzen1@gmail.com> | 2017-01-26 12:14:18 +0100 |
commit | 0d7d4918eb6fb73c3cf9863479b3e66d38fad6df (patch) | |
tree | aba146adb5371e19935493b132fc605abbe081ca /lib | |
parent | 48284844a461e6113bb9911cdcdad7ab8a3d85de (diff) | |
download | tracifyjs-0d7d4918eb6fb73c3cf9863479b3e66d38fad6df.tar.gz tracifyjs-0d7d4918eb6fb73c3cf9863479b3e66d38fad6df.zip |
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
Diffstat (limited to 'lib')
-rw-r--r-- | lib/compress.js | 66 | ||||
-rw-r--r-- | lib/scope.js | 17 |
2 files changed, 71 insertions, 12 deletions
diff --git a/lib/compress.js b/lib/compress.js index bbd3659d..5c019623 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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){ diff --git a/lib/scope.js b/lib/scope.js index bc5db90d..ae792a0a 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -184,6 +184,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ var func = null; var globals = self.globals = new Dictionary(); var tw = new TreeWalker(function(node, descend){ + function isModified(node, level) { + var parent = tw.parent(level); + if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--") + || parent instanceof AST_Assign && parent.left === node + || parent instanceof AST_Call && parent.expression === node) { + return true; + } else if (parent instanceof AST_PropAccess && parent.expression === node) { + return isModified(parent, level + 1); + } + } + if (node instanceof AST_Lambda) { var prev_func = func; func = node; @@ -197,8 +208,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ } if (node instanceof AST_SymbolRef) { var name = node.name; - var parent = tw.parent(); - if (name == "eval" && parent instanceof AST_Call) { + if (name == "eval" && tw.parent() instanceof AST_Call) { for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { s.uses_eval = true; } @@ -220,8 +230,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ sym = g; } node.thedef = sym; - if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--") - || parent instanceof AST_Assign && parent.left === node) { + if (isModified(node, 0)) { sym.modified = true; } node.reference(); |