var path = require("path");
var UglifyJS = require("../tools/node");
-var skip_keys = [ "cname", "fixed", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
+var skip_keys = [ "cname", "fixed", "inlined", "length_read", "parent_scope", "scope" ];
+var truthy_keys = [ "optional", "pure", "terminal", "uses_arguments", "uses_eval", "uses_with" ];
+
var files = {};
var options = {};
var short_forms = {
case "thedef":
return symdef(value);
}
- if (skip_key(key)) return;
+ if (skip_property(key, value)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
if (value instanceof UglifyJS.AST_Node) {
return options;
}
-function skip_key(key) {
- return skip_keys.indexOf(key) >= 0;
+function skip_property(key, value) {
+ return skip_keys.indexOf(key) >= 0
+ // only skip truthy_keys if their value is falsy
+ || truthy_keys.indexOf(key) >= 0 && !value;
}
function symdef(def) {
if (base && base.PROPS) props = props.concat(base.PROPS);
var code = [
"return function AST_", type, "(props){",
+ // not essential, but speeds up compress by a few percent
+ "this._bits=0;",
"if(props){",
];
props.forEach(function(prop) {
},
}, null);
+DEF_BITPROPS(AST_Node, [
+ "_optimized",
+ "_squeezed",
+ // AST_Call
+ "call_only",
+ "collapse_scanning",
+ // AST_SymbolRef
+ "defined",
+ "evaluating",
+ "falsy",
+ // AST_SymbolRef
+ "in_arg",
+ // AST_Return
+ "in_bool",
+ // AST_SymbolRef
+ "is_undefined",
+ // AST_LambdaExpression
+ // AST_LambdaDefinition
+ "inlined",
+ // AST_Lambda
+ "length_read",
+ // AST_Yield
+ "nested",
+ // AST_Lambda
+ "new",
+ // AST_Call
+ // AST_PropAccess
+ "optional",
+ // AST_ClassProperty
+ "private",
+ // AST_Call
+ "pure",
+ // AST_Assign
+ "redundant",
+ // AST_ClassProperty
+ "static",
+ // AST_Call
+ // AST_PropAccess
+ "terminal",
+ "truthy",
+ // AST_Scope
+ "uses_eval",
+ // AST_Scope
+ "uses_with",
+]);
+
(AST_Node.log_function = function(fn, verbose) {
if (typeof fn != "function") {
AST_Node.info = AST_Node.warn = noop;
argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
length_read: "[boolean/S] whether length property of this function is accessed",
rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
- uses_arguments: "[boolean/S] whether this function accesses the arguments array",
+ uses_arguments: "[boolean|number/S] whether this function accesses the arguments array",
},
each_argname: function(visit) {
var tw = new TreeWalker(function(node) {
args: "[AST_Node*] array of arguments",
expression: "[AST_Node] expression to invoke as function",
optional: "[boolean] whether the expression is optional chaining",
- pure: "[string/S] marker for side-effect-free call expression",
+ pure: "[boolean/S] marker for side-effect-free call expression",
terminal: "[boolean] whether the chain has ended",
},
walk: function(visitor) {
if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
right.walk(tw);
walk_prop(left);
- node.__drop = true;
+ node.redundant = true;
return true;
}
if (ld && right instanceof AST_LambdaExpression) {
stop_if_hit = if_hit;
stop_after = after;
can_replace = replace;
- delete fn.collapse_scanning;
+ fn.collapse_scanning = false;
if (!abort) return false;
abort = false;
return true;
}
if (node instanceof AST_Scope && node !== fn) return true;
}));
- delete fn.evaluating;
+ fn.evaluating = false;
if (!found) return;
}
return this;
});
fn.evaluating = true;
val = val._eval(compressor, ignore_side_effects, cached, depth);
- delete fn.evaluating;
+ fn.evaluating = false;
}
cached_args.forEach(function(node) {
delete node._eval;
}) && all(fn.argnames, function(argname) {
return !argname.match_symbol(return_false);
}) && !(fn.rest && fn.rest.match_symbol(return_false));
- delete fn.new;
+ fn.new = false;
return result;
}
function drop_class(self, compressor, first_in_statement) {
if (compressor.option("dead_code")) {
if (self.left instanceof AST_PropAccess) {
if (self.operator == "=") {
- if (self.__drop) {
+ if (self.redundant) {
var exprs = [ self.left.expression ];
if (self.left instanceof AST_Sub) exprs.push(self.left.property);
exprs.push(self.right);
parent = output.parent(level++);
if (parent instanceof AST_Call && parent.expression === node) return;
} while (parent instanceof AST_PropAccess && parent.expression === node);
- output.print(typeof self.pure == "string" ? "/*" + self.pure + "*/" : "/*@__PURE__*/");
+ output.print("/*@__PURE__*/");
}
function print_call_args(self, output) {
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
var comments = start.comments_before;
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
while (--i >= 0) {
- var match = /[@#]__PURE__/.exec(comments[i].value);
- if (match) {
- expr.pure = match[0];
+ if (/[@#]__PURE__/.test(comments[i].value)) {
+ expr.pure = true;
break;
}
}
"use strict";
function SymbolDef(id, scope, orig, init) {
+ this._bits = 0;
this.eliminated = 0;
- this.exported = false;
- this.global = false;
this.id = id;
this.init = init;
this.mangled_name = null;
this.references = [];
this.replaced = 0;
this.scope = scope;
- this.undeclared = false;
}
SymbolDef.prototype = {
},
};
+DEF_BITPROPS(SymbolDef, [
+ "const_redefs",
+ "cross_loop",
+ "direct_access",
+ "exported",
+ "global",
+ "undeclared",
+]);
+
var unary_side_effects = makePredicate("delete ++ --");
function is_lhs(node, parent) {
return false;
}
}
+
+function DEF_BITPROPS(ctor, props) {
+ if (props.length > 31) throw new Error("Too many properties: " + props.length + "\n" + props.join(", "));
+ props.forEach(function(name, pos) {
+ var mask = 1 << pos;
+ Object.defineProperty(ctor.prototype, name, {
+ get: function() {
+ return !!(this._bits & mask);
+ },
+ set: function(val) {
+ if (val)
+ this._bits |= mask;
+ else
+ this._bits &= ~mask;
+ },
+ });
+ });
+}
}
expect_exact: [
"/*@__PURE__*/a(3),",
- "/*#__PURE__*/b(5),",
+ "/*@__PURE__*/b(5),",
"c(side_effect),",
- "/*#__PURE__*/d(effect());",
+ "/*@__PURE__*/d(effect());",
]
}