From 3ee13cae02174e565ca40462d7b15c4974265871 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 23 Apr 2020 23:50:53 +0100 Subject: [PATCH] improve `compress` (#3814) - avoid identifier overflow through consecutive API calls - simplify `reduce_vars` - enhance `unsafe` `evaluate` --- lib/compress.js | 17 +++++++---------- lib/scope.js | 29 +++++++++++++++++------------ test/compress/reduce_vars.js | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 7c2077b7..9420d8d2 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -460,7 +460,7 @@ merge(Compressor.prototype, { return def.fixed instanceof AST_Defun; } - function safe_to_assign(tw, def, scope, value) { + function safe_to_assign(tw, def, value) { if (def.fixed === undefined) return true; if (def.fixed === null && def.safe_ids) { def.safe_ids[def.id] = false; @@ -471,11 +471,8 @@ merge(Compressor.prototype, { if (!safe_to_read(tw, def)) return false; if (def.fixed === false) return false; if (def.fixed != null && (!value || def.references.length > def.assignments)) return false; - if (def.fixed instanceof AST_Defun) { - return value instanceof AST_Node && def.fixed.parent_scope === scope; - } return all(def.orig, function(sym) { - return !(sym instanceof AST_SymbolDefun || sym instanceof AST_SymbolLambda); + return !(sym instanceof AST_SymbolLambda); }); } @@ -557,7 +554,7 @@ merge(Compressor.prototype, { } if (sym.fixed) delete sym.fixed; var d = sym.definition(); - var safe = safe_to_assign(tw, d, sym.scope, node.right); + var safe = safe_to_assign(tw, d, node.right); d.assignments++; var fixed = d.fixed; if (!fixed && node.operator != "=") return; @@ -820,7 +817,7 @@ merge(Compressor.prototype, { } if (exp.fixed) delete exp.fixed; var d = exp.definition(); - var safe = safe_to_assign(tw, d, exp.scope, true); + var safe = safe_to_assign(tw, d, true); d.assignments++; var fixed = d.fixed; if (!fixed) return; @@ -846,7 +843,7 @@ merge(Compressor.prototype, { var node = this; var d = node.name.definition(); if (node.value) { - if (safe_to_assign(tw, d, node.name.scope, node.value)) { + if (safe_to_assign(tw, d, node.value)) { d.fixed = function() { return node.value; }; @@ -3195,7 +3192,7 @@ merge(Compressor.prototype, { def(AST_Statement, function() { throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); }); - def(AST_Lambda, return_this); + def(AST_Accessor, return_this); def(AST_Node, return_this); def(AST_Constant, function() { return this.value; @@ -3213,7 +3210,7 @@ merge(Compressor.prototype, { var value = node._eval(compressor, ignore_side_effects, cached, depth); return value === node ? this : value; }); - def(AST_Function, function(compressor) { + def(AST_Lambda, function(compressor) { if (compressor.option("unsafe")) { var fn = function() {}; fn.node = this; diff --git a/lib/scope.js b/lib/scope.js index ad33c5e0..adbfe4ac 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -43,23 +43,21 @@ "use strict"; -function SymbolDef(scope, orig, init) { +function SymbolDef(id, scope, orig, init) { + this.eliminated = 0; + this.global = false; + this.id = id; + this.init = init; + this.lambda = orig instanceof AST_SymbolLambda; + this.mangled_name = null; this.name = orig.name; this.orig = [ orig ]; - this.init = init; - this.eliminated = 0; - this.scope = scope; this.references = []; this.replaced = 0; - this.global = false; - this.mangled_name = null; + this.scope = scope; this.undeclared = false; - this.id = SymbolDef.next_id++; - this.lambda = orig instanceof AST_SymbolLambda; } -SymbolDef.next_id = 1; - SymbolDef.prototype = { unmangleable: function(options) { return this.global && !options.toplevel @@ -151,6 +149,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { scope.def_variable(node).defun = defun; } }); + self.next_def_id = 0; self.walk(tw); // pass 2: find back references and eval @@ -240,12 +239,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { } }); +AST_Scope.DEFMETHOD("make_def", function(orig, init) { + var top = this; + while (top.parent_scope) top = top.parent_scope; + return new SymbolDef(++top.next_def_id, this, orig, init); +}); + AST_Toplevel.DEFMETHOD("def_global", function(node) { var globals = this.globals, name = node.name; if (globals.has(name)) { return globals.get(name); } else { - var g = new SymbolDef(this, node); + var g = this.make_def(node); g.undeclared = true; g.global = true; globals.set(name, g); @@ -310,7 +315,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) { def.orig.push(symbol); if (def.init instanceof AST_Function) def.init = init; } else { - def = new SymbolDef(this, symbol, init); + def = this.make_def(symbol, init); this.variables.set(symbol.name, def); def.global = !this.parent_scope; } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 6be1171f..25108cae 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -223,6 +223,25 @@ unsafe_evaluate: { expect_stdout: true } +unsafe_evaluate_defun: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function() { + function f() {} + return ++f; + }()); + } + expect: { + console.log(NaN); + } + expect_stdout: "NaN" +} + unsafe_evaluate_side_effect_free_1: { options = { evaluate: true, -- 2.34.1