diff --git a/README.md b/README.md index 27d06cd6..f97c9948 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,10 @@ to set `true`; it's effectively a shortcut for `foo=true`). - `unsafe` (default: false) -- apply "unsafe" transformations (discussion below) +- `null_this` -- if `null` appears in the `this`-parameter position of `.call(..)`, `.apply(..)`, or `.bind(..)`, rewrite `null` to `0`. + + **Note:** potentially unsafe operation, only use if `null` implies the `this` binding is not relevant for the function call in question. + - `conditionals` -- apply optimizations for `if`-s and conditional expressions diff --git a/lib/compress.js b/lib/compress.js index b589aca5..8bd1b661 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -54,6 +54,7 @@ function Compressor(options, false_by_default) { drop_debugger : !false_by_default, unsafe : false, unsafe_comps : false, + null_this : false, conditionals : !false_by_default, comparisons : !false_by_default, evaluate : !false_by_default, @@ -1833,6 +1834,11 @@ merge(Compressor.prototype, { return make_node(AST_Undefined, self).transform(compressor); } } + + // check for a `this` param to `call/apply/bind` that's `null` + // and rewrite to `0`, if "null_this" option is enabled + self.null_this(compressor); + return self.evaluate(compressor)[0]; }); @@ -2371,4 +2377,17 @@ merge(Compressor.prototype, { OPT(AST_Object, literals_in_boolean_context); OPT(AST_RegExp, literals_in_boolean_context); + AST_Call.DEFMETHOD("null_this",function(compressor){ + if (compressor.option("null_this") + && this.expression.property + && /^(?:call|apply|bind)$/.test(this.expression.property) + && this.args.length > 0 + && this.args[0].start.type === "atom" + && this.args[0].end.value === "null") { + this.args[0] = make_node(AST_Number, this.args[0], { + value: 0 + }); + } + }); + })(); diff --git a/test/compress/null-this.js b/test/compress/null-this.js new file mode 100644 index 00000000..e1d1d4bb --- /dev/null +++ b/test/compress/null-this.js @@ -0,0 +1,46 @@ +keep_null_this: { + options = { + }; + input: { + foo(null,42); + call(null,42); + apply(null,42); + bind(null,42); + foo.call(null,42); + foo.apply(null,42); + foo.bind(null,42); + } + expect: { + foo(null,42); + call(null,42); + apply(null,42); + bind(null,42); + foo.call(null,42); + foo.apply(null,42); + foo.bind(null,42); + } +} + +drop_null_this: { + options = { + null_this: true + }; + input: { + foo(null,42); + call(null,42); + apply(null,42); + bind(null,42); + foo.call(null,42); + foo.apply(null,42); + foo.bind(null,42); + } + expect: { + foo(null,42); + call(null,42); + apply(null,42); + bind(null,42); + foo.call(0,42); + foo.apply(0,42); + foo.bind(0,42); + } +}