reduce memory pressure via bit fields (#5203)
This commit is contained in:
12
bin/uglifyjs
12
bin/uglifyjs
@@ -10,7 +10,9 @@ var info = require("../package.json");
|
|||||||
var path = require("path");
|
var path = require("path");
|
||||||
var UglifyJS = require("../tools/node");
|
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 files = {};
|
||||||
var options = {};
|
var options = {};
|
||||||
var short_forms = {
|
var short_forms = {
|
||||||
@@ -430,7 +432,7 @@ function run() {
|
|||||||
case "thedef":
|
case "thedef":
|
||||||
return symdef(value);
|
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.AST_Token) return;
|
||||||
if (value instanceof UglifyJS.Dictionary) return;
|
if (value instanceof UglifyJS.Dictionary) return;
|
||||||
if (value instanceof UglifyJS.AST_Node) {
|
if (value instanceof UglifyJS.AST_Node) {
|
||||||
@@ -559,8 +561,10 @@ function parse_js(value, options, flag) {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
function skip_key(key) {
|
function skip_property(key, value) {
|
||||||
return skip_keys.indexOf(key) >= 0;
|
return skip_keys.indexOf(key) >= 0
|
||||||
|
// only skip truthy_keys if their value is falsy
|
||||||
|
|| truthy_keys.indexOf(key) >= 0 && !value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function symdef(def) {
|
function symdef(def) {
|
||||||
|
|||||||
52
lib/ast.js
52
lib/ast.js
@@ -50,6 +50,8 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
if (base && base.PROPS) props = props.concat(base.PROPS);
|
if (base && base.PROPS) props = props.concat(base.PROPS);
|
||||||
var code = [
|
var code = [
|
||||||
"return function AST_", type, "(props){",
|
"return function AST_", type, "(props){",
|
||||||
|
// not essential, but speeds up compress by a few percent
|
||||||
|
"this._bits=0;",
|
||||||
"if(props){",
|
"if(props){",
|
||||||
];
|
];
|
||||||
props.forEach(function(prop) {
|
props.forEach(function(prop) {
|
||||||
@@ -135,6 +137,52 @@ var AST_Node = DEFNODE("Node", "start end", {
|
|||||||
},
|
},
|
||||||
}, null);
|
}, 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) {
|
(AST_Node.log_function = function(fn, verbose) {
|
||||||
if (typeof fn != "function") {
|
if (typeof fn != "function") {
|
||||||
AST_Node.info = AST_Node.warn = noop;
|
AST_Node.info = AST_Node.warn = noop;
|
||||||
@@ -549,7 +597,7 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
|
|||||||
argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
|
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",
|
length_read: "[boolean/S] whether length property of this function is accessed",
|
||||||
rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
|
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) {
|
each_argname: function(visit) {
|
||||||
var tw = new TreeWalker(function(node) {
|
var tw = new TreeWalker(function(node) {
|
||||||
@@ -1295,7 +1343,7 @@ var AST_Call = DEFNODE("Call", "args expression optional pure terminal", {
|
|||||||
args: "[AST_Node*] array of arguments",
|
args: "[AST_Node*] array of arguments",
|
||||||
expression: "[AST_Node] expression to invoke as function",
|
expression: "[AST_Node] expression to invoke as function",
|
||||||
optional: "[boolean] whether the expression is optional chaining",
|
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",
|
terminal: "[boolean] whether the chain has ended",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
|
|||||||
@@ -898,7 +898,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
|
if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
|
||||||
right.walk(tw);
|
right.walk(tw);
|
||||||
walk_prop(left);
|
walk_prop(left);
|
||||||
node.__drop = true;
|
node.redundant = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ld && right instanceof AST_LambdaExpression) {
|
if (ld && right instanceof AST_LambdaExpression) {
|
||||||
@@ -2274,7 +2274,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
stop_if_hit = if_hit;
|
stop_if_hit = if_hit;
|
||||||
stop_after = after;
|
stop_after = after;
|
||||||
can_replace = replace;
|
can_replace = replace;
|
||||||
delete fn.collapse_scanning;
|
fn.collapse_scanning = false;
|
||||||
if (!abort) return false;
|
if (!abort) return false;
|
||||||
abort = false;
|
abort = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -4919,7 +4919,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== fn) return true;
|
if (node instanceof AST_Scope && node !== fn) return true;
|
||||||
}));
|
}));
|
||||||
delete fn.evaluating;
|
fn.evaluating = false;
|
||||||
if (!found) return;
|
if (!found) return;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -4935,7 +4935,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
});
|
});
|
||||||
fn.evaluating = true;
|
fn.evaluating = true;
|
||||||
val = val._eval(compressor, ignore_side_effects, cached, depth);
|
val = val._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
delete fn.evaluating;
|
fn.evaluating = false;
|
||||||
}
|
}
|
||||||
cached_args.forEach(function(node) {
|
cached_args.forEach(function(node) {
|
||||||
delete node._eval;
|
delete node._eval;
|
||||||
@@ -8055,7 +8055,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
}) && all(fn.argnames, function(argname) {
|
}) && all(fn.argnames, function(argname) {
|
||||||
return !argname.match_symbol(return_false);
|
return !argname.match_symbol(return_false);
|
||||||
}) && !(fn.rest && fn.rest.match_symbol(return_false));
|
}) && !(fn.rest && fn.rest.match_symbol(return_false));
|
||||||
delete fn.new;
|
fn.new = false;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
function drop_class(self, compressor, first_in_statement) {
|
function drop_class(self, compressor, first_in_statement) {
|
||||||
@@ -11640,7 +11640,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
if (self.left instanceof AST_PropAccess) {
|
if (self.left instanceof AST_PropAccess) {
|
||||||
if (self.operator == "=") {
|
if (self.operator == "=") {
|
||||||
if (self.__drop) {
|
if (self.redundant) {
|
||||||
var exprs = [ self.left.expression ];
|
var exprs = [ self.left.expression ];
|
||||||
if (self.left instanceof AST_Sub) exprs.push(self.left.property);
|
if (self.left instanceof AST_Sub) exprs.push(self.left.property);
|
||||||
exprs.push(self.right);
|
exprs.push(self.right);
|
||||||
|
|||||||
@@ -1465,7 +1465,7 @@ function OutputStream(options) {
|
|||||||
parent = output.parent(level++);
|
parent = output.parent(level++);
|
||||||
if (parent instanceof AST_Call && parent.expression === node) return;
|
if (parent instanceof AST_Call && parent.expression === node) return;
|
||||||
} while (parent instanceof AST_PropAccess && parent.expression === node);
|
} 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) {
|
function print_call_args(self, output) {
|
||||||
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
|
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
|
||||||
|
|||||||
@@ -2316,9 +2316,8 @@ function parse($TEXT, options) {
|
|||||||
var comments = start.comments_before;
|
var comments = start.comments_before;
|
||||||
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
|
var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
var match = /[@#]__PURE__/.exec(comments[i].value);
|
if (/[@#]__PURE__/.test(comments[i].value)) {
|
||||||
if (match) {
|
expr.pure = true;
|
||||||
expr.pure = match[0];
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
lib/scope.js
13
lib/scope.js
@@ -44,9 +44,8 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function SymbolDef(id, scope, orig, init) {
|
function SymbolDef(id, scope, orig, init) {
|
||||||
|
this._bits = 0;
|
||||||
this.eliminated = 0;
|
this.eliminated = 0;
|
||||||
this.exported = false;
|
|
||||||
this.global = false;
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.init = init;
|
this.init = init;
|
||||||
this.mangled_name = null;
|
this.mangled_name = null;
|
||||||
@@ -55,7 +54,6 @@ function SymbolDef(id, scope, orig, init) {
|
|||||||
this.references = [];
|
this.references = [];
|
||||||
this.replaced = 0;
|
this.replaced = 0;
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.undeclared = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolDef.prototype = {
|
SymbolDef.prototype = {
|
||||||
@@ -104,6 +102,15 @@ SymbolDef.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DEF_BITPROPS(SymbolDef, [
|
||||||
|
"const_redefs",
|
||||||
|
"cross_loop",
|
||||||
|
"direct_access",
|
||||||
|
"exported",
|
||||||
|
"global",
|
||||||
|
"undeclared",
|
||||||
|
]);
|
||||||
|
|
||||||
var unary_side_effects = makePredicate("delete ++ --");
|
var unary_side_effects = makePredicate("delete ++ --");
|
||||||
|
|
||||||
function is_lhs(node, parent) {
|
function is_lhs(node, parent) {
|
||||||
|
|||||||
18
lib/utils.js
18
lib/utils.js
@@ -273,3 +273,21 @@ function first_in_statement(stack, arrow, export_default) {
|
|||||||
return false;
|
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;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -442,9 +442,9 @@ compress_annotations_disabled_output_annotations_enabled: {
|
|||||||
}
|
}
|
||||||
expect_exact: [
|
expect_exact: [
|
||||||
"/*@__PURE__*/a(3),",
|
"/*@__PURE__*/a(3),",
|
||||||
"/*#__PURE__*/b(5),",
|
"/*@__PURE__*/b(5),",
|
||||||
"c(side_effect),",
|
"c(side_effect),",
|
||||||
"/*#__PURE__*/d(effect());",
|
"/*@__PURE__*/d(effect());",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user