diff --git a/lib/compress.js b/lib/compress.js index 142728c2a00..1c9bc429e2c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -90,6 +90,8 @@ function Compressor(options, false_by_default) { unsafe_regexp : false, unsafe_undefined: false, unused : !false_by_default, + // TODO Is "void" a good name? "void_to_var"? + void : false, warnings : false, }, true); var global_defs = this.options["global_defs"]; @@ -199,6 +201,9 @@ merge(Compressor.prototype, { if (this.option("expression")) { node.process_expression(false); } + if (this.option("void")) { + node.process_void(); + } return node; }, info: function() { @@ -315,6 +320,75 @@ merge(Compressor.prototype, { 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){ def(AST_Node, noop); diff --git a/test/compress/void.js b/test/compress/void.js new file mode 100644 index 00000000000..b48d7c1a8ee --- /dev/null +++ b/test/compress/void.js @@ -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", + ] +}