@@ -642,151 +642,134 @@ merge(Compressor.prototype, {
642
642
// and if it has exactly one reference then attempt to replace its reference
643
643
// in the statement with the var value and then erase the var definition.
644
644
645
-
var self = compressor.self();
646
-
var var_defs_removed = false;
645
+
var scope = compressor.find_parent(AST_Scope);
647
646
var toplevel = compressor.option("toplevel");
648
647
for (var stat_index = statements.length; --stat_index >= 0;) {
649
648
var stat = statements[stat_index];
650
-
if (stat instanceof AST_Definitions) continue;
651
-
652
-
// Process child blocks of statement if present.
653
-
[stat, stat.body, stat.alternative, stat.bcatch, stat.bfinally].forEach(function(node) {
654
-
node && node.body && collapse_single_use_vars(node.body, compressor);
655
-
});
656
649
657
-
// The variable definition must precede a statement.
658
-
if (stat_index <= 0) break;
659
-
var prev_stat_index = stat_index - 1;
660
-
var prev_stat = statements[prev_stat_index];
661
-
if (!(prev_stat instanceof AST_Definitions)) continue;
662
-
var var_defs = prev_stat.definitions;
663
-
if (var_defs == null) continue;
664
-
665
-
var var_names_seen = {};
650
+
var var_names_seen = Object.create(null);
666
651
var side_effects_encountered = false;
667
652
var lvalues_encountered = false;
668
-
var lvalues = {};
653
+
var lvalues = Object.create(null);
654
+
var prev_stat_index, var_defs, var_defs_index;
669
655
670
656
// Scan variable definitions from right to left.
671
-
for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) {
672
-
673
-
// Obtain var declaration and var name with basic sanity check.
674
-
var var_decl = var_defs[var_defs_index];
675
-
if (var_decl.value == null) break;
676
-
var var_name = var_decl.name.name;
677
-
if (!var_name || !var_name.length) break;
678
-
679
-
// Bail if we've seen a var definition of same name before.
680
-
if (var_name in var_names_seen) break;
681
-
var_names_seen[var_name] = true;
682
-
683
-
// Only interested in cases with just one reference to the variable.
684
-
var def = self.find_variable && self.find_variable(var_name);
685
-
if (!def || !def.references || def.references.length !== 1
686
-
|| var_name == "arguments" || (!toplevel && def.global)) {
687
-
side_effects_encountered = true;
688
-
continue;
657
+
if (stat instanceof AST_Definitions) {
658
+
prev_stat_index = stat_index;
659
+
var_defs = stat.definitions;
660
+
for (var_defs_index = var_defs.length - 1; --var_defs_index >= 0;) {
661
+
if (collapse(var_defs[var_defs_index + 1])) break;
689
662
}
690
-
var ref = def.references[0];
691
-
692
-
// Don't replace ref if eval() or with statement in scope.
693
-
if (ref.scope.uses_eval || ref.scope.uses_with) break;
694
-
695
-
// Constant single use vars can be replaced in any scope.
696
-
if (var_decl.value.is_constant()) {
697
-
var ctt = new TreeTransformer(function(node) {
698
-
var parent = ctt.parent();
699
-
if (parent instanceof AST_IterationStatement
700
-
&& (parent.condition === node || parent.init === node)) {
701
-
return node;
702
-
}
703
-
if (node === ref)
704
-
return replace_var(node, parent, true);
705
-
});
706
-
stat.transform(ctt);
707
-
continue;
663
+
} else if (stat_index > 0) {
664
+
// The variable definition must precede a statement.
665
+
prev_stat_index = stat_index - 1;
666
+
var prev_stat = statements[prev_stat_index];
667
+
if (!(prev_stat instanceof AST_Definitions)) continue;
668
+
var_defs = prev_stat.definitions;
669
+
for (var_defs_index = var_defs.length; --var_defs_index >= 0;) {
670
+
if (collapse(stat)) break;
708
671
}
672
+
}
673
+
}
709
674
710
-
// Restrict var replacement to constants if side effects encountered.
711
-
if (side_effects_encountered |= lvalues_encountered) continue;
675
+
return statements;
712
676
713
-
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
714
-
// Non-constant single use vars can only be replaced in same scope.
715
-
if (ref.scope !== self) {
716
-
side_effects_encountered |= value_has_side_effects;
717
-
continue;
718
-
}
677
+
function collapse(stat) {
678
+
var var_decl = var_defs[var_defs_index];
679
+
// `drop_unused()` shuffles variables without values to the top,
680
+
// so we can terminate upon first sighting as an optimization.
681
+
if (var_decl.value == null) return true;
682
+
var var_name = var_decl.name.name;
683
+
684
+
// Bail if we've seen a var definition of same name before.
685
+
if (var_name in var_names_seen) return true;
686
+
var_names_seen[var_name] = true;
687
+
688
+
// Only interested in non-constant values.
689
+
if (var_decl.value.is_constant()) return;
690
+
691
+
// Only interested in cases with just one reference to the variable.
692
+
var def = var_decl.name.definition();
693
+
if (def.references.length !== 1
694
+
|| var_name == "arguments" || (!toplevel && def.global)) {
695
+
side_effects_encountered = true;
696
+
return;
697
+
}
698
+
var ref = def.references[0];
699
+
700
+
// Don't replace ref if eval() or with statement in scope.
701
+
if (ref.scope.uses_eval || ref.scope.uses_with) return true;
702
+
703
+
// Restrict var replacement to constants if side effects encountered.
704
+
if (side_effects_encountered |= lvalues_encountered) return;
719
705
720
-
// Detect lvalues in var value.
721
-
var tw = new TreeWalker(function(node){
722
-
if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
723
-
lvalues[node.name] = lvalues_encountered = true;
706
+
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
707
+
// Non-constant single use vars can only be replaced in same scope.
708
+
if (ref.scope !== scope) {
709
+
side_effects_encountered |= value_has_side_effects;
710
+
return;
711
+
}
712
+
713
+
// Detect lvalues in var value.
714
+
var tw = new TreeWalker(function(node){
715
+
if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
716
+
lvalues[node.name] = lvalues_encountered = true;
717
+
}
718
+
});
719
+
var_decl.value.walk(tw);
720
+
721
+
// Replace the non-constant single use var in statement if side effect free.
722
+
var unwind = false;
723
+
var tt = new TreeTransformer(
724
+
function preorder(node) {
725
+
if (unwind || node instanceof AST_Scope && node !== scope) return node;
726
+
var parent = tt.parent();
727
+
if (node instanceof AST_Try
728
+
|| node instanceof AST_With
729
+
|| node instanceof AST_Case
730
+
|| node instanceof AST_IterationStatement
731
+
|| (parent instanceof AST_If && node !== parent.condition)
732
+
|| (parent instanceof AST_Conditional && node !== parent.condition)
733
+
|| (node instanceof AST_SymbolRef
734
+
&& value_has_side_effects
735
+
&& !are_references_in_scope(node.definition(), scope))
736
+
|| (parent instanceof AST_Binary
737
+
&& (parent.operator == "&&" || parent.operator == "||")
738
+
&& node === parent.right)
739
+
|| (parent instanceof AST_Switch && node !== parent.expression)) {
740
+
return side_effects_encountered = unwind = true, node;
724
741
}
725
-
});
726
-
var_decl.value.walk(tw);
727
-
728
-
// Replace the non-constant single use var in statement if side effect free.
729
-
var unwind = false;
730
-
var tt = new TreeTransformer(
731
-
function preorder(node) {
732
-
if (unwind) return node;
733
-
var parent = tt.parent();
734
-
if (node instanceof AST_Lambda
735
-
|| node instanceof AST_Try
736
-
|| node instanceof AST_With
737
-
|| node instanceof AST_Case
738
-
|| node instanceof AST_IterationStatement
739
-
|| (parent instanceof AST_If && node !== parent.condition)
740
-
|| (parent instanceof AST_Conditional && node !== parent.condition)
741
-
|| (node instanceof AST_SymbolRef
742
-
&& value_has_side_effects
743
-
&& !are_references_in_scope(node.definition(), self))
744
-
|| (parent instanceof AST_Binary
745
-
&& (parent.operator == "&&" || parent.operator == "||")
746
-
&& node === parent.right)
747
-
|| (parent instanceof AST_Switch && node !== parent.expression)) {
748
-
return side_effects_encountered = unwind = true, node;
749
-
}
750
-
function are_references_in_scope(def, scope) {
751
-
if (def.orig.length === 1
752
-
&& def.orig[0] instanceof AST_SymbolDefun) return true;
753
-
if (def.scope !== scope) return false;
754
-
var refs = def.references;
755
-
for (var i = 0, len = refs.length; i < len; i++) {
756
-
if (refs[i].scope !== scope) return false;
757
-
}
758
-
return true;
759
-
}
760
-
},
761
-
function postorder(node) {
762
-
if (unwind) return node;
763
-
if (node === ref)
764
-
return unwind = true, replace_var(node, tt.parent(), false);
765
-
if (side_effects_encountered |= node.has_side_effects(compressor))
766
-
return unwind = true, node;
767
-
if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
768
-
side_effects_encountered = true;
769
-
return unwind = true, node;
742
+
function are_references_in_scope(def, scope) {
743
+
if (def.orig.length === 1
744
+
&& def.orig[0] instanceof AST_SymbolDefun) return true;
745
+
if (def.scope !== scope) return false;
746
+
var refs = def.references;
747
+
for (var i = 0, len = refs.length; i < len; i++) {
748
+
if (refs[i].scope !== scope) return false;
770
749
}
750
+
return true;
771
751
}
772
-
);
773
-
stat.transform(tt);
774
-
}
775
-
}
776
-
777
-
// Remove extraneous empty statments in block after removing var definitions.
778
-
// Leave at least one statement in `statements`.
779
-
if (var_defs_removed) for (var i = statements.length; --i >= 0;) {
780
-
if (statements.length > 1 && statements[i] instanceof AST_EmptyStatement)
781
-
statements.splice(i, 1);
752
+
},
753
+
function postorder(node) {
754
+
if (unwind) return node;
755
+
if (node === ref)
756
+
return unwind = true, replace_var(var_decl, node, tt.parent(), false);
757
+
if (side_effects_encountered |= node.has_side_effects(compressor))
758
+
return unwind = true, node;
759
+
if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
760
+
side_effects_encountered = true;
761
+
return unwind = true, node;
762
+
}
763
+
}
764
+
);
765
+
stat.transform(tt);
782
766
}
783
767
784
-
return statements;
785
-
786
768
function is_lvalue(node, parent) {
787
769
return node instanceof AST_SymbolRef && is_lhs(node, parent);
788
770
}
789
-
function replace_var(node, parent, is_constant) {
771
+
772
+
function replace_var(var_decl, node, parent, is_constant) {
790
773
if (is_lvalue(node, parent)) return node;
791
774
792
775
// Remove var definition and return its value to the TreeTransformer to replace.
@@ -795,14 +778,19 @@ merge(Compressor.prototype, {
795
778
796
779
var_defs.splice(var_defs_index, 1);
797
780
if (var_defs.length === 0) {
798
-
statements[prev_stat_index] = make_node(AST_EmptyStatement, self);
799
-
var_defs_removed = true;
781
+
statements.splice(prev_stat_index, 1);
782
+
stat_index--;
800
783
}
801
784
// Further optimize statement after substitution.
802
785
stat.reset_opt_flags(compressor);
803
786
804
-
compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
805
-
" " + var_name + " [{file}:{line},{col}]", node.start);
787
+
compressor.info("Collapsing {type} {name} [{file}:{line},{col}]", {
788
+
type: is_constant ? "constant" : "variable",
789
+
name: var_decl.name.name,
790
+
file: node.start.file,
791
+
line: node.start.line,
792
+
col: node.start.col
793
+
});
806
794
CHANGED = true;
807
795
return value;
808
796
}
@@ -1746,6 +1734,7 @@ merge(Compressor.prototype, {
1746
1734
def(AST_SymbolRef, function(compressor){
1747
1735
return this.undeclared();
1748
1736
});
1737
+
def(AST_SymbolDeclaration, return_false);
1749
1738
def(AST_Object, function(compressor){
1750
1739
return any(this.properties, compressor);
1751
1740
});
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4