Support marking a call as pure

A function call or IIFE with an immediately preceding comment
containing `@__PURE__` or `#__PURE__` is deemed to be a
side-effect-free pure function call and can potentially be
dropped.

Depends on `side_effects` option.

`[#@]__PURE__` hint will be removed from comment when pure
call is dropped.

fixes #1261
closes #1448
This commit is contained in:
kzc
2017-02-21 14:24:18 +08:00
committed by alexlamsl
parent d48a3080ac
commit 1e51586996
3 changed files with 151 additions and 2 deletions

View File

@@ -1360,6 +1360,22 @@ merge(Compressor.prototype, {
});
});
AST_Call.DEFMETHOD("has_pure_annotation", function(compressor) {
if (!compressor.option("side_effects")) return false;
if (this.pure !== undefined) return this.pure;
var pure = false;
var comments, last_comment;
if (this.start
&& (comments = this.start.comments_before)
&& comments.length
&& /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
last_comment.value = last_comment.value.replace(/[@#]__PURE__/g, ' ');
pure = true;
}
return this.pure = pure;
});
// determine if expression has side effects
(function(def){
def(AST_Node, return_true);
@@ -1369,7 +1385,7 @@ merge(Compressor.prototype, {
def(AST_This, return_false);
def(AST_Call, function(compressor){
if (compressor.pure_funcs(this)) return true;
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return true;
for (var i = this.args.length; --i >= 0;) {
if (this.args[i].has_side_effects(compressor))
return true;
@@ -1904,7 +1920,7 @@ merge(Compressor.prototype, {
def(AST_Constant, return_null);
def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){
if (compressor.pure_funcs(this)) return this;
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
var args = trim(this.args, compressor, first_in_statement);
return args && AST_Seq.from_array(args);
});