From 8b71c6559b0e1773bb3455c68701ff512fc18277 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 19 Jan 2016 13:12:32 -0600 Subject: Mark vars with /** @const */ pragma as consts so they can be eliminated. Fixes older browser support for consts and allows more flexibility in dead code removal. --- lib/scope.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/scope.js b/lib/scope.js index 5e93020f..22fb150d 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -154,7 +154,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { var def = defun.def_variable(node); - def.constant = node instanceof AST_SymbolConst; + def.constant = node instanceof AST_SymbolConst || node.has_const_pragma(); def.init = tw.parent().value; } else if (node instanceof AST_SymbolCatch) { @@ -357,6 +357,16 @@ AST_Symbol.DEFMETHOD("global", function(){ return this.definition().global; }); +AST_Symbol.DEFMETHOD("has_const_pragma", function() { + var token = this.scope.body[0] && this.scope.body[0].start; + var comments = token && token.comments_before; + if (comments && comments.length > 0) { + var last = comments[comments.length - 1]; + return /@const/.test(last.value); + } + return false; +}) + AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ return defaults(options, { except : [], -- cgit v1.2.3 From f97da4294a7e9adbd560ecafd94ec697de35affc Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 20 Jan 2016 10:52:48 -0600 Subject: Use TreeWalker for more accurate @const results and update tests --- lib/scope.js | 32 +++++++++++++++++++++++------- test/compress/dead-code.js | 49 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/scope.js b/lib/scope.js index 22fb150d..144ae48e 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -358,13 +358,31 @@ AST_Symbol.DEFMETHOD("global", function(){ }); AST_Symbol.DEFMETHOD("has_const_pragma", function() { - var token = this.scope.body[0] && this.scope.body[0].start; - var comments = token && token.comments_before; - if (comments && comments.length > 0) { - var last = comments[comments.length - 1]; - return /@const/.test(last.value); - } - return false; + var symbol = this; + var symbol_has_pragma = false; + var pragma_found = false; + var found_symbol = false; + // Walk the current scope, looking for a comment with the @const pragma. + // If it exists, mark a bool that will remain true only for the next iteration. + // If the next iteration is this symbol, then we return true. + // Otherwise we stop descending and get out of here. + var tw = new TreeWalker(function(node, descend){ + // This is our symbol. Was the pragma before this? + if (node.name === symbol) { + found_symbol = true; + symbol_has_pragma = pragma_found; + } + + // Look for the /** @const */ pragma + var comments_before = node.start && node.start.comments_before; + var lastComment = comments_before && comments_before[comments_before.length - 1]; + pragma_found = lastComment && /@const/.test(lastComment.value); + + // no need to descend after finding our node + return found_symbol; + }); + this.scope.walk(tw); + return symbol_has_pragma; }) AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index f79b04de..8aad336c 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -97,6 +97,7 @@ dead_code_const_declaration: { evaluate : true }; input: { + var unused; const CONST_FOO = false; if (CONST_FOO) { console.log("unreachable"); @@ -105,6 +106,7 @@ dead_code_const_declaration: { } } expect: { + var unused; const CONST_FOO = !1; var moo; function bar() {} @@ -120,7 +122,8 @@ dead_code_const_annotation: { evaluate : true }; input: { - /** @const*/ var CONST_FOO_ANN = false; + var unused; + /** @const */ var CONST_FOO_ANN = false; if (CONST_FOO_ANN) { console.log("unreachable"); var moo; @@ -128,8 +131,52 @@ dead_code_const_annotation: { } } expect: { + var unused; var CONST_FOO_ANN = !1; var moo; function bar() {} } } + +dead_code_const_annotation_complex_scope: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true + }; + input: { + var unused_var; + /** @const */ var test = 'test'; + /** @const */ var CONST_FOO_ANN = false; + var unused_var_2; + if (CONST_FOO_ANN) { + console.log("unreachable"); + var moo; + function bar() {} + } + if (test === 'test') { + var beef = 'good'; + /** @const */ var meat = 'beef'; + var pork = 'bad'; + if (meat === 'pork') { + console.log('also unreachable'); + } else if (pork === 'good') { + console.log('reached, not const'); + } + } + } + expect: { + var unused_var; + var test = 'test'; + var CONST_FOO_ANN = !1; + var unused_var_2; + var moo; + function bar() {} + var beef = 'good'; + var meat = 'beef'; + var pork = 'bad'; + 'good' === pork && console.log('reached, not const'); + } +} -- cgit v1.2.3 From 4a7179ff9183cd27d036043c0bbcb01d1604d824 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 20 Jan 2016 11:03:41 -0600 Subject: Simplify by skipping extra tree walk. --- lib/scope.js | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/scope.js b/lib/scope.js index 144ae48e..794254d4 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -94,6 +94,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ var scope = self.parent_scope = null; var labels = new Dictionary(); var defun = null; + var last_var_had_const_pragma = false; var nesting = 0; var tw = new TreeWalker(function(node, descend){ if (options.screw_ie8 && node instanceof AST_Catch) { @@ -151,10 +152,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ // later. (node.scope = defun.parent_scope).def_function(node); } + else if (node instanceof AST_Var) { + last_var_had_const_pragma = node.has_const_pragma(); + } else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { var def = defun.def_variable(node); - def.constant = node instanceof AST_SymbolConst || node.has_const_pragma(); + def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma; def.init = tw.parent().value; } else if (node instanceof AST_SymbolCatch) { @@ -357,33 +361,11 @@ AST_Symbol.DEFMETHOD("global", function(){ return this.definition().global; }); -AST_Symbol.DEFMETHOD("has_const_pragma", function() { - var symbol = this; - var symbol_has_pragma = false; - var pragma_found = false; - var found_symbol = false; - // Walk the current scope, looking for a comment with the @const pragma. - // If it exists, mark a bool that will remain true only for the next iteration. - // If the next iteration is this symbol, then we return true. - // Otherwise we stop descending and get out of here. - var tw = new TreeWalker(function(node, descend){ - // This is our symbol. Was the pragma before this? - if (node.name === symbol) { - found_symbol = true; - symbol_has_pragma = pragma_found; - } - - // Look for the /** @const */ pragma - var comments_before = node.start && node.start.comments_before; - var lastComment = comments_before && comments_before[comments_before.length - 1]; - pragma_found = lastComment && /@const/.test(lastComment.value); - - // no need to descend after finding our node - return found_symbol; - }); - this.scope.walk(tw); - return symbol_has_pragma; -}) +AST_Var.DEFMETHOD("has_const_pragma", function() { + var comments_before = this.start && this.start.comments_before; + var lastComment = comments_before && comments_before[comments_before.length - 1]; + return lastComment && /@const/.test(lastComment.value); +}); AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ return defaults(options, { -- cgit v1.2.3 From 1b703349cf824020c4dc64a58aa6d0dc3b809cea Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 20 Jan 2016 11:35:45 -0600 Subject: Tighten up @const regex. --- lib/scope.js | 2 +- test/compress/dead-code.js | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/scope.js b/lib/scope.js index 794254d4..4cea5176 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -364,7 +364,7 @@ AST_Symbol.DEFMETHOD("global", function(){ AST_Var.DEFMETHOD("has_const_pragma", function() { var comments_before = this.start && this.start.comments_before; var lastComment = comments_before && comments_before[comments_before.length - 1]; - return lastComment && /@const/.test(lastComment.value); + return lastComment && /@const\b/.test(lastComment.value); }); AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 8aad336c..fa4b37d6 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -138,6 +138,29 @@ dead_code_const_annotation: { } } +dead_code_const_annotation_regex: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true + }; + input: { + var unused; + // @constraint this shouldn't be a constant + var CONST_FOO_ANN = false; + if (CONST_FOO_ANN) { + console.log("reachable"); + } + } + expect: { + var unused; + var CONST_FOO_ANN = !1; + CONST_FOO_ANN && console.log('reachable'); + } +} + dead_code_const_annotation_complex_scope: { options = { dead_code : true, @@ -149,7 +172,8 @@ dead_code_const_annotation_complex_scope: { input: { var unused_var; /** @const */ var test = 'test'; - /** @const */ var CONST_FOO_ANN = false; + // @const + var CONST_FOO_ANN = false; var unused_var_2; if (CONST_FOO_ANN) { console.log("unreachable"); -- cgit v1.2.3