First class block scope

- Make let, const, and class symbols be declared in a block scope.
- Piggy back on existing catch symbol implementation to get block-aware mangling working
- Make sure unused block-scoped declarations can be dropped
- Don't eliminate a block if it has a block-scoped declaration
- Remove silly empty anonymous blocks left over from drop_unused
- AST_Toplevel now gets to call drop_unused too, since block-scoped variables aren't global!
- Don't consider block declarations global
This commit is contained in:
Fábio Santos
2016-02-28 14:06:51 +00:00
committed by Richard van Velzen
parent 6702cae918
commit 634f231b78
7 changed files with 266 additions and 30 deletions

View File

@@ -190,6 +190,14 @@ merge(Compressor.prototype, {
return false;
};
function can_be_evicted_from_block(node) {
return !(
node instanceof AST_DefClass ||
node instanceof AST_Let ||
node instanceof AST_Const
);
}
function loop_body(x) {
if (x instanceof AST_Switch) return x;
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
@@ -311,7 +319,7 @@ merge(Compressor.prototype, {
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
return statements.reduce(function(a, stat){
if (stat instanceof AST_BlockStatement) {
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
a.push.apply(a, eliminate_spurious_blocks(stat.body));
} else if (stat instanceof AST_EmptyStatement) {
@@ -633,7 +641,7 @@ merge(Compressor.prototype, {
function extract_declarations_from_unreachable_code(compressor, stat, target) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
stat.walk(new TreeWalker(function(node){
if (node instanceof AST_Definitions) {
if (node instanceof AST_Var) {
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
node.remove_initializers();
target.push(node);
@@ -957,6 +965,8 @@ merge(Compressor.prototype, {
});
def(AST_Defun, function(compressor){ return true });
def(AST_Function, function(compressor){ return false });
def(AST_Class, function(compressor){ return false });
def(AST_DefClass, function(compressor){ return true });
def(AST_Binary, function(compressor){
return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor);
@@ -1067,7 +1077,11 @@ merge(Compressor.prototype, {
OPT(AST_BlockStatement, function(self, compressor){
self.body = tighten_body(self.body, compressor);
switch (self.body.length) {
case 1: return self.body[0];
case 1:
if (can_be_evicted_from_block(self.body[0])) {
return self.body[0];
}
break;
case 0: return make_node(AST_EmptyStatement, self);
}
return self;
@@ -1077,7 +1091,6 @@ merge(Compressor.prototype, {
var self = this;
if (compressor.has_directive("use asm")) return self;
if (compressor.option("unused")
&& !(self instanceof AST_Toplevel)
&& !self.uses_eval
) {
var in_use = [];
@@ -1166,8 +1179,11 @@ merge(Compressor.prototype, {
}
}
}
if (node instanceof AST_Defun && node !== self) {
if (!member(node.name.definition(), in_use)) {
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
var keep =
member(node.name.definition(), in_use) ||
node.name.definition().global;
if (!keep) {
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
name : node.name.name,
file : node.name.start.file,
@@ -1182,6 +1198,7 @@ merge(Compressor.prototype, {
var def = node.definitions.filter(function(def){
if (def.is_destructuring()) return true;
if (member(def.name.definition(), in_use)) return true;
if (def.name.definition().global) return true;
var w = {
name : def.name.name,
file : def.name.start.file,
@@ -1260,6 +1277,12 @@ merge(Compressor.prototype, {
});
}
}
if (node instanceof AST_BlockStatement) {
descend(node, this);
if (in_list && all(node.body, can_be_evicted_from_block)) {
return MAP.splice(node.body);
}
}
if (node instanceof AST_Scope && node !== self)
return node;
}