Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c5e23506b | ||
|
|
07f35ea2c9 | ||
|
|
57a9519c3d | ||
|
|
9e4c4c995c | ||
|
|
d11c82f8ca | ||
|
|
bc27966a19 | ||
|
|
8f39491e96 | ||
|
|
065c50ebde | ||
|
|
d2e7c4af20 | ||
|
|
033d8d9405 | ||
|
|
b0799105c2 |
14
bin/uglifyjs
14
bin/uglifyjs
@@ -10,7 +10,9 @@ var info = require("../package.json");
|
||||
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", "in_arg", "inlined", "length_read", "parent_scope", "redef", "scope", "unused" ];
|
||||
var truthy_keys = [ "optional", "pure", "terminal", "uses_arguments", "uses_eval", "uses_with" ];
|
||||
|
||||
var files = {};
|
||||
var options = {};
|
||||
var short_forms = {
|
||||
@@ -430,7 +432,7 @@ function run() {
|
||||
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) {
|
||||
@@ -519,7 +521,7 @@ function read_file(path, default_value) {
|
||||
}
|
||||
|
||||
function parse_js(value, options, flag) {
|
||||
if (!options || typeof options != "object") options = {};
|
||||
if (!options || typeof options != "object") options = Object.create(null);
|
||||
if (typeof value == "string") try {
|
||||
UglifyJS.parse(value, {
|
||||
expression: true
|
||||
@@ -559,8 +561,10 @@ function parse_js(value, options, flag) {
|
||||
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) {
|
||||
|
||||
60
lib/ast.js
60
lib/ast.js
@@ -50,6 +50,8 @@ function DEFNODE(type, props, methods, base) {
|
||||
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) {
|
||||
@@ -135,6 +137,52 @@ var AST_Node = DEFNODE("Node", "start end", {
|
||||
},
|
||||
}, 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;
|
||||
@@ -253,7 +301,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
||||
},
|
||||
}, AST_Statement);
|
||||
|
||||
var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", {
|
||||
var AST_BlockScope = DEFNODE("BlockScope", "_var_names enclosed functions make_def parent_scope variables", {
|
||||
$documentation: "Base class for all statements introducing a lexical scope",
|
||||
$propdoc: {
|
||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||
@@ -484,7 +532,7 @@ var AST_With = DEFNODE("With", "expression", {
|
||||
|
||||
/* -----[ scope and functions ]----- */
|
||||
|
||||
var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
|
||||
var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", {
|
||||
$documentation: "Base class for all statements introducing a lexical scope",
|
||||
$propdoc: {
|
||||
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
||||
@@ -543,13 +591,13 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
}
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
|
||||
var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", {
|
||||
$documentation: "Base class for functions",
|
||||
$propdoc: {
|
||||
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) {
|
||||
@@ -1295,7 +1343,7 @@ var AST_Call = DEFNODE("Call", "args expression optional pure terminal", {
|
||||
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) {
|
||||
@@ -1747,7 +1795,7 @@ var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
||||
$documentation: "Symbol defining a variable",
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
||||
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", "unused", {
|
||||
$documentation: "Symbol naming a function argument",
|
||||
}, AST_SymbolVar);
|
||||
|
||||
|
||||
426
lib/compress.js
426
lib/compress.js
@@ -185,90 +185,89 @@ function Compressor(options, false_by_default) {
|
||||
};
|
||||
}
|
||||
|
||||
Compressor.prototype = new TreeTransformer;
|
||||
merge(Compressor.prototype, {
|
||||
option: function(key) { return this.options[key] },
|
||||
exposed: function(def) {
|
||||
if (def.exported) return true;
|
||||
if (def.undeclared) return true;
|
||||
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
|
||||
var toplevel = this.toplevel;
|
||||
return !all(def.orig, function(sym) {
|
||||
return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
|
||||
});
|
||||
},
|
||||
compress: function(node) {
|
||||
node = node.resolve_defines(this);
|
||||
node.hoist_exports(this);
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(true);
|
||||
}
|
||||
var merge_vars = this.options.merge_vars;
|
||||
var passes = +this.options.passes || 1;
|
||||
var min_count = 1 / 0;
|
||||
var stopping = false;
|
||||
var mangle = { ie: this.option("ie") };
|
||||
for (var pass = 0; pass < passes; pass++) {
|
||||
node.figure_out_scope(mangle);
|
||||
if (pass > 0 || this.option("reduce_vars"))
|
||||
node.reset_opt_flags(this);
|
||||
this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
|
||||
node = node.transform(this);
|
||||
if (passes > 1) {
|
||||
var count = 0;
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
|
||||
pass: pass,
|
||||
min_count: min_count,
|
||||
count: count,
|
||||
});
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
} else if (stopping) {
|
||||
break;
|
||||
} else {
|
||||
stopping = true;
|
||||
}
|
||||
Compressor.prototype = new TreeTransformer(function(node, descend, in_list) {
|
||||
if (node._squeezed) return node;
|
||||
var is_scope = node instanceof AST_Scope;
|
||||
if (is_scope) {
|
||||
node.hoist_properties(this);
|
||||
node.hoist_declarations(this);
|
||||
node.process_boolean_returns(this);
|
||||
}
|
||||
// Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
|
||||
// would call AST_Node.transform() if a different instance of AST_Node is
|
||||
// produced after OPT().
|
||||
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
|
||||
// Migrate and defer all children's AST_Node.transform() to below, which
|
||||
// will now happen after this parent AST_Node has been properly substituted
|
||||
// thus gives a consistent AST snapshot.
|
||||
descend(node, this);
|
||||
// Existing code relies on how AST_Node.optimize() worked, and omitting the
|
||||
// following replacement call would result in degraded efficiency of both
|
||||
// output and performance.
|
||||
descend(node, this);
|
||||
var opt = node.optimize(this);
|
||||
if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
|
||||
opt.drop_unused(this);
|
||||
if (opt.merge_variables(this)) opt.drop_unused(this);
|
||||
descend(opt, this);
|
||||
}
|
||||
if (opt === node) opt._squeezed = true;
|
||||
return opt;
|
||||
});
|
||||
Compressor.prototype.option = function(key) {
|
||||
return this.options[key];
|
||||
};
|
||||
Compressor.prototype.exposed = function(def) {
|
||||
if (def.exported) return true;
|
||||
if (def.undeclared) return true;
|
||||
if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
|
||||
var toplevel = this.toplevel;
|
||||
return !all(def.orig, function(sym) {
|
||||
return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
|
||||
});
|
||||
};
|
||||
Compressor.prototype.compress = function(node) {
|
||||
node = node.resolve_defines(this);
|
||||
node.hoist_exports(this);
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(true);
|
||||
}
|
||||
var merge_vars = this.options.merge_vars;
|
||||
var passes = +this.options.passes || 1;
|
||||
var min_count = 1 / 0;
|
||||
var stopping = false;
|
||||
var mangle = { ie: this.option("ie") };
|
||||
for (var pass = 0; pass < passes; pass++) {
|
||||
node.figure_out_scope(mangle);
|
||||
if (pass > 0 || this.option("reduce_vars"))
|
||||
node.reset_opt_flags(this);
|
||||
this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
|
||||
node = node.transform(this);
|
||||
if (passes > 1) {
|
||||
var count = 0;
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
|
||||
pass: pass,
|
||||
min_count: min_count,
|
||||
count: count,
|
||||
});
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
} else if (stopping) {
|
||||
break;
|
||||
} else {
|
||||
stopping = true;
|
||||
}
|
||||
}
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(false);
|
||||
}
|
||||
return node;
|
||||
},
|
||||
before: function(node, descend, in_list) {
|
||||
if (node._squeezed) return node;
|
||||
var is_scope = node instanceof AST_Scope;
|
||||
if (is_scope) {
|
||||
node.hoist_properties(this);
|
||||
node.hoist_declarations(this);
|
||||
node.process_boolean_returns(this);
|
||||
}
|
||||
// Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
|
||||
// would call AST_Node.transform() if a different instance of AST_Node is
|
||||
// produced after OPT().
|
||||
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
|
||||
// Migrate and defer all children's AST_Node.transform() to below, which
|
||||
// will now happen after this parent AST_Node has been properly substituted
|
||||
// thus gives a consistent AST snapshot.
|
||||
descend(node, this);
|
||||
// Existing code relies on how AST_Node.optimize() worked, and omitting the
|
||||
// following replacement call would result in degraded efficiency of both
|
||||
// output and performance.
|
||||
descend(node, this);
|
||||
var opt = node.optimize(this);
|
||||
if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
|
||||
opt.drop_unused(this);
|
||||
if (opt.merge_variables(this)) opt.drop_unused(this);
|
||||
descend(opt, this);
|
||||
}
|
||||
if (opt === node) opt._squeezed = true;
|
||||
return opt;
|
||||
}
|
||||
});
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(false);
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
(function(OPT) {
|
||||
OPT(AST_Node, function(self, compressor) {
|
||||
@@ -532,6 +531,11 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
}
|
||||
|
||||
function safe_to_visit(tw, fn) {
|
||||
var marker = fn.safe_ids;
|
||||
return marker === undefined || marker === tw.safe_ids;
|
||||
}
|
||||
|
||||
function walk_fn_def(tw, fn) {
|
||||
var was_scanning = tw.fn_scanning;
|
||||
tw.fn_scanning = fn;
|
||||
@@ -546,14 +550,14 @@ merge(Compressor.prototype, {
|
||||
d.single_use = false;
|
||||
var fixed = d.fixed;
|
||||
if (typeof fixed == "function") fixed = fixed();
|
||||
if (fixed instanceof AST_Lambda && HOP(fixed, "safe_ids")) return;
|
||||
if (fixed instanceof AST_Lambda && fixed.safe_ids !== undefined) return;
|
||||
d.fixed = false;
|
||||
});
|
||||
}
|
||||
|
||||
function mark_fn_def(tw, def, fn) {
|
||||
if (!HOP(fn, "safe_ids")) return;
|
||||
var marker = fn.safe_ids;
|
||||
if (marker === undefined) return;
|
||||
if (marker === false) return;
|
||||
if (fn.parent_scope.resolve().may_call_this === return_true) {
|
||||
if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
|
||||
@@ -588,10 +592,10 @@ merge(Compressor.prototype, {
|
||||
walk_fn_def(tw, fn);
|
||||
});
|
||||
fn_defs.forEach(function(fn) {
|
||||
delete fn.safe_ids;
|
||||
fn.safe_ids = undefined;
|
||||
});
|
||||
delete scope.fn_defs;
|
||||
delete scope.may_call_this;
|
||||
scope.fn_defs = undefined;
|
||||
scope.may_call_this = undefined;
|
||||
}
|
||||
|
||||
function push(tw) {
|
||||
@@ -622,7 +626,7 @@ merge(Compressor.prototype, {
|
||||
if (def.global && def.name == "arguments") return false;
|
||||
tw.loop_ids[def.id] = null;
|
||||
def.fixed = make_node(AST_Undefined, def.orig[0]);
|
||||
if (in_order) delete def.safe_ids;
|
||||
if (in_order) def.safe_ids = undefined;
|
||||
return true;
|
||||
}
|
||||
return !safe.assign || safe.assign === tw.safe_ids;
|
||||
@@ -644,7 +648,7 @@ merge(Compressor.prototype, {
|
||||
var safe = tw.safe_ids[def.id];
|
||||
if (def.safe_ids) {
|
||||
def.safe_ids[def.id] = false;
|
||||
delete def.safe_ids;
|
||||
def.safe_ids = undefined;
|
||||
return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
|
||||
}
|
||||
if (!HOP(tw.safe_ids, def.id)) {
|
||||
@@ -899,7 +903,7 @@ merge(Compressor.prototype, {
|
||||
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) {
|
||||
@@ -1207,7 +1211,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Lambda, function(tw, descend, compressor) {
|
||||
var fn = this;
|
||||
if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
|
||||
if (!safe_to_visit(tw, fn)) return true;
|
||||
if (!push_uniq(tw.fn_visited, fn)) return true;
|
||||
fn.inlined = false;
|
||||
push(tw);
|
||||
@@ -1222,7 +1226,7 @@ merge(Compressor.prototype, {
|
||||
var def = fn.name.definition();
|
||||
var parent = tw.parent();
|
||||
if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
|
||||
if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
|
||||
if (!safe_to_visit(tw, fn)) return true;
|
||||
if (!push_uniq(tw.fn_visited, fn)) return true;
|
||||
fn.inlined = false;
|
||||
push(tw);
|
||||
@@ -1470,8 +1474,8 @@ merge(Compressor.prototype, {
|
||||
function reset_flags(node) {
|
||||
node._squeezed = false;
|
||||
node._optimized = false;
|
||||
delete node.fixed;
|
||||
if (node instanceof AST_Scope) delete node._var_names;
|
||||
if (node instanceof AST_BlockScope) node._var_names = undefined;
|
||||
if (node instanceof AST_SymbolRef) node.fixed = undefined;
|
||||
}
|
||||
|
||||
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
|
||||
@@ -1872,9 +1876,9 @@ merge(Compressor.prototype, {
|
||||
function collapse(statements, compressor) {
|
||||
if (scope.pinned()) return statements;
|
||||
var args;
|
||||
var assignments = Object.create(null);
|
||||
var assignments = new Dictionary();
|
||||
var candidates = [];
|
||||
var declare_only = Object.create(null);
|
||||
var declare_only = new Dictionary();
|
||||
var force_single;
|
||||
var stat_index = statements.length;
|
||||
var scanner = new TreeTransformer(function(node, descend) {
|
||||
@@ -2275,7 +2279,7 @@ merge(Compressor.prototype, {
|
||||
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;
|
||||
@@ -2403,7 +2407,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
args = iife.args.slice();
|
||||
var len = args.length;
|
||||
var names = Object.create(null);
|
||||
var names = new Dictionary();
|
||||
for (var i = fn.argnames.length; --i >= 0;) {
|
||||
var sym = fn.argnames[i];
|
||||
var arg = args[i];
|
||||
@@ -2418,8 +2422,8 @@ merge(Compressor.prototype, {
|
||||
candidates.length = 0;
|
||||
break;
|
||||
}
|
||||
if (sym.name in names) continue;
|
||||
names[sym.name] = true;
|
||||
if (names.has(sym.name)) continue;
|
||||
names.set(sym.name, true);
|
||||
if (value) arg = !arg || is_undefined(arg) ? value : null;
|
||||
if (!arg && !value) {
|
||||
arg = make_node(AST_Undefined, sym).transform(compressor);
|
||||
@@ -2452,7 +2456,7 @@ merge(Compressor.prototype, {
|
||||
extract_candidates(lhs);
|
||||
extract_candidates(expr.right);
|
||||
if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
|
||||
assignments[lhs.name] = (assignments[lhs.name] || 0) + 1;
|
||||
assignments.set(lhs.name, (assignments.get(lhs.name) || 0) + 1);
|
||||
}
|
||||
} else if (expr instanceof AST_Await) {
|
||||
extract_candidates(expr.expression, unused);
|
||||
@@ -2544,7 +2548,7 @@ merge(Compressor.prototype, {
|
||||
candidates.push(hit_stack.slice());
|
||||
}
|
||||
} else {
|
||||
declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
|
||||
declare_only.set(expr.name.name, (declare_only.get(expr.name.name) || 0) + 1);
|
||||
}
|
||||
}
|
||||
if (expr.value) extract_candidates(expr.value);
|
||||
@@ -2760,7 +2764,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function remaining_refs(def) {
|
||||
return def.references.length - def.replaced - (assignments[def.name] || 0);
|
||||
return def.references.length - def.replaced - (assignments.get(def.name) || 0);
|
||||
}
|
||||
|
||||
function get_lhs(expr) {
|
||||
@@ -2792,7 +2796,7 @@ merge(Compressor.prototype, {
|
||||
if (def.const_redefs) return;
|
||||
if (!member(lhs, def.orig)) return;
|
||||
if (scope.uses_arguments && is_funarg(def)) return;
|
||||
var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0);
|
||||
var declared = def.orig.length - def.eliminated - (declare_only.get(def.name) || 0);
|
||||
remaining = remaining_refs(def);
|
||||
if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
|
||||
if (!ref.fixed) return true;
|
||||
@@ -3001,7 +3005,7 @@ merge(Compressor.prototype, {
|
||||
if (hit_index <= end) return handle_custom_scan_order(node, tt);
|
||||
hit = true;
|
||||
if (node instanceof AST_VarDef) {
|
||||
declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1;
|
||||
declare_only.set(node.name.name, (declare_only.get(node.name.name) || 0) + 1);
|
||||
if (value_def) value_def.replaced++;
|
||||
node = node.clone();
|
||||
node.value = null;
|
||||
@@ -3626,10 +3630,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function trim_assigns(name, value, exprs) {
|
||||
var names = Object.create(null);
|
||||
names[name.name] = true;
|
||||
var names = new Dictionary();
|
||||
names.set(name.name, true);
|
||||
while (value instanceof AST_Assign && value.operator == "=") {
|
||||
if (value.left instanceof AST_SymbolRef) names[value.left.name] = true;
|
||||
if (value.left instanceof AST_SymbolRef) names.set(value.left.name, true);
|
||||
value = value.right;
|
||||
}
|
||||
if (!(value instanceof AST_Object)) return;
|
||||
@@ -3647,7 +3651,7 @@ merge(Compressor.prototype, {
|
||||
if (!(node.left instanceof AST_PropAccess)) return;
|
||||
var sym = node.left.expression;
|
||||
if (!(sym instanceof AST_SymbolRef)) return;
|
||||
if (!(sym.name in names)) return;
|
||||
if (!names.has(sym.name)) return;
|
||||
if (!node.right.is_constant_expression(scope)) return;
|
||||
var prop = node.left.property;
|
||||
if (prop instanceof AST_Node) {
|
||||
@@ -4618,7 +4622,6 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return this;
|
||||
});
|
||||
var nonsafe_props = makePredicate("__proto__ toString valueOf");
|
||||
def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var val = {};
|
||||
@@ -4630,7 +4633,12 @@ merge(Compressor.prototype, {
|
||||
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||
if (key === prop.key) return this;
|
||||
}
|
||||
if (nonsafe_props[key]) return this;
|
||||
switch (key) {
|
||||
case "__proto__":
|
||||
case "toString":
|
||||
case "valueOf":
|
||||
return this;
|
||||
}
|
||||
val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
|
||||
if (val[key] === prop.value) return this;
|
||||
}
|
||||
@@ -4916,7 +4924,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== fn) return true;
|
||||
}));
|
||||
delete fn.evaluating;
|
||||
fn.evaluating = false;
|
||||
if (!found) return;
|
||||
}
|
||||
return this;
|
||||
@@ -4932,7 +4940,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
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;
|
||||
@@ -6589,7 +6597,7 @@ merge(Compressor.prototype, {
|
||||
argnames.pop();
|
||||
} else if (i > default_length) {
|
||||
log(sym.name, "Dropping unused default argument assignment {name}");
|
||||
sym.name.__unused = true;
|
||||
sym.name.unused = true;
|
||||
argnames[i] = sym.name;
|
||||
} else {
|
||||
log(sym.name, "Dropping unused default argument value {name}");
|
||||
@@ -6600,12 +6608,12 @@ merge(Compressor.prototype, {
|
||||
var def = sym.definition();
|
||||
if (def.id in in_use_ids) {
|
||||
trim = false;
|
||||
if (indexOf_assign(def, sym) < 0) sym.__unused = null;
|
||||
if (indexOf_assign(def, sym) < 0) sym.unused = null;
|
||||
} else if (trim) {
|
||||
log(sym, "Dropping unused function argument {name}");
|
||||
argnames.pop();
|
||||
} else {
|
||||
sym.__unused = true;
|
||||
sym.unused = true;
|
||||
}
|
||||
}
|
||||
fns_with_marked_args.push(node);
|
||||
@@ -6651,7 +6659,7 @@ merge(Compressor.prototype, {
|
||||
if (value && indexOf_assign(sym, def) < 0) {
|
||||
value = value.drop_side_effect_free(compressor);
|
||||
if (value) {
|
||||
AST_Node.warn("Side effects in last use of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
AST_Node.warn("Side effects in definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
side_effects.push(value);
|
||||
}
|
||||
value = null;
|
||||
@@ -6815,7 +6823,7 @@ merge(Compressor.prototype, {
|
||||
var assign = make_node(AST_Assign, def, {
|
||||
operator: "=",
|
||||
left: ref,
|
||||
right: def.value
|
||||
right: def.value,
|
||||
});
|
||||
var index = indexOf_assign(sym, def);
|
||||
if (index >= 0) assign_in_use[sym.id][index] = assign;
|
||||
@@ -7159,7 +7167,7 @@ merge(Compressor.prototype, {
|
||||
var value = node.value.drop_side_effect_free(compressor);
|
||||
if (!value) return null;
|
||||
log(node.name, "Side effects in default value of unused variable {name}");
|
||||
node.name.__unused = null;
|
||||
node.name.unused = null;
|
||||
node.value = value;
|
||||
}
|
||||
return node;
|
||||
@@ -7268,7 +7276,7 @@ merge(Compressor.prototype, {
|
||||
var prop_keys, prop_map;
|
||||
if (value instanceof AST_Object) {
|
||||
prop_keys = [];
|
||||
prop_map = Object.create(null);
|
||||
prop_map = new Dictionary();
|
||||
value.properties.forEach(function(prop, index) {
|
||||
if (prop instanceof AST_Spread) return prop_map = false;
|
||||
var key = prop.key;
|
||||
@@ -7276,7 +7284,7 @@ merge(Compressor.prototype, {
|
||||
if (key instanceof AST_Node) {
|
||||
prop_map = false;
|
||||
} else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
|
||||
prop_map[key] = prop;
|
||||
prop_map.set(key, prop);
|
||||
}
|
||||
prop_keys[index] = key;
|
||||
});
|
||||
@@ -7285,8 +7293,8 @@ merge(Compressor.prototype, {
|
||||
value = false;
|
||||
node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
|
||||
}
|
||||
var can_drop = Object.create(null);
|
||||
var drop_keys = drop && Object.create(null);
|
||||
var can_drop = new Dictionary();
|
||||
var drop_keys = drop && new Dictionary();
|
||||
var properties = [];
|
||||
node.properties.map(function(prop) {
|
||||
var key = prop.key;
|
||||
@@ -7297,7 +7305,7 @@ merge(Compressor.prototype, {
|
||||
if (key instanceof AST_Node) {
|
||||
drop_keys = false;
|
||||
} else {
|
||||
can_drop[key] = !(key in can_drop);
|
||||
can_drop.set(key, !can_drop.has(key));
|
||||
}
|
||||
return key;
|
||||
}).forEach(function(key, index) {
|
||||
@@ -7307,8 +7315,8 @@ merge(Compressor.prototype, {
|
||||
value = false;
|
||||
trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
|
||||
} else {
|
||||
drop = drop_keys && can_drop[key];
|
||||
var mapped = prop_map && prop_map[key];
|
||||
drop = drop_keys && can_drop.get(key);
|
||||
var mapped = prop_map && prop_map.get(key);
|
||||
if (mapped) {
|
||||
value = mapped.value;
|
||||
if (value instanceof AST_Accessor) value = false;
|
||||
@@ -7318,21 +7326,21 @@ merge(Compressor.prototype, {
|
||||
trimmed = prop.value.transform(trimmer);
|
||||
if (!trimmed) {
|
||||
if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
|
||||
if (drop_keys && !(key in drop_keys)) {
|
||||
if (drop_keys && !drop_keys.has(key)) {
|
||||
if (mapped) {
|
||||
drop_keys[key] = mapped;
|
||||
drop_keys.set(key, mapped);
|
||||
if (value === null) {
|
||||
prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
|
||||
prop_map.set(key, retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
|
||||
key: mapped.key,
|
||||
value: make_node(AST_Number, mapped, { value: 0 }),
|
||||
});
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
drop_keys[key] = true;
|
||||
drop_keys.set(key, true);
|
||||
}
|
||||
}
|
||||
} else if (drop_keys) {
|
||||
drop_keys[key] = false;
|
||||
drop_keys.set(key, false);
|
||||
}
|
||||
if (value) mapped.value = value;
|
||||
}
|
||||
@@ -7347,10 +7355,10 @@ merge(Compressor.prototype, {
|
||||
if (prop instanceof AST_Spread) return prop;
|
||||
var key = prop_keys[index];
|
||||
if (key instanceof AST_Node) return prop;
|
||||
if (key in drop_keys) {
|
||||
var mapped = drop_keys[key];
|
||||
if (drop_keys.has(key)) {
|
||||
var mapped = drop_keys.get(key);
|
||||
if (!mapped) return prop;
|
||||
if (mapped === prop) return prop_map[key] || List.skip;
|
||||
if (mapped === prop) return prop_map.get(key) || List.skip;
|
||||
} else if (node.rest) {
|
||||
return prop;
|
||||
}
|
||||
@@ -7416,7 +7424,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return make_node(AST_DestructuredObject, node, { properties: [] });
|
||||
}
|
||||
node.__unused = null;
|
||||
node.unused = null;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
@@ -7443,7 +7451,7 @@ merge(Compressor.prototype, {
|
||||
if (var_decl <= 1) hoist_vars = false;
|
||||
}
|
||||
if (!hoist_funs && !hoist_vars) return;
|
||||
var consts = Object.create(null);
|
||||
var consts = new Dictionary();
|
||||
var dirs = [];
|
||||
var hoisted = [];
|
||||
var vars = new Dictionary(), vars_found = 0;
|
||||
@@ -7469,7 +7477,7 @@ merge(Compressor.prototype, {
|
||||
if (!all(node.definitions, function(defn) {
|
||||
var sym = defn.name;
|
||||
return sym instanceof AST_SymbolVar
|
||||
&& !consts[sym.name]
|
||||
&& !consts.has(sym.name)
|
||||
&& self.find_variable(sym.name) === sym.definition();
|
||||
})) return node;
|
||||
node.definitions.forEach(function(def) {
|
||||
@@ -7488,7 +7496,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (node instanceof AST_Scope) return node;
|
||||
if (node instanceof AST_SymbolConst) {
|
||||
consts[node.name] = true;
|
||||
consts.set(node.name, true);
|
||||
return node;
|
||||
}
|
||||
});
|
||||
@@ -7650,12 +7658,12 @@ merge(Compressor.prototype, {
|
||||
AST_BlockScope.DEFMETHOD("var_names", function() {
|
||||
var var_names = this._var_names;
|
||||
if (!var_names) {
|
||||
this._var_names = var_names = Object.create(null);
|
||||
this._var_names = var_names = new Dictionary();
|
||||
this.enclosed.forEach(function(def) {
|
||||
var_names[def.name] = true;
|
||||
var_names.set(def.name, true);
|
||||
});
|
||||
this.variables.each(function(def, name) {
|
||||
var_names[name] = true;
|
||||
var_names.set(name, true);
|
||||
});
|
||||
}
|
||||
return var_names;
|
||||
@@ -7674,7 +7682,7 @@ merge(Compressor.prototype, {
|
||||
prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
|
||||
var name = prefix;
|
||||
for (var i = 0; !all(scopes, function(scope) {
|
||||
return !scope.var_names()[name];
|
||||
return !scope.var_names().has(name);
|
||||
}); i++) name = prefix + "$" + i;
|
||||
var sym = make_node(type, orig, {
|
||||
name: name,
|
||||
@@ -7683,7 +7691,7 @@ merge(Compressor.prototype, {
|
||||
var def = this.def_variable(sym);
|
||||
scopes.forEach(function(scope) {
|
||||
scope.enclosed.push(def);
|
||||
scope.var_names()[name] = true;
|
||||
scope.var_names().set(name, true);
|
||||
});
|
||||
return sym;
|
||||
});
|
||||
@@ -8052,7 +8060,7 @@ merge(Compressor.prototype, {
|
||||
}) && 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) {
|
||||
@@ -9142,7 +9150,7 @@ merge(Compressor.prototype, {
|
||||
var scope = def.scope.resolve();
|
||||
for (var s = def.scope; s !== scope;) {
|
||||
s = s.parent_scope;
|
||||
if (s.var_names()[def.name]) return true;
|
||||
if (s.var_names().has(def.name)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9158,7 +9166,7 @@ merge(Compressor.prototype, {
|
||||
def.scope = scope;
|
||||
scope.variables.set(def.name, def);
|
||||
scope.enclosed.push(def);
|
||||
scope.var_names()[def.name] = true;
|
||||
scope.var_names().set(def.name, true);
|
||||
}),
|
||||
value: defn.value,
|
||||
});
|
||||
@@ -9244,7 +9252,7 @@ merge(Compressor.prototype, {
|
||||
if (argname instanceof AST_DestructuredObject) {
|
||||
return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
|
||||
}
|
||||
return argname.__unused;
|
||||
return argname.unused;
|
||||
} : return_false;
|
||||
var side_effects = [];
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
@@ -9252,7 +9260,7 @@ merge(Compressor.prototype, {
|
||||
if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
|
||||
argnames[i] = argname = argname.name;
|
||||
}
|
||||
if (!argname || "__unused" in argname) {
|
||||
if (!argname || argname.unused !== undefined) {
|
||||
var node = args[i].drop_side_effect_free(compressor);
|
||||
if (drop_fargs(argname)) {
|
||||
if (argname) argnames.splice(i, 1);
|
||||
@@ -9959,30 +9967,33 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function var_exists(defined, name) {
|
||||
return defined[name] || identifier_atom[name] || scope.var_names()[name];
|
||||
return defined.has(name) || identifier_atom[name] || scope.var_names().has(name);
|
||||
}
|
||||
|
||||
function can_inject_args(defined, used, safe_to_inject) {
|
||||
function can_inject_args(defined, safe_to_inject) {
|
||||
var abort = false;
|
||||
fn.each_argname(function(arg) {
|
||||
if (abort) return;
|
||||
if (arg.__unused) return;
|
||||
if (arg.unused) return;
|
||||
if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
|
||||
used[arg.name] = true;
|
||||
arg_used.set(arg.name, true);
|
||||
if (in_loop) in_loop.push(arg.definition());
|
||||
});
|
||||
return !abort;
|
||||
}
|
||||
|
||||
function can_inject_vars(defined, used, safe_to_inject) {
|
||||
function can_inject_vars(defined, safe_to_inject) {
|
||||
for (var i = 0; i < fn.body.length; i++) {
|
||||
var stat = fn.body[i];
|
||||
if (stat instanceof AST_LambdaDefinition) {
|
||||
if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
|
||||
var name = stat.name;
|
||||
if (!safe_to_inject) return false;
|
||||
if (arg_used.has(name.name)) return false;
|
||||
if (var_exists(defined, name.name)) return false;
|
||||
if (!all(stat.enclosed, function(def) {
|
||||
return def.scope === stat || !defined[def.name];
|
||||
return def.scope === stat || !defined.has(def.name);
|
||||
})) return false;
|
||||
if (in_loop) in_loop.push(stat.name.definition());
|
||||
if (in_loop) in_loop.push(name.definition());
|
||||
continue;
|
||||
}
|
||||
if (!(stat instanceof AST_Var)) continue;
|
||||
@@ -9997,12 +10008,12 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function can_inject_symbols() {
|
||||
var defined = Object.create(null);
|
||||
var defined = new Dictionary();
|
||||
var level = 0, child;
|
||||
scope = current;
|
||||
do {
|
||||
if (scope.variables) scope.variables.each(function(def) {
|
||||
defined[def.name] = true;
|
||||
defined.set(def.name, true);
|
||||
});
|
||||
child = scope;
|
||||
scope = compressor.parent(level++);
|
||||
@@ -10025,23 +10036,22 @@ merge(Compressor.prototype, {
|
||||
var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
|
||||
if (scope instanceof AST_Toplevel) {
|
||||
if (compressor.toplevel.vars) {
|
||||
defined["arguments"] = true;
|
||||
defined.set("arguments", true);
|
||||
} else {
|
||||
safe_to_inject = false;
|
||||
}
|
||||
}
|
||||
arg_used = new Dictionary();
|
||||
var inline = compressor.option("inline");
|
||||
arg_used = Object.create(defined);
|
||||
if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
|
||||
var used = Object.create(arg_used);
|
||||
if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
|
||||
if (!can_inject_args(defined, inline >= 2 && safe_to_inject)) return false;
|
||||
if (!can_inject_vars(defined, inline >= 3 && safe_to_inject)) return false;
|
||||
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
|
||||
}
|
||||
|
||||
function append_var(decls, expressions, name, value) {
|
||||
var def = name.definition();
|
||||
if (!scope.var_names()[name.name]) {
|
||||
scope.var_names()[name.name] = true;
|
||||
if (!scope.var_names().has(name.name)) {
|
||||
scope.var_names().set(name.name, true);
|
||||
decls.push(make_node(AST_VarDef, name, {
|
||||
name: name,
|
||||
value: null,
|
||||
@@ -10075,12 +10085,12 @@ merge(Compressor.prototype, {
|
||||
name = argname;
|
||||
}
|
||||
var value = self.args[i];
|
||||
if (name.__unused || scope.var_names()[name.name]) {
|
||||
if (name.unused || scope.var_names().has(name.name)) {
|
||||
if (value) expressions.push(value);
|
||||
} else {
|
||||
var symbol = make_node(AST_SymbolVar, name, name);
|
||||
name.definition().orig.push(symbol);
|
||||
if ("__unused" in name) {
|
||||
if (name.unused !== undefined) {
|
||||
append_var(decls, expressions, symbol);
|
||||
if (value) expressions.push(value);
|
||||
} else {
|
||||
@@ -10093,7 +10103,7 @@ merge(Compressor.prototype, {
|
||||
expressions.reverse();
|
||||
for (i = default_args.length; --i >= 0;) {
|
||||
var node = default_args[i];
|
||||
if ("__unused" in node.name) {
|
||||
if (node.name.unused !== undefined) {
|
||||
expressions.push(node.value);
|
||||
} else {
|
||||
var sym = make_node(AST_SymbolRef, node.name, node.name);
|
||||
@@ -10112,7 +10122,7 @@ merge(Compressor.prototype, {
|
||||
operator: "=",
|
||||
left: make_node(AST_DestructuredArray, self, {
|
||||
elements: fn.argnames.map(function(argname) {
|
||||
if (argname.__unused) return make_node(AST_Hole, argname);
|
||||
if (argname.unused) return make_node(AST_Hole, argname);
|
||||
return argname.convert_symbol(AST_SymbolRef, process);
|
||||
}),
|
||||
rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
|
||||
@@ -10153,7 +10163,7 @@ merge(Compressor.prototype, {
|
||||
scope.functions.set(def.name, def);
|
||||
scope.variables.set(def.name, def);
|
||||
scope.enclosed.push(def);
|
||||
scope.var_names()[def.name] = true;
|
||||
scope.var_names().set(def.name, true);
|
||||
args.push(stat);
|
||||
}
|
||||
continue;
|
||||
@@ -10163,7 +10173,7 @@ merge(Compressor.prototype, {
|
||||
var var_def = stat.definitions[j];
|
||||
var name = flatten_var(var_def.name);
|
||||
append_var(decl_var, expr_var, name, var_def.value);
|
||||
if (in_loop && !HOP(arg_used, name.name)) {
|
||||
if (in_loop && !arg_used.has(name.name)) {
|
||||
var def = fn.variables.get(name.name);
|
||||
var sym = make_node(AST_SymbolRef, name, name);
|
||||
def.references.push(sym);
|
||||
@@ -10196,9 +10206,9 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
[].splice.apply(scope.body, args);
|
||||
fn.enclosed.forEach(function(def) {
|
||||
if (scope.var_names()[def.name]) return;
|
||||
if (scope.var_names().has(def.name)) return;
|
||||
scope.enclosed.push(def);
|
||||
scope.var_names()[def.name] = true;
|
||||
scope.var_names().set(def.name, true);
|
||||
});
|
||||
return expressions;
|
||||
}
|
||||
@@ -11320,9 +11330,9 @@ merge(Compressor.prototype, {
|
||||
var scope = self.scope.resolve();
|
||||
fixed.enclosed.forEach(function(def) {
|
||||
if (fixed.variables.has(def.name)) return;
|
||||
if (scope.var_names()[def.name]) return;
|
||||
if (scope.var_names().has(def.name)) return;
|
||||
scope.enclosed.push(def);
|
||||
scope.var_names()[def.name] = true;
|
||||
scope.var_names().set(def.name, true);
|
||||
});
|
||||
}
|
||||
var value;
|
||||
@@ -11635,7 +11645,7 @@ merge(Compressor.prototype, {
|
||||
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);
|
||||
@@ -12302,7 +12312,7 @@ merge(Compressor.prototype, {
|
||||
if (assigned) def.reassigned--;
|
||||
var sym = make_node(AST_SymbolRef, self, argname);
|
||||
sym.reference();
|
||||
delete argname.__unused;
|
||||
argname.unused = undefined;
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
@@ -12380,10 +12390,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
AST_Arrow.DEFMETHOD("contains_super", return_false);
|
||||
AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
|
||||
AST_Lambda.DEFMETHOD("contains_super", function() {
|
||||
var result;
|
||||
AST_LambdaExpression.DEFMETHOD("contains_super", function() {
|
||||
var result = false;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (result) return true;
|
||||
@@ -12392,20 +12400,26 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
return result;
|
||||
});
|
||||
AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
|
||||
AST_Scope.DEFMETHOD("contains_super", return_false);
|
||||
|
||||
AST_Arrow.DEFMETHOD("contains_this", return_false);
|
||||
AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
|
||||
AST_Node.DEFMETHOD("contains_this", function() {
|
||||
var result;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (result) return true;
|
||||
if (node instanceof AST_This) return result = true;
|
||||
if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
|
||||
}));
|
||||
return result;
|
||||
// contains_this()
|
||||
// returns false only if context bound by the specified scope (or scope
|
||||
// containing the specified expression) is not referenced by `this`
|
||||
(function(def) {
|
||||
// scope of arrow function cannot bind to any context
|
||||
def(AST_Arrow, return_false);
|
||||
def(AST_AsyncArrow, return_false);
|
||||
def(AST_Node, function() {
|
||||
var result = false;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (result) return true;
|
||||
if (node instanceof AST_This) return result = true;
|
||||
if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
|
||||
}));
|
||||
return result;
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("contains_this", func);
|
||||
});
|
||||
|
||||
function can_hoist_property(prop) {
|
||||
@@ -12546,7 +12560,8 @@ merge(Compressor.prototype, {
|
||||
var found = false;
|
||||
var generated = false;
|
||||
var keep_duplicate = compressor.has_directive("use strict");
|
||||
var keys = new Dictionary();
|
||||
var keys = [];
|
||||
var map = new Dictionary();
|
||||
var values = [];
|
||||
self.properties.forEach(function(prop) {
|
||||
if (!(prop instanceof AST_Spread)) return process(prop);
|
||||
@@ -12589,19 +12604,27 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Object, self, { properties: values });
|
||||
|
||||
function flush() {
|
||||
keys.each(function(props) {
|
||||
if (props.length == 1) return values.push(props[0]);
|
||||
keys.forEach(function(key) {
|
||||
var props = map.get(key);
|
||||
switch (props.length) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
return values.push(props[0]);
|
||||
}
|
||||
changed = true;
|
||||
var tail = keep_duplicate && !generated && props.pop();
|
||||
values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
|
||||
key: props[0].key,
|
||||
value: make_sequence(self, props.map(function(prop) {
|
||||
return prop.value;
|
||||
}))
|
||||
})),
|
||||
}));
|
||||
if (tail) values.push(tail);
|
||||
props.length = 0;
|
||||
});
|
||||
keys = new Dictionary();
|
||||
keys = [];
|
||||
map = new Dictionary();
|
||||
}
|
||||
|
||||
function process(prop) {
|
||||
@@ -12617,14 +12640,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (can_hoist_property(prop)) {
|
||||
if (prop.value.has_side_effects(compressor)) flush();
|
||||
keys.add(key, prop);
|
||||
keys.push(key);
|
||||
map.add(key, prop);
|
||||
} else {
|
||||
flush();
|
||||
values.push(prop);
|
||||
}
|
||||
if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
|
||||
generated = true;
|
||||
if (keys.has(key)) prop = keys.get(key)[0];
|
||||
if (map.has(key)) prop = map.get(key)[0];
|
||||
prop.key = make_node(AST_Number, prop, { value: +key });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1465,7 +1465,7 @@ function OutputStream(options) {
|
||||
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) {
|
||||
|
||||
@@ -2316,9 +2316,8 @@ function parse($TEXT, options) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"use strict";
|
||||
|
||||
var builtins = function() {
|
||||
var names = [];
|
||||
var names = new Dictionary();
|
||||
// NaN will be included due to Number.NaN
|
||||
[
|
||||
"null",
|
||||
@@ -72,10 +72,10 @@ var builtins = function() {
|
||||
Object.getOwnPropertyNames(ctor.prototype).map(add);
|
||||
}
|
||||
});
|
||||
return makePredicate(names);
|
||||
return names;
|
||||
|
||||
function add(name) {
|
||||
names.push(name);
|
||||
names.set(name, true);
|
||||
}
|
||||
}();
|
||||
|
||||
@@ -116,9 +116,9 @@ function mangle_properties(ast, options) {
|
||||
reserved: null,
|
||||
}, true);
|
||||
|
||||
var reserved = Object.create(options.builtins ? null : builtins);
|
||||
var reserved = options.builtins ? new Dictionary() : builtins.clone();
|
||||
if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
|
||||
reserved[name] = true;
|
||||
reserved.set(name, true);
|
||||
});
|
||||
|
||||
var cname = -1;
|
||||
@@ -126,7 +126,7 @@ function mangle_properties(ast, options) {
|
||||
if (options.cache) {
|
||||
cache = options.cache.props;
|
||||
cache.each(function(name) {
|
||||
reserved[name] = true;
|
||||
reserved.set(name, true);
|
||||
});
|
||||
} else {
|
||||
cache = new Dictionary();
|
||||
@@ -141,8 +141,8 @@ function mangle_properties(ast, options) {
|
||||
var debug_suffix;
|
||||
if (debug) debug_suffix = options.debug === true ? "" : options.debug;
|
||||
|
||||
var names_to_mangle = Object.create(null);
|
||||
var unmangleable = Object.create(reserved);
|
||||
var names_to_mangle = new Dictionary();
|
||||
var unmangleable = reserved.clone();
|
||||
|
||||
// step 1: find candidates to mangle
|
||||
ast.walk(new TreeWalker(function(node) {
|
||||
@@ -211,20 +211,20 @@ function mangle_properties(ast, options) {
|
||||
// only function declarations after this line
|
||||
|
||||
function can_mangle(name) {
|
||||
if (unmangleable[name]) return false;
|
||||
if (unmangleable.has(name)) return false;
|
||||
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function should_mangle(name) {
|
||||
if (reserved[name]) return false;
|
||||
if (reserved.has(name)) return false;
|
||||
if (regex && !regex.test(name)) return false;
|
||||
return cache.has(name) || names_to_mangle[name];
|
||||
return cache.has(name) || names_to_mangle.has(name);
|
||||
}
|
||||
|
||||
function add(name) {
|
||||
if (can_mangle(name)) names_to_mangle[name] = true;
|
||||
if (!should_mangle(name)) unmangleable[name] = true;
|
||||
if (can_mangle(name)) names_to_mangle.set(name, true);
|
||||
if (!should_mangle(name)) unmangleable.set(name, true);
|
||||
}
|
||||
|
||||
function mangle(name) {
|
||||
|
||||
50
lib/scope.js
50
lib/scope.js
@@ -44,9 +44,9 @@
|
||||
"use strict";
|
||||
|
||||
function SymbolDef(id, scope, orig, init) {
|
||||
this._bits = 0;
|
||||
this.defun = undefined;
|
||||
this.eliminated = 0;
|
||||
this.exported = false;
|
||||
this.global = false;
|
||||
this.id = id;
|
||||
this.init = init;
|
||||
this.mangled_name = null;
|
||||
@@ -54,8 +54,8 @@ function SymbolDef(id, scope, orig, init) {
|
||||
this.orig = [ orig ];
|
||||
this.references = [];
|
||||
this.replaced = 0;
|
||||
this.safe_ids = undefined;
|
||||
this.scope = scope;
|
||||
this.undeclared = false;
|
||||
}
|
||||
|
||||
SymbolDef.prototype = {
|
||||
@@ -104,6 +104,15 @@ 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) {
|
||||
@@ -363,7 +372,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
if (node instanceof AST_SymbolLambda) {
|
||||
var def = node.thedef;
|
||||
if (!redefine(node, node.scope.parent_scope.resolve())) {
|
||||
delete def.defun;
|
||||
def.defun = undefined;
|
||||
} else if (typeof node.thedef.init !== "undefined") {
|
||||
node.thedef.init = false;
|
||||
} else if (def.init) {
|
||||
@@ -465,7 +474,7 @@ AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
|
||||
for (var s = this.scope; s; s = s.parent_scope) {
|
||||
push_uniq(s.enclosed, def);
|
||||
if (!options) {
|
||||
delete s._var_names;
|
||||
s._var_names = undefined;
|
||||
} else if (options.keep_fnames) {
|
||||
s.functions.each(function(d) {
|
||||
push_uniq(def.scope.enclosed, d);
|
||||
@@ -510,12 +519,12 @@ function names_in_use(scope, options) {
|
||||
if (!names) {
|
||||
scope.cname = -1;
|
||||
scope.cname_holes = [];
|
||||
scope.names_in_use = names = Object.create(null);
|
||||
scope.names_in_use = names = new Dictionary();
|
||||
var cache = options.cache && options.cache.props;
|
||||
scope.enclosed.forEach(function(def) {
|
||||
if (def.unmangleable(options)) names[def.name] = true;
|
||||
if (def.unmangleable(options)) names.set(def.name, true);
|
||||
if (def.global && cache && cache.has(def.name)) {
|
||||
names[cache.get(def.name)] = true;
|
||||
names.set(cache.get(def.name), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -526,34 +535,33 @@ function next_mangled_name(def, options) {
|
||||
var scope = def.scope;
|
||||
var in_use = names_in_use(scope, options);
|
||||
var holes = scope.cname_holes;
|
||||
var names = Object.create(null);
|
||||
var names = new Dictionary();
|
||||
var scopes = [ scope ];
|
||||
def.forEach(function(sym) {
|
||||
var scope = sym.scope;
|
||||
do {
|
||||
if (scopes.indexOf(scope) < 0) {
|
||||
for (var name in names_in_use(scope, options)) {
|
||||
names[name] = true;
|
||||
}
|
||||
scopes.push(scope);
|
||||
} else break;
|
||||
if (member(scope, scopes)) break;
|
||||
names_in_use(scope, options).each(function(marker, name) {
|
||||
names.set(name, marker);
|
||||
});
|
||||
scopes.push(scope);
|
||||
} while (scope = scope.parent_scope);
|
||||
});
|
||||
var name;
|
||||
for (var i = 0; i < holes.length; i++) {
|
||||
name = base54(holes[i]);
|
||||
if (names[name]) continue;
|
||||
if (names.has(name)) continue;
|
||||
holes.splice(i, 1);
|
||||
in_use[name] = true;
|
||||
in_use.set(name, true);
|
||||
return name;
|
||||
}
|
||||
while (true) {
|
||||
name = base54(++scope.cname);
|
||||
if (in_use[name] || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
|
||||
if (!names[name]) break;
|
||||
if (in_use.has(name) || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
|
||||
if (!names.has(name)) break;
|
||||
holes.push(scope.cname);
|
||||
}
|
||||
in_use[name] = true;
|
||||
in_use.set(name, true);
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -598,7 +606,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
||||
if (options.cache && options.cache.props) {
|
||||
var mangled_names = names_in_use(this, options);
|
||||
options.cache.props.each(function(mangled_name) {
|
||||
mangled_names[mangled_name] = true;
|
||||
mangled_names.set(mangled_name, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -77,21 +77,23 @@ function vlq_encode(num) {
|
||||
}
|
||||
|
||||
function create_array_map() {
|
||||
var map = Object.create(null);
|
||||
var map = new Dictionary();
|
||||
var array = [];
|
||||
array.index = function(name) {
|
||||
if (!HOP(map, name)) {
|
||||
map[name] = array.length;
|
||||
var index = map.get(name);
|
||||
if (!(index >= 0)) {
|
||||
index = array.length;
|
||||
array.push(name);
|
||||
map.set(name, index);
|
||||
}
|
||||
return map[name];
|
||||
return index;
|
||||
};
|
||||
return array;
|
||||
}
|
||||
|
||||
function SourceMap(options) {
|
||||
var sources = create_array_map();
|
||||
var sources_content = options.includeSources && Object.create(null);
|
||||
var sources_content = options.includeSources && new Dictionary();
|
||||
var names = create_array_map();
|
||||
var mappings = "";
|
||||
if (options.orig) Object.keys(options.orig).forEach(function(name) {
|
||||
@@ -110,7 +112,7 @@ function SourceMap(options) {
|
||||
if (!sources_content || !map.sourcesContent) return;
|
||||
for (var i = 0; i < map.sources.length; i++) {
|
||||
var content = map.sourcesContent[i];
|
||||
if (content) sources_content[map.sources[i]] = content;
|
||||
if (content) sources_content.set(map.sources[i], content);
|
||||
}
|
||||
});
|
||||
var prev_source;
|
||||
@@ -144,8 +146,8 @@ function SourceMap(options) {
|
||||
add(source, gen_line, gen_col, orig_line, orig_col, name);
|
||||
} : add,
|
||||
setSourceContent: sources_content ? function(source, content) {
|
||||
if (!(source in sources_content)) {
|
||||
sources_content[source] = content;
|
||||
if (!sources_content.has(source)) {
|
||||
sources_content.set(source, content);
|
||||
}
|
||||
} : noop,
|
||||
toString: function() {
|
||||
@@ -155,7 +157,7 @@ function SourceMap(options) {
|
||||
sourceRoot: options.root || undefined,
|
||||
sources: sources,
|
||||
sourcesContent: sources_content ? sources.map(function(source) {
|
||||
return sources_content[source] || null;
|
||||
return sources_content.get(source) || null;
|
||||
}) : undefined,
|
||||
names: names,
|
||||
mappings: mappings,
|
||||
|
||||
92
lib/utils.js
92
lib/utils.js
@@ -96,15 +96,6 @@ function defaults(args, defs, croak) {
|
||||
return defs;
|
||||
}
|
||||
|
||||
function merge(obj, ext) {
|
||||
var count = 0;
|
||||
for (var i in ext) if (HOP(ext, i)) {
|
||||
obj[i] = ext[i];
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
function return_false() { return false; }
|
||||
function return_true() { return true; }
|
||||
@@ -171,63 +162,80 @@ function all(array, predicate) {
|
||||
}
|
||||
|
||||
function Dictionary() {
|
||||
this._values = Object.create(null);
|
||||
this._size = 0;
|
||||
this.values = Object.create(null);
|
||||
}
|
||||
Dictionary.prototype = {
|
||||
set: function(key, val) {
|
||||
if (!this.has(key)) ++this._size;
|
||||
this._values["$" + key] = val;
|
||||
if (key == "__proto__") {
|
||||
this.proto_value = val;
|
||||
} else {
|
||||
this.values[key] = val;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
add: function(key, val) {
|
||||
if (this.has(key)) {
|
||||
this.get(key).push(val);
|
||||
var list = this.get(key);
|
||||
if (list) {
|
||||
list.push(val);
|
||||
} else {
|
||||
this.set(key, [ val ]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
get: function(key) { return this._values["$" + key] },
|
||||
get: function(key) {
|
||||
return key == "__proto__" ? this.proto_value : this.values[key];
|
||||
},
|
||||
del: function(key) {
|
||||
if (this.has(key)) {
|
||||
--this._size;
|
||||
delete this._values["$" + key];
|
||||
if (key == "__proto__") {
|
||||
delete this.proto_value;
|
||||
} else {
|
||||
delete this.values[key];
|
||||
}
|
||||
return this;
|
||||
},
|
||||
has: function(key) { return ("$" + key) in this._values },
|
||||
has: function(key) {
|
||||
return key == "__proto__" ? "proto_value" in this : key in this.values;
|
||||
},
|
||||
all: function(predicate) {
|
||||
for (var i in this._values)
|
||||
if (!predicate(this._values[i], i.substr(1)))
|
||||
return false;
|
||||
for (var i in this.values)
|
||||
if (!predicate(this.values[i], i)) return false;
|
||||
if ("proto_value" in this && !predicate(this.proto_value, "__proto__")) return false;
|
||||
return true;
|
||||
},
|
||||
each: function(f) {
|
||||
for (var i in this._values)
|
||||
f(this._values[i], i.substr(1));
|
||||
for (var i in this.values)
|
||||
f(this.values[i], i);
|
||||
if ("proto_value" in this) f(this.proto_value, "__proto__");
|
||||
},
|
||||
size: function() {
|
||||
return this._size;
|
||||
return Object.keys(this.values).length + ("proto_value" in this);
|
||||
},
|
||||
map: function(f) {
|
||||
var ret = [];
|
||||
for (var i in this._values)
|
||||
ret.push(f(this._values[i], i.substr(1)));
|
||||
for (var i in this.values)
|
||||
ret.push(f(this.values[i], i));
|
||||
if ("proto_value" in this) ret.push(f(this.proto_value, "__proto__"));
|
||||
return ret;
|
||||
},
|
||||
clone: function() {
|
||||
var ret = new Dictionary();
|
||||
for (var i in this._values)
|
||||
ret._values[i] = this._values[i];
|
||||
ret._size = this._size;
|
||||
this.each(function(value, i) {
|
||||
ret.set(i, value);
|
||||
});
|
||||
return ret;
|
||||
},
|
||||
toObject: function() { return this._values }
|
||||
toObject: function() {
|
||||
var obj = {};
|
||||
this.each(function(value, i) {
|
||||
obj["$" + i] = value;
|
||||
});
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
Dictionary.fromObject = function(obj) {
|
||||
var dict = new Dictionary();
|
||||
dict._size = merge(dict._values, obj);
|
||||
for (var i in obj)
|
||||
if (HOP(obj, i)) dict.set(i.slice(1), obj[i]);
|
||||
return dict;
|
||||
};
|
||||
|
||||
@@ -265,3 +273,21 @@ function first_in_statement(stack, arrow, export_default) {
|
||||
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;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.14.4",
|
||||
"version": "3.14.5",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -442,9 +442,9 @@ compress_annotations_disabled_output_annotations_enabled: {
|
||||
}
|
||||
expect_exact: [
|
||||
"/*@__PURE__*/a(3),",
|
||||
"/*#__PURE__*/b(5),",
|
||||
"/*@__PURE__*/b(5),",
|
||||
"c(side_effect),",
|
||||
"/*#__PURE__*/d(effect());",
|
||||
"/*@__PURE__*/d(effect());",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +363,28 @@ negate: {
|
||||
}
|
||||
|
||||
inline_this: {
|
||||
options = {
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
var p = "PASS";
|
||||
console.log({
|
||||
p: "FAIL",
|
||||
q: (() => this.p)(),
|
||||
}.q);
|
||||
}
|
||||
expect: {
|
||||
var p = "PASS";
|
||||
console.log({
|
||||
p: "FAIL",
|
||||
q: this.p,
|
||||
}.q);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
dont_inline_this: {
|
||||
options = {
|
||||
inline: true,
|
||||
}
|
||||
|
||||
@@ -340,6 +340,33 @@ inline_await_3_trim: {
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
inline_await_this: {
|
||||
options = {
|
||||
awaits: true,
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
var p = "FAIL";
|
||||
({
|
||||
p: "PASS",
|
||||
async f() {
|
||||
return await (async () => this.p)();
|
||||
},
|
||||
}).f().then(console.log);
|
||||
}
|
||||
expect: {
|
||||
var p = "FAIL";
|
||||
({
|
||||
p: "PASS",
|
||||
async f() {
|
||||
return await this.p;
|
||||
},
|
||||
}).f().then(console.log);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
await_unary: {
|
||||
options = {
|
||||
awaits: true,
|
||||
|
||||
@@ -4,7 +4,7 @@ arithmetic: {
|
||||
}
|
||||
expect_exact: "console.log((1n+0x2n)*(0o3n- -4n)>>5n-6n);"
|
||||
expect_stdout: "42n"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
minus_dot: {
|
||||
@@ -13,7 +13,7 @@ minus_dot: {
|
||||
}
|
||||
expect_exact: "console.log(typeof-42n.toString(),typeof(-42n).toString());"
|
||||
expect_stdout: "number string"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
evaluate: {
|
||||
@@ -28,7 +28,7 @@ evaluate: {
|
||||
console.log(0xdeadbeefn.toString(16));
|
||||
}
|
||||
expect_stdout: "deadbeef"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
Number: {
|
||||
@@ -42,7 +42,7 @@ Number: {
|
||||
console.log(+("" + -0xfeed_dead_beef_badn));
|
||||
}
|
||||
expect_stdout: "-1148098955808013200"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
issue_4590: {
|
||||
@@ -58,7 +58,7 @@ issue_4590: {
|
||||
0n || console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
issue_4801: {
|
||||
@@ -88,5 +88,5 @@ issue_4801: {
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
@@ -1037,7 +1037,7 @@ mangle_arrow_1: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
node_version: ">=6.9.3"
|
||||
}
|
||||
|
||||
mangle_arrow_1_toplevel: {
|
||||
@@ -1073,7 +1073,7 @@ mangle_arrow_1_toplevel: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
node_version: ">=6.9.3"
|
||||
}
|
||||
|
||||
mangle_arrow_2: {
|
||||
@@ -1109,7 +1109,7 @@ mangle_arrow_2: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
node_version: ">=6.9.3"
|
||||
}
|
||||
|
||||
mangle_arrow_2_toplevel: {
|
||||
@@ -1145,7 +1145,7 @@ mangle_arrow_2_toplevel: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
node_version: ">=6.9.3"
|
||||
}
|
||||
|
||||
issue_4444: {
|
||||
@@ -1587,7 +1587,7 @@ issue_4510_2: {
|
||||
};
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4523: {
|
||||
|
||||
@@ -220,7 +220,7 @@ funarg_side_effects_2: {
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
node_version: ">=6.9.2"
|
||||
}
|
||||
|
||||
funarg_side_effects_3: {
|
||||
@@ -254,7 +254,7 @@ funarg_side_effects_3: {
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
node_version: ">=6.9.2"
|
||||
}
|
||||
|
||||
funarg_unused_1: {
|
||||
|
||||
@@ -3203,7 +3203,7 @@ issue_4552: {
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
issue_4886: {
|
||||
issue_4886_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
@@ -3222,3 +3222,23 @@ issue_4886: {
|
||||
}
|
||||
expect_stdout: "true"
|
||||
}
|
||||
|
||||
issue_4886_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log("foo" in {
|
||||
"foo": null,
|
||||
__proto__: 42,
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
console.log("foo" in {
|
||||
"foo": null,
|
||||
__proto__: 42,
|
||||
});
|
||||
}
|
||||
expect_stdout: "true"
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ assignment_2: {
|
||||
}
|
||||
expect_exact: "var a=8n;a**=a;console.log(a);"
|
||||
expect_stdout: "16777216n"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.4.0"
|
||||
}
|
||||
|
||||
evaluate: {
|
||||
|
||||
@@ -521,3 +521,25 @@ issue_4415: {
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_5213: {
|
||||
options = {
|
||||
objects: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL";
|
||||
console.log({
|
||||
p: a = "PASS",
|
||||
0: a,
|
||||
p: null,
|
||||
}[0]);
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL";
|
||||
console.log({
|
||||
p: (a = "PASS", null),
|
||||
0: a,
|
||||
}[0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ arrow_destructured_object_1: {
|
||||
}
|
||||
expect_exact: "var f=({...a})=>a,o=f({PASS:42});for(var k in o)console.log(k,o[k]);"
|
||||
expect_stdout: "PASS 42"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
arrow_destructured_object_2: {
|
||||
@@ -62,7 +62,7 @@ arrow_destructured_object_2: {
|
||||
}
|
||||
expect_exact: "var f=({FAIL:a,...b})=>b,o=f({PASS:42,FAIL:null});for(var k in o)console.log(k,o[k]);"
|
||||
expect_stdout: "PASS 42"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
arrow_destructured_object_3: {
|
||||
@@ -79,7 +79,7 @@ arrow_destructured_object_3: {
|
||||
"2 S",
|
||||
"3 S",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
funarg_1: {
|
||||
@@ -131,7 +131,7 @@ destructured_object_1: {
|
||||
}
|
||||
expect_exact: 'var{...a}=["FAIL","PASS",42];console.log(a[1],a[2]);'
|
||||
expect_stdout: "PASS 42"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
destructured_object_2: {
|
||||
@@ -141,7 +141,7 @@ destructured_object_2: {
|
||||
}
|
||||
expect_exact: 'var{0:a,...b}=["FAIL","PASS",42];console.log(b[1],b[2]);'
|
||||
expect_stdout: "PASS 42"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
drop_fargs: {
|
||||
@@ -231,7 +231,7 @@ reduce_destructured_object: {
|
||||
console.log(a[0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
retain_destructured_array: {
|
||||
@@ -270,7 +270,7 @@ retain_destructured_object_1: {
|
||||
"1 PASS",
|
||||
"2 42",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
retain_destructured_object_2: {
|
||||
@@ -292,7 +292,7 @@ retain_destructured_object_2: {
|
||||
"bar PASS",
|
||||
"baz 42",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
retain_funarg_destructured_array_1: {
|
||||
@@ -344,7 +344,7 @@ retain_funarg_destructured_object_1: {
|
||||
console.log((({ ...a }) => a)([ "PASS" ])[0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
retain_funarg_destructured_object_2: {
|
||||
@@ -362,7 +362,7 @@ retain_funarg_destructured_object_2: {
|
||||
}({ p: "FAIL" }).p || "PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
drop_unused_call_args_1: {
|
||||
@@ -482,7 +482,7 @@ merge_funarg_destructured_object: {
|
||||
})([ "PASS" ]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
keep_arguments: {
|
||||
@@ -992,7 +992,7 @@ issue_5089_1: {
|
||||
console.log(o.p);
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_5089_2: {
|
||||
@@ -1019,7 +1019,7 @@ issue_5089_2: {
|
||||
console.log(o.p);
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_5100_1: {
|
||||
@@ -1054,7 +1054,7 @@ issue_5100_1: {
|
||||
console.log(a.r);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_5100_2: {
|
||||
@@ -1085,7 +1085,7 @@ issue_5100_2: {
|
||||
} ][0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=10"
|
||||
node_version: ">=10.22.0"
|
||||
}
|
||||
|
||||
issue_5108: {
|
||||
|
||||
@@ -341,7 +341,7 @@ convert_setter: {
|
||||
console.log(k, o[k]);
|
||||
}
|
||||
expect_stdout: "PASS undefined"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
keep_getter_1: {
|
||||
@@ -370,7 +370,7 @@ keep_getter_1: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
keep_getter_2: {
|
||||
@@ -399,7 +399,7 @@ keep_getter_2: {
|
||||
"foo",
|
||||
"bar",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
keep_getter_3: {
|
||||
@@ -429,7 +429,7 @@ keep_getter_3: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
keep_getter_4: {
|
||||
@@ -460,7 +460,7 @@ keep_getter_4: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
keep_accessor: {
|
||||
@@ -508,7 +508,7 @@ keep_accessor: {
|
||||
"q undefined",
|
||||
"r null",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
object_key_order_1: {
|
||||
@@ -538,7 +538,7 @@ object_key_order_1: {
|
||||
"a 3",
|
||||
"b 2",
|
||||
]
|
||||
node_version: ">=8 <=10"
|
||||
node_version: ">=8.3.0 <=10"
|
||||
}
|
||||
|
||||
object_key_order_2: {
|
||||
@@ -568,7 +568,7 @@ object_key_order_2: {
|
||||
"a 3",
|
||||
"b 2",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
object_key_order_3: {
|
||||
@@ -598,7 +598,7 @@ object_key_order_3: {
|
||||
"a 3",
|
||||
"b 2",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
object_key_order_4: {
|
||||
@@ -628,7 +628,7 @@ object_key_order_4: {
|
||||
"a 3",
|
||||
"b 2",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
object_spread_array: {
|
||||
@@ -654,7 +654,7 @@ object_spread_array: {
|
||||
"0 foo",
|
||||
"1 bar",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
object_spread_string: {
|
||||
@@ -681,7 +681,7 @@ object_spread_string: {
|
||||
"1 o",
|
||||
"2 o",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
unused_var_side_effects: {
|
||||
@@ -711,7 +711,7 @@ unused_var_side_effects: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
unsafe_join_1: {
|
||||
@@ -793,7 +793,7 @@ issue_4329: {
|
||||
}[0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4331: {
|
||||
@@ -871,7 +871,7 @@ issue_4345: {
|
||||
}[42]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4361: {
|
||||
@@ -901,7 +901,7 @@ issue_4361: {
|
||||
"foo",
|
||||
"undefined",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4363: {
|
||||
@@ -922,7 +922,7 @@ issue_4363: {
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4556: {
|
||||
@@ -994,7 +994,7 @@ issue_4849: {
|
||||
}()));
|
||||
}
|
||||
expect_stdout: "object"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4882_1: {
|
||||
@@ -1026,7 +1026,7 @@ issue_4882_1: {
|
||||
"PASS",
|
||||
"undefined",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4882_2: {
|
||||
@@ -1052,7 +1052,7 @@ issue_4882_2: {
|
||||
"42",
|
||||
"PASS",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_4882_3: {
|
||||
@@ -1082,7 +1082,7 @@ issue_4882_3: {
|
||||
"PASS",
|
||||
"42",
|
||||
]
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.3.0"
|
||||
}
|
||||
|
||||
issue_5006: {
|
||||
|
||||
@@ -337,7 +337,7 @@ malformed_evaluate_4: {
|
||||
console.log("\\u00b5");
|
||||
}
|
||||
expect_stdout: "\\u00b5"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.10.0"
|
||||
}
|
||||
|
||||
unsafe_evaluate: {
|
||||
@@ -353,7 +353,7 @@ unsafe_evaluate: {
|
||||
console.log("\\uFo");
|
||||
}
|
||||
expect_stdout: "\\uFo"
|
||||
node_version: ">=8"
|
||||
node_version: ">=8.10.0"
|
||||
}
|
||||
|
||||
side_effects_1: {
|
||||
|
||||
@@ -67,7 +67,8 @@ unicode_escaped_identifier_2: {
|
||||
}
|
||||
expect_exact: 'var a="foo";var \u{10000}="bar";console.log(a,\u{10000});'
|
||||
expect_stdout: "foo bar"
|
||||
node_version: ">=4"
|
||||
// non-BMP support is platform-dependent on Node.js v4
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
unicode_identifier_ascii_only: {
|
||||
@@ -200,7 +201,8 @@ surrogate_pair: {
|
||||
}
|
||||
expect_exact: 'var \ud87e\udc00={"\ud87e\udc01":"\udbc0\udc00"};\ud87e\udc00.\ud87e\udc02="\udbc0\udc01";console.log(typeof \ud87e\udc00,\ud87e\udc00.\ud87e\udc01,\ud87e\udc00["\ud87e\udc02"]);'
|
||||
expect_stdout: "object \udbc0\udc00 \udbc0\udc01"
|
||||
node_version: ">=4"
|
||||
// non-BMP support is platform-dependent on Node.js v4
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
surrogate_pair_ascii: {
|
||||
@@ -216,5 +218,6 @@ surrogate_pair_ascii: {
|
||||
}
|
||||
expect_exact: 'var \\u{2f800}={"\\ud87e\\udc01":"\\udbc0\\udc00"};\\u{2f800}.\\u{2f802}="\\udbc0\\udc01";console.log(typeof \\u{2f800},\\u{2f800}.\\u{2f801},\\u{2f800}["\\ud87e\\udc02"]);'
|
||||
expect_stdout: "object \udbc0\udc00 \udbc0\udc01"
|
||||
node_version: ">=4"
|
||||
// non-BMP support is platform-dependent on Node.js v4
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
@@ -970,7 +970,7 @@ issue_4639_1: {
|
||||
}().next().value());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
node_version: ">=4 <7 || >=8.7.0"
|
||||
}
|
||||
|
||||
issue_4639_2: {
|
||||
|
||||
@@ -252,8 +252,11 @@ function run_code_vm(code, toplevel, timeout) {
|
||||
var ctx = vm.createContext({ console: console });
|
||||
// for Node.js v6
|
||||
vm.runInContext(setup_code, ctx);
|
||||
vm.runInContext(toplevel ? "(function(){" + code + "})();" : code, ctx, { timeout: timeout });
|
||||
return strip_color_codes(stdout);
|
||||
vm.runInContext(toplevel ? "(function(){\n" + code + "\n})();" : code, ctx, { timeout: timeout });
|
||||
// for Node.js v4
|
||||
return strip_color_codes(stdout.replace(/\b(Array \[|Object {)/g, function(match) {
|
||||
return match.slice(-1);
|
||||
}));
|
||||
} catch (ex) {
|
||||
return ex;
|
||||
} finally {
|
||||
@@ -263,7 +266,7 @@ function run_code_vm(code, toplevel, timeout) {
|
||||
|
||||
function run_code_exec(code, toplevel, timeout) {
|
||||
if (toplevel) {
|
||||
code = setup_code + "(function(){" + code + "})();";
|
||||
code = setup_code + "(function(){\n" + code + "\n})();";
|
||||
} else {
|
||||
code = code.replace(/^((["'])[^"']*\2(;|$))?/, function(directive) {
|
||||
return directive + setup_code;
|
||||
@@ -292,7 +295,7 @@ function run_code_exec(code, toplevel, timeout) {
|
||||
} catch (e) {}
|
||||
}
|
||||
var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg);
|
||||
if (!match) return details;
|
||||
if (!match) return details || new Error("Script execution aborted.");
|
||||
var ex = new global[match[1]](match[2]);
|
||||
ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3];
|
||||
if (typeof details == "object") {
|
||||
|
||||
@@ -2469,8 +2469,10 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
println("original result:");
|
||||
println(result);
|
||||
println();
|
||||
// ignore v8 parser bug
|
||||
return bug_async_arrow_rest(result);
|
||||
// ignore v8 parser bug
|
||||
return bug_async_arrow_rest(result)
|
||||
// ignore runtime platform bugs
|
||||
|| result.message == "Script execution aborted.";
|
||||
})) continue;
|
||||
minify_options.forEach(function(options) {
|
||||
var o = JSON.parse(options);
|
||||
@@ -2485,6 +2487,8 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
ok = sandbox.same_stdout(original_result, uglify_result);
|
||||
// ignore v8 parser bug
|
||||
if (!ok && bug_async_arrow_rest(uglify_result)) ok = true;
|
||||
// ignore runtime platform bugs
|
||||
if (!ok && uglify_result.message == "Script execution aborted.") ok = true;
|
||||
// handle difference caused by time-outs
|
||||
if (!ok && errored && is_error_timeout(original_result)) {
|
||||
if (is_error_timeout(uglify_result)) {
|
||||
|
||||
Reference in New Issue
Block a user