convert void 0 to a new var

fixes #2585
This commit is contained in:
Dan Wolff 2018-02-12 23:08:44 +01:00
parent dea0cc0662
commit 000620be59
2 changed files with 203 additions and 0 deletions

View File

@ -90,6 +90,8 @@ function Compressor(options, false_by_default) {
unsafe_regexp : false, unsafe_regexp : false,
unsafe_undefined: false, unsafe_undefined: false,
unused : !false_by_default, unused : !false_by_default,
// TODO Is "void" a good name? "void_to_var"?
void : false,
warnings : false, warnings : false,
}, true); }, true);
var global_defs = this.options["global_defs"]; var global_defs = this.options["global_defs"];
@ -199,6 +201,13 @@ merge(Compressor.prototype, {
if (this.option("expression")) { if (this.option("expression")) {
node.process_expression(false); node.process_expression(false);
} }
if (this.option("void")) {
try {
node.process_void();
} catch (e) {
console.warn(e);
}
}
return node; return node;
}, },
info: function() { info: function() {
@ -315,6 +324,75 @@ merge(Compressor.prototype, {
self.transform(tt); self.transform(tt);
}); });
AST_Scope.DEFMETHOD("process_void", function (compressor) {
var lambdas_with_var = [];
var tw = new TreeWalker(function(node) {
if (node instanceof AST_Var) {
// TODO Find parent efficiently
// TODO Should AST_Scope be used instead?
var lambda = tw.find_parent(AST_Lambda);
if (lambda && !lambda._first_var) {
lambda._first_var = node;
lambdas_with_var.push(lambda);
}
} else if (node instanceof AST_UnaryPrefix
&& node.operator == "void"
&& node.expression instanceof AST_Number) {
// TODO Find parent efficiently
// TODO Should AST_Scope be used instead?
var lambda = tw.find_parent(AST_Lambda);
if (lambda) {
if (!lambda._void_uses) lambda._void_uses = [];
// TODO Simpler way?
node._parent = tw.parent();
lambda._void_uses.push(node);
}
} else if (node instanceof AST_Catch
&& node.argname.name.indexOf("undefined") == 0) {
// TODO Is there a cleaner way to do this?
return true;
}
});
this.walk(tw);
// TODO Is there a better way to replace a node?
var replacer = new TreeTransformer(function(node) {
if (node._replacement) {
return node._replacement;
}
});
lambdas_with_var.forEach(function(lambda) {
if (lambda._processed_void || !lambda._void_uses) return;
// TODO This is similar to make_sym() below. Should they be combined?
var new_var = make_node(AST_SymbolVar, null, {
name: lambda.make_var_name("undefined"),
scope: lambda,
});
var def = lambda.def_variable(new_var);
lambda.enclosed.push(def);
var new_var_def = make_node(AST_VarDef, lambda._first_var, {
name: new_var,
value: null,
});
lambda._first_var.definitions.push(new_var_def);
lambda._void_uses.forEach(function(void_use) {
// TODO Is there a better way to replace a node?
void_use._replacement = make_node(AST_SymbolRef, void_use, new_var);
void_use._parent.transform(replacer);
});
lambda._processed_void = true;
});
});
(function(def){ (function(def){
def(AST_Node, noop); def(AST_Node, noop);

125
test/compress/void.js Normal file
View File

@ -0,0 +1,125 @@
void_1: {
options = {
void: true,
}
input: {
var a = 0;
x = void 0;
if (void 0 === b)
c = void 0;
function f1() {
var a = 1;
console.log(void 0);
}
function f2(undefined) {
var a = 2;
console.log(void 0);
}
function f3() {
var undefined = 3;
console.log(void 0);
}
function f4() {
console.log(void 0);
for (var a = 4;;);
var b = 4;
function f5() {
var c = 5;
var d = 5;
console.log(void 0);
}
}
function f6() {
try {
var a = 6;
console.log(void 0);
} catch (e) {
console.log(void 0);
}
}
}
expect: {
var a = 0;
x = void 0;
if (void 0 === b)
c = void 0;
function f1() {
var a = 1, undefined;
console.log(undefined)
}
function f2(undefined) {
var a = 2, undefined$0;
console.log(undefined$0)
}
function f3() {
var undefined = 3, undefined$0;
console.log(undefined$0)
}
function f4() {
console.log(undefined);
for (var a = 4, undefined;;);
var b = 4;
function f5() {
var c = 5, undefined;
var d = 5;
console.log(undefined)
}
}
function f6() {
try {
var a = 6, undefined;
console.log(undefined)
} catch (e) {
console.log(undefined)
}
}
}
}
void_2: {
options = {
void: true,
}
input: {
f();
function f() {
var a = 1;
console.log(void 0);
try {
throw "FAIL";
} catch (undefined) {
console.log(void 0);
}
}
}
expect: {
f();
function f() {
var a = 1, undefined;
console.log(undefined);
try {
throw "FAIL"
} catch (undefined) {
console.log(void 0);
}
}
}
expect_stdout: [
"undefined",
"undefined",
]
}