improve unsafe on undefined (#1548)

`unsafe` turns undefined keyword into a variable of the same name if found, but that interferes with other related optimisations.

Keep track of such transformations to ensure zero information loss in the process.
This commit is contained in:
Alex Lam S.L
2017-03-05 13:09:27 +08:00
committed by GitHub
parent 1f0333e9f1
commit b33e7f88e6
3 changed files with 52 additions and 21 deletions

View File

@@ -765,7 +765,7 @@ merge(Compressor.prototype, {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, {
value: make_node(AST_Undefined, stat)
value: null
});
ret[0] = stat.transform(compressor);
continue loop;
@@ -798,7 +798,7 @@ merge(Compressor.prototype, {
&& !stat.alternative) {
CHANGED = true;
ret.push(make_node(AST_Return, ret[0], {
value: make_node(AST_Undefined, ret[0])
value: null
}).transform(compressor));
ret.unshift(stat);
continue loop;
@@ -1055,6 +1055,10 @@ merge(Compressor.prototype, {
}));
};
function is_undefined(node) {
return node instanceof AST_Undefined || node.is_undefined;
}
/* -----[ boolean/negation helpers ]----- */
// methods to determine whether an expression has a boolean result type
@@ -2402,8 +2406,8 @@ merge(Compressor.prototype, {
return make_node(self.body.CTOR, self, {
value: make_node(AST_Conditional, self, {
condition : self.condition,
consequent : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
consequent : self.body.value || make_node(AST_Undefined, self.body),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
})
}).transform(compressor);
}
@@ -2834,7 +2838,7 @@ merge(Compressor.prototype, {
return self.car;
}
}
if (self.cdr instanceof AST_Undefined) {
if (is_undefined(self.cdr)) {
return make_node(AST_UnaryPrefix, self, {
operator : "void",
expression : self.car
@@ -2873,7 +2877,7 @@ merge(Compressor.prototype, {
self.expression = e;
return self;
} else {
return make_node(AST_Undefined, self);
return make_node(AST_Undefined, self).transform(compressor);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
@@ -3354,7 +3358,7 @@ merge(Compressor.prototype, {
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
return make_node(AST_Undefined, self);
return make_node(AST_Undefined, self).transform(compressor);
case "NaN":
return make_node(AST_NaN, self).transform(compressor);
case "Infinity":
@@ -3397,11 +3401,13 @@ merge(Compressor.prototype, {
var scope = compressor.find_parent(AST_Scope);
var undef = scope.find_variable("undefined");
if (undef) {
return make_node(AST_SymbolRef, self, {
var ref = make_node(AST_SymbolRef, self, {
name : "undefined",
scope : scope,
thedef : undef
});
ref.is_undefined = true;
return ref;
}
}
return self;
@@ -3688,7 +3694,7 @@ merge(Compressor.prototype, {
OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){
if (self.value instanceof AST_Undefined) {
if (self.value && is_undefined(self.value)) {
self.value = null;
}
return self;

View File

@@ -2,6 +2,7 @@
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true
}
@@ -19,12 +20,7 @@ unsafe_undefined: {
expect: {
function f(n) {
return function() {
if (a)
return b;
if (c)
return d;
else
return n;
return a ? b : c ? d : n;
};
}
}
@@ -32,6 +28,7 @@ unsafe_undefined: {
keep_fnames: {
options = {
conditionals: true,
if_return: true,
unsafe: true
}
@@ -57,12 +54,7 @@ keep_fnames: {
function n(n) {
return n * n;
}
if (a)
return b;
if (c)
return d;
else
return r;
return a ? b : c ? d : r;
};
}
}

View File

@@ -251,3 +251,36 @@ iife: {
function d() {}(), function e() {}(), function f() {}(), function g() {}();
}
}
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
unsafe: true,
}
input: {
function f(undefined) {
if (a)
return b;
if (c)
return d;
}
function g(undefined) {
if (a)
return b;
if (c)
return d;
e();
}
}
expect: {
function f(undefined) {
return a ? b : c ? d : undefined;
}
function g(undefined) {
return a ? b : c ? d : void e();
}
}
}